diff --git a/article/admin.py b/article/admin.py index 18722821..28d7d132 100644 --- a/article/admin.py +++ b/article/admin.py @@ -9,7 +9,7 @@ from django.contrib.contenttypes.models import ContentType from forms import ArticleForm, ArticleDeleteForm, Article, NewsForm from theme.models import Tag from file.models import FileModel, TmpFile -from file.forms import FileModelForm +from file.forms import FileModelForm, FileForm #custom views from functions.custom_views import objects_list, add_object_with_file, delete_object @@ -180,7 +180,13 @@ class BlogView(FormView): def get_context_data(self, **kwargs): context = super(BlogView, self).get_context_data(**kwargs) self.set_obj() + + context['article'] = self.obj + if context['article']: + context['file_form'] = FileForm(initial={'model': 'article.Article'}) + context['files'] = FileModel.objects.filter(content_type=ContentType.objects.get_for_model(context['article']), + object_id=getattr(context['article'], 'id')) context['languages'] = settings.LANGUAGES return context diff --git a/article/models.py b/article/models.py index 2ee840ca..25fe1135 100644 --- a/article/models.py +++ b/article/models.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import copy from django.db import models +from django.contrib.contenttypes import generic from django.utils.translation import ugettext_lazy as _ from django.utils.timezone import now from hvad.models import TranslatableModel, TranslatedFields, TranslationManager @@ -11,6 +12,7 @@ from functions.model_utils import base_concrete_model from functions.form_check import translit_with_separator + class ArticleManager(TranslationManager): def safe_get(self, **kwargs): model = self.model @@ -91,6 +93,10 @@ class Article(TranslatableModel): keywords = models.CharField(max_length=255, blank=True), ) + files = generic.GenericRelation('file.FileModel', content_type_field='content_type', object_id_field='object_id') + class Meta: + ordering = ['-created'] + def __unicode__(self): return self.lazy_translation_getter('main_title', self.pk) @@ -196,9 +202,17 @@ class Article(TranslatableModel): def get_permanent_url(self): if self.type == 1: - return '/blogs/%s'%self.slug + return '/blogs/%s/'%self.slug elif self.type == 2: - return '/news/%s'%self.slug + return '/news/%s/'%self.slug + + def get_blog_preview(self): + + preview = self.files.filter(purpose='preview') + if preview: + return preview[0] + else: + return None from django.db.models.signals import post_save from functions.signal_handlers import post_save_handler diff --git a/article/urls.py b/article/urls.py index bdd15307..4fcd7d52 100644 --- a/article/urls.py +++ b/article/urls.py @@ -7,6 +7,6 @@ urlpatterns = patterns('', url(r'blogs/$', BlogList.as_view()), url(r'news/page/(?P\d+)/$', NewsList.as_view()), url(r'news/$', NewsList.as_view()), - url(r'blogs/(?P.*)$', BlogDetail.as_view()), - url(r'news/(?P.*)$', NewsDetail.as_view()), + url(r'blogs/(?P.*)/$', BlogDetail.as_view()), + url(r'news/(?P.*)/$', NewsDetail.as_view()), ) diff --git a/city/models.py b/city/models.py index c4f07298..0e523876 100644 --- a/city/models.py +++ b/city/models.py @@ -11,6 +11,9 @@ from service.models import Service from exposition.models import Exposition from place_exposition.models import PlaceExposition from organiser.models import Organiser +from conference.models import Conference +from seminar.models import Seminar +from webinar.models import Webinar # custom functions from functions.db import db_table_exists from functions.signal_handlers import post_save_handler @@ -86,7 +89,19 @@ class City(TranslatableModel): def expositions_number(self): - return len(Exposition.objects.filter(city=self.id)) + return Exposition.objects.filter(city=self.id).count() + + def conferences_number(self): + + return Conference.objects.filter(city=self.id).count() + + def seminars_number(self): + + return Seminar.objects.filter(city=self.id).count() + + def webinars_number(self): + + return Webinar.objects.filter(city=self.id).count() def get_parent(self): parent = {'text' : self.country.name, 'id': self.country.id, 'name': 'co', diff --git a/conference/admin.py b/conference/admin.py index fa8a3790..c5c4ca62 100644 --- a/conference/admin.py +++ b/conference/admin.py @@ -13,13 +13,14 @@ from forms import ConferenceChangeForm, ConferenceCreateForm, ConferenceDeleteFo from theme.models import Tag from city.models import City from file.models import FileModel, TmpFile -from file.forms import FileModelForm +from file.forms import FileModelForm, FileForm +from photologue.forms import PhotoForm #python import random #custom views from functions.custom_views import objects_list, delete_object from functions.views_help import get_referer -from functions.admin_views import AdminListView +from functions.admin_views import AdminListView, AdminView, upload_photo def conference_all(request): @@ -214,7 +215,83 @@ def conference_change(request, url): return render_to_response('conference_add.html', args) + +class ConferenceView(AdminView): + form_class = ConferenceCreateForm + model = Conference + success_url = '/admin/conference/all/' + template_name = 'admin/conference/conference.html' + + def form_valid(self, form): + self.set_obj() + expo = form.save(obj=self.obj) + + return HttpResponseRedirect(self.success_url) + + def get_form(self, form_class): + if self.request.POST: + return super(ConferenceView, self).get_form(form_class) + obj = self.set_obj() + if obj: + data = {'web_page':obj.web_page, 'foundation_year': obj.foundation_year, + 'data_begin':obj.data_begin, 'data_end':obj.data_end, 'currency':obj.currency, + 'tax':obj.tax, 'min_price':obj.min_price, 'max_price':obj.max_price, + 'link':obj.link, 'conference_id':obj.id, 'expohit': obj.expohit, + 'discount': obj.discount,'canceled': obj.canceled, 'moved': obj.moved, + 'visitors': obj.visitors, 'members': obj.members, + 'quality_label': [item for item, bool in obj.quality_label if bool==True]} + + if obj.place: + data['place'] = obj.place.id + + data['theme'] = [item.id for item in obj.theme.all()] + data['tag'] = ','.join(['%s:%s'%(item.id, item.name) for item in obj.tag.all()]) + data['organiser'] = [item.id for item in obj.organiser.all()] + data['company'] = [item.id for item in obj.company.all()] + + data['country'] = obj.country_id + data['city'] = obj.city_id + + for code, name in settings.LANGUAGES: + trans_obj = self.model._meta.translations_model.objects.get(language_code = code,master__id=obj.id) #access to translated fields + data['name_%s' % code] = obj.name + data['description_%s' % code] = trans_obj.description + data['main_title_%s' % code] = trans_obj.main_title + data['time_%s' % code] = trans_obj.time + data['main_themes_%s' % code] = trans_obj.main_themes + data['discount_description_%s' % code] = trans_obj.discount_description + data['title_%s' % code] = trans_obj.title + data['keywords_%s' % code] = trans_obj.keywords + data['descriptions_%s' % code] = trans_obj.descriptions + + form =form_class(initial=data) + form.fields['city'].widget.attrs['data-init-text'] = obj.city.name + form.fields['tag'].choices = [(item.id, item.name) for item in Tag.objects.language().filter(theme__in=data['theme'])] + return form + else: + return form_class() + + + def get_context_data(self, **kwargs): + context = super(ConferenceView, self).get_context_data(**kwargs) + obj = self.set_obj() + if obj: + + context['stat_form'] = StatisticForm() + context['file_form'] = FileForm(initial={'model': 'conference.Conference'}) + files = FileModel.objects.filter(content_type=ContentType.objects.get_for_model(obj),object_id=getattr(obj, 'id')) + context['files'] = files + + context['photo_form'] = PhotoForm() + context['timetable_form'] = TimeTableForm() + context['timetables'] = TimeTable.objects.filter(conference=obj) + return context + + class ConferenceListView(AdminListView): template_name = 'admin/conference/conference_list.html' form_class = ConferenceFilterForm - model = Conference \ No newline at end of file + model = Conference + +def upload_conference_photo(request, conf_id): + return upload_photo(request, conf_id, Conference) \ No newline at end of file diff --git a/conference/admin_urls.py b/conference/admin_urls.py index b652b3f7..9a206a58 100644 --- a/conference/admin_urls.py +++ b/conference/admin_urls.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url -from admin import ConferenceListView +from admin import ConferenceListView, ConferenceView urlpatterns = patterns('conference.admin', - url(r'^add.*/$', 'conference_add'), + url(r'^upload-photo/(?P.*)/$', 'upload_conference_photo'), url(r'^delete/(?P.*)$', 'conference_delete'), - url(r'^change/(?P.*)/$', 'conference_change'), - url(r'^copy/(?P.*)/$', 'conference_copy'), - url(r'^switch/(?P.*)/(?P.*)$', 'conference_switch'), - #url(r'^all/$', 'conference_all'), url(r'^all/$', ConferenceListView.as_view()), + #url(r'^change/(?P.*)/$', 'conference_change'), + + url(r'^switch/(?P.*)/(?P.*)$', 'conference_switch'), + + url(r'^(?P.*)/$', ConferenceView.as_view()), + url(r'^$', ConferenceView.as_view()), ) \ No newline at end of file diff --git a/conference/forms.py b/conference/forms.py index f635400d..b66c0f94 100644 --- a/conference/forms.py +++ b/conference/forms.py @@ -5,10 +5,10 @@ from ckeditor.widgets import CKEditorWidget from django.forms.util import ErrorList from django.core.validators import validate_email, URLValidator #models -from models import Conference, TimeTable, CURRENCY, Statistic +from models import Conference, TimeTable, CURRENCY, Statistic, BIT_AUDIENCE from country.models import Country from city.models import City -from theme.models import Theme +from theme.models import Theme, Tag from organiser.models import Organiser from accounts.models import User from company.models import Company @@ -21,6 +21,9 @@ from functions.files import check_tmp_files from functions.form_check import translit_with_separator from functions.admin_forms import AdminFilterForm +places = [(item.id, item.name) for item in PlaceConference.objects.language().all()] +places.insert(0,('', 'Не выбрано')) + class ConferenceCreateForm(forms.Form): """ @@ -31,18 +34,35 @@ class ConferenceCreateForm(forms.Form): save function saves data in Conference object. If it doesnt exist create new object """ + PERIODIC = ((0, u'Не выбрано'), + (1.0, u'Ежегодно'), (2.0, u'2 раза в год'), (3.0, u'3 раза в год'), + (4.0, u'4 раза в год'), (5.0, u'5 раз в год'), + (0.5, u'Раз в 2 года'),(0.33, u'Раз в 3 года'),(0.25, u'Раз в 4 года')) + + public = [(item1, item2) for item1, item2 in BIT_AUDIENCE] + currencies = [(item, item) for item in CURRENCY] - data_begin = forms.DateField(label='Дата начала') - data_end = forms.DateField(label='Дата окночания') + data_begin = forms.DateField(label=u'Дата начала', input_formats=['%Y-%m-%d', '%d.%m.%Y']) + data_end = forms.DateField(label=u'Дата окончания', input_formats=['%Y-%m-%d', '%d.%m.%Y']) + logo = forms.ImageField(label='Logo', required=False) + + organiser = forms.MultipleChoiceField(label=u'Организаторы', required=False, + choices=[(item.id, item.name) for item in Organiser.objects.language().all()]) country = forms.ChoiceField(label=u'Страна', choices=[(c.id, c.name) for c in Country.objects.all()]) - theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=Theme.objects.all()) - place = forms.ModelChoiceField(label='Место проведения', queryset=PlaceConference.objects.all(), - empty_label='', required=False) + theme = forms.MultipleChoiceField(label='Тематики', + choices=[(item.id, item.name) for item in Theme.objects.language().all()]) + + place = forms.ChoiceField(label=u'Место проведения', required=False, + choices=places) + #creates select input with empty choices cause it will be filled with ajax city = forms.CharField(label=u'Город', widget=forms.HiddenInput()) - tag = forms.MultipleChoiceField(label='Теги', required=False) + tag = forms.CharField(label=u'Теги', widget=forms.HiddenInput(), required=False) + + periodic = forms.ChoiceField(label=u'Периодичность', choices=PERIODIC, required=False) + audience = forms.MultipleChoiceField(label=u'Аудитория', choices=public, initial='', required=False) web_page = forms.CharField(label='Веб страница', required=False) link = forms.CharField(label='Линк на регистрацию', required=False) @@ -61,9 +81,7 @@ class ConferenceCreateForm(forms.Form): quality_label = forms.MultipleChoiceField(label='Тип', required=False, choices=[('ufi', 'UFI'), ('rsva', 'РСВЯ'), ('exporating', 'ExpoRating')], widget=forms.CheckboxSelectMultiple()) - #field for comparing tmp files - key = forms.CharField(required=False, widget=forms.HiddenInput()) - # + conference_id = forms.CharField(required=False, widget=forms.HiddenInput()) @@ -99,14 +117,8 @@ class ConferenceCreateForm(forms.Form): widget=forms.TextInput(attrs={'style':'width: 550px'})) - #!service has bitfield. uncomment when country data will be filled - #services = [(item.id, item.name) for item in Service.objects.all()] - #self.fields['service'] = forms.ChoiceField(label='Услуги', choices=services, required=False) - - - - def save(self, id=None): + def save(self, obj=None): """ changes Conference model object with id = id N/A add new Conference model object @@ -115,15 +127,18 @@ class ConferenceCreateForm(forms.Form): """ data = self.cleaned_data #create new conference object or get exists - if not id: + if not obj: conference = Conference() else: - conference = Conference.objects.get(id=id) + conference = obj conference.theme.clear() conference.tag.clear() #simple fields conference.url = translit_with_separator(data['name_ru'].strip()).lower() + if data.get('logo'): + conference.logo = data['logo'] + conference.data_begin = data['data_begin'] conference.data_end = data['data_end'] conference.link = data['link'] @@ -141,6 +156,7 @@ class ConferenceCreateForm(forms.Form): conference.expohit = data['expohit'] conference.canceled = data['canceled'] conference.moved = data['moved'] + conference.periodic = data['periodic'] # generates bitfield flag = 0 if data['quality_label']: @@ -148,6 +164,12 @@ class ConferenceCreateForm(forms.Form): conference.quality_label = flag + audience = 0 + if data['audience']: + audience = reduce(lambda x,y: x|y, (getattr(Conference.audience, item) for item in data['audience'])) + + conference.audience = audience + if data.get('country'): conference.country = Country.objects.get(id=data['country'].id)#.id cause select uses queryset @@ -156,22 +178,20 @@ class ConferenceCreateForm(forms.Form): conference.city = City.objects.get(id=data['city']) if data.get('place'): - conference.place = PlaceConference.objects.get(id=data['place'].id)#.id cause select uses queryset + conference.place = PlaceConference.objects.get(id=data['place']) + else: + conference.place = None # fill translated fields and save object fill_with_signal(Conference, conference, data) - #fill manytomany fields - for item in data['theme']: - conference.theme.add(item.id)#.id cause select uses queryset + conference.theme.add(*data['theme']) + conference.tag.add(*Tag.objects.filter(id__in=data['tag'])) + conference.organiser.add(*Organiser.objects.filter(id__in=data.get('organiser', []))) + - for item in data['tag']: - conference.tag.add(item) - # uses because in the next loop data will be overwritten conference.save() - #save files - check_tmp_files(conference, data['key']) return conference def clean(self): @@ -186,6 +206,19 @@ class ConferenceCreateForm(forms.Form): return self.cleaned_data + def clean_tag(self): + tags = self.cleaned_data.get('tag') + if tags: + res = [] + for id in tags.split(','): + try: + res.append(int(id)) + except: + continue + return res + else: + return [] + def clean_web_page(self): """ checking web_page diff --git a/conference/management/__init__.py b/conference/management/__init__.py new file mode 100644 index 00000000..3ed9fd0f --- /dev/null +++ b/conference/management/__init__.py @@ -0,0 +1 @@ +__author__ = 'root' diff --git a/conference/management/commands/__init__.py b/conference/management/commands/__init__.py new file mode 100644 index 00000000..3ed9fd0f --- /dev/null +++ b/conference/management/commands/__init__.py @@ -0,0 +1 @@ +__author__ = 'root' diff --git a/conference/management/commands/conf_load.py b/conference/management/commands/conf_load.py new file mode 100644 index 00000000..88504c25 --- /dev/null +++ b/conference/management/commands/conf_load.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +import xlrd +from django.core.management.base import BaseCommand +from django.conf import settings +from conference.models import Conference +from import_xls.excel_settings import event_sett +from django.db import IntegrityError + + +CONF_FILE = settings.MEDIA_ROOT+'/import/conf.xls' + + +class Command(BaseCommand): + def handle(self, *args, **options): + + f = open(CONF_FILE, 'r') + book = xlrd.open_workbook(file_contents=f.read()) + sheet = book.sheet_by_index(0) + row_list = [sheet.row_values(row_number) for row_number in range(sheet.nrows)] + labels = [label for label in row_list[0]] + + + existing = 0 + for row_number, row in enumerate(row_list[1:]): + if row[0] != '': + try: + object = Conference.objects.language('ru').get(id=int(row[0])) + existing += 1 + except ValueError: + object = Conference() + object.translate('ru') + + except Conference.DoesNotExist: + object = Conference(id= int(row[0])) + object.translate('ru') + existing += 1 + else: + # if id blank - its a new place + object = Conference() + object.translate('ru') + + methods = [] + for col_number, cell in enumerate(row): + label = labels[col_number] + + setting = event_sett.get(label) + + if setting is None: + continue + if setting.get('method'): + if cell != "": + methods.append({'func': setting['func'], 'value': cell, 'purpose': setting.get('purpose')}) + continue + + field_name = setting['field'] + func = setting.get('func') + + if func is not None: + extra_value = setting.get('extra_values') + if extra_value is not None: + # if setting has extra value then + # it is some field like city, theme, tag + # that has relation and can be created + + # in function we add language(need for relation fields) + # and extra value from object (like for city need country) + value = func(cell, 'ru', getattr(object, extra_value)) + elif setting.get('bitfield'): + value = func(object, cell, setting['label']) + + else: + value = func(cell) + + + setattr(object, field_name, value) + + try: + object.save() + except IntegrityError: + print('error %s'% str(object)) + continue + + + print('post save %s'% str(object)) + + + for method in methods: + func = method['func'] + if method.get('purpose'): + try: + func(object, method['value'], method['purpose']) + except: + continue + else: + func(object, method['value']) diff --git a/conference/models.py b/conference/models.py index 52221bd3..8a970194 100644 --- a/conference/models.py +++ b/conference/models.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- +from django.utils.translation import ugettext as _ from django.db import models from django.db.models.signals import post_save, pre_save from django.contrib.contenttypes import generic +from exposition.manager import ClientManager from hvad.models import TranslatableModel, TranslatedFields, TranslationManager +from functions.translate import fill_with_signal +from photologue.models import Gallery from functions.signal_handlers import post_save_handler, pre_save_handler import copy from bitfield import BitField @@ -12,11 +16,14 @@ from functions.db import db_table_exists from functions.custom_fields import EnumField from functions.models_methods import ExpoManager from functions.model_mixin import EventMixin, ExpoMixin +from functions.models_methods import hvad_to_dict # check if table exist and create flags if true flags = [str(item.id) for item in Service.objects.all()] if db_table_exists('service_service') else [] +from django.conf import settings -CURRENCY = ('RUB', 'USD', 'EUR') +CURRENCY = settings.CURRENCY +BIT_AUDIENCE = settings.BIT_AUDIENCE class Conference(TranslatableModel, EventMixin, ExpoMixin): """ @@ -25,17 +32,22 @@ class Conference(TranslatableModel, EventMixin, ExpoMixin): Uses hvad.TranslatableModel which is child of django.db.models class """ catalog = '/conference/' + catalog_name = _(u'Конференции:') # type of event event_type = 'conf' #set manager of this model objects = ExpoManager() + enable = ClientManager() - url = models.SlugField(unique=True) + url = models.SlugField(unique=True, max_length=255) data_begin = models.DateField(verbose_name='Дата начала') data_end = models.DateField(verbose_name='Дата окончания') + services = BitField(flags=flags) #relations - country = models.ForeignKey('country.Country', verbose_name='Страна', on_delete=models.PROTECT) - city = models.ForeignKey('city.City', verbose_name='Город', on_delete=models.PROTECT) + country = models.ForeignKey('country.Country', verbose_name='Страна', on_delete=models.PROTECT, + related_name='conference_country') + city = models.ForeignKey('city.City', verbose_name='Город', on_delete=models.PROTECT, + related_name='conference_city') place = models.ForeignKey('place_conference.PlaceConference', verbose_name='Место проведения', blank=True, null=True, on_delete=models.PROTECT, related_name='conference_place') theme = models.ManyToManyField('theme.Theme', verbose_name='Тематики', @@ -48,15 +60,20 @@ class Conference(TranslatableModel, EventMixin, ExpoMixin): blank=True, null=True, related_name='conference_companies') users = models.ManyToManyField('accounts.User', verbose_name='Посетители выставки', blank=True, null=True, related_name='conference_users') - #!service has bitfield uncomment when country data will be filled - services = BitField(flags=flags) - #service = models.ManyToManyField('service.Service', verbose_name='Услуги', blank=True, null=True) + photogallery = models.ForeignKey('photologue.Gallery', blank=True, null=True) + logo = models.ImageField(verbose_name='Logo', upload_to='conference/logo/', blank=True) + rating = models.IntegerField(default=0, db_index=True) # добавить индекс в базе + + quality_label = BitField(flags=['ufi', 'rsva', 'exporating']) + + periodic = models.FloatField(verbose_name='Переодичность', blank=True, null=True) + audience = BitField(flags=[k for k, v in BIT_AUDIENCE]) web_page = models.CharField(verbose_name='Вебсайт', max_length=255, blank=True) link = models.CharField(verbose_name='Линк на регистрацию', max_length=255, blank=True) - quality_label = BitField(flags=['ufi', 'rsva', 'exporating']) discount = models.PositiveIntegerField(verbose_name='Скидка', blank=True, null=True) # - currency = EnumField(values=CURRENCY, default='RUB') + + currency = EnumField(values=CURRENCY, default='USD') tax = models.BooleanField(verbose_name='Налог', default=1) min_price = models.PositiveIntegerField(verbose_name='Минимальная цена', blank=True, null=True) max_price = models.PositiveIntegerField(verbose_name='Максимальная цена', blank=True, null=True) @@ -69,6 +86,7 @@ class Conference(TranslatableModel, EventMixin, ExpoMixin): is_published = models.BooleanField(default=0) files = generic.GenericRelation('file.FileModel', content_type_field='content_type', object_id_field='object_id') + note = generic.GenericRelation('note.Note', content_type_field='content_type', object_id_field='object_id') # statistic foundation_year = models.PositiveIntegerField(verbose_name='Год основания', blank=True, null=True) visitors = models.PositiveIntegerField(verbose_name='Посетитеил', blank=True, null=True) @@ -103,49 +121,18 @@ class Conference(TranslatableModel, EventMixin, ExpoMixin): return conferences def get_catalog_url(self): - return '/conferences/' - - def clone(self): - """ - Return an identical copy of the instance with a new ID. - """ - if not self.pk: - raise ValueError('Instance must be saved before it can be cloned.') - - duplicate = copy.copy(self) - # Setting pk to None. Django thinking this is a new object. - duplicate.pk = None - # url must be unique - duplicate.url += '_copy' - if Conference.objects.safe_get(url=duplicate.url): - #already has copy this instance - return - - # duplicate should not be published - duplicate.is_published = False - duplicate.cancel_by_administrator = False - - ignore_fields = ['id', 'master', 'language_code'] - duplicate.translate('ru') - tr = self._meta.translations_model.objects.get(language_code = 'ru',master__id=self.pk) - for field in duplicate._translated_field_names: - - if field in ignore_fields: - continue - - setattr(duplicate, field, getattr(tr, field)) - - duplicate.save() - # but lost all ManyToMany relations and Translations. - - # copy relations - for field in self._meta.many_to_many: - source = getattr(self, field.attname) - destination = getattr(duplicate, field.attname) - for item in source.all(): - destination.add(item) - - return duplicate + return '/conference/' + + def get_audience(self): + checked = [item for item, bool in self.audience if bool==True] + audience = [] + for k, v in BIT_AUDIENCE: + for item in checked: + if item == k: + audience.append(v) + + return ', '.join(audience) + def get_calendar_url(self): return '/conference-add-calendar/%s/'%self.id @@ -153,12 +140,51 @@ class Conference(TranslatableModel, EventMixin, ExpoMixin): def get_visit_url(self): return '/conference-visit/%s/'%self.id + def get_note_by_user(self, user_id): + note = self.note.filter(user__id=user_id) + try: + return note.get().text + except: + return '' + + def tags(self): + return self.tag.language().all() + + def get_gallery(self): + if self.photogallery: + return self.photogallery + + data = {} + model = type(self) + for code, name in settings.LANGUAGES: + obj = model._meta.translations_model.objects.get(language_code = code,master__id=self.id) #access to translated fields + data['title_%s'%code] = obj.name + data['description_%s'%code] = obj.description + + gallery = Gallery() -class Statistic(models.Model): + fill_with_signal(Gallery, gallery, data) + self.photogallery = gallery + self.save() + + + return gallery + + +class Statistic(TranslatableModel): conference = models.ForeignKey(Conference, related_name='statistic') year = models.PositiveIntegerField(verbose_name='Год') - members = models.PositiveIntegerField(verbose_name='Посетители') - visitors = models.PositiveIntegerField(verbose_name='Участники') + members = models.PositiveIntegerField(verbose_name='Посетители', blank=True, null=True) + visitors = models.PositiveIntegerField(verbose_name='Участники', blank=True, null=True) + area = models.PositiveIntegerField(verbose_name='Площадь', blank=True, null=True) + countries_number = models.PositiveIntegerField(verbose_name='Количество стран', blank=True, null=True) + + translations = TranslatedFields( + countries = models.TextField(blank=True) + ) + + def to_dict(self): + return hvad_to_dict(self) class TimeTable(TranslatableModel): @@ -166,13 +192,27 @@ class TimeTable(TranslatableModel): TimeTable for business program """ - exposition = models.ForeignKey(Conference, related_name='business_program') + conference = models.ForeignKey(Conference, related_name='business_program') begin = models.DateTimeField(verbose_name='Начало') end = models.DateTimeField(verbose_name='Конец') + timetable_organiser = models.ForeignKey('organiser.Organiser', null=True, blank=True, + related_name='conf_timetable') + # + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) #translated fields translations = TranslatedFields( - name = models.CharField(verbose_name='Название', max_length=255) + name = models.CharField(verbose_name='Название программы', max_length=255, blank=True), + programe = models.TextField(verbose_name='Программа'), + speaker = models.CharField(verbose_name='Спикеры', max_length=255, blank=True), + place = models.CharField(verbose_name='Место проведения', max_length=255, blank=True) ) + def to_dict(self): + return hvad_to_dict(self) + + pre_save.connect(pre_save_handler, sender=Conference) -post_save.connect(post_save_handler, sender=Conference) \ No newline at end of file +post_save.connect(post_save_handler, sender=Conference) +post_save.connect(post_save_handler, sender=TimeTable) +post_save.connect(post_save_handler, sender=Statistic) \ No newline at end of file diff --git a/conference/urls.py b/conference/urls.py index a6b812a4..6e8b60f7 100644 --- a/conference/urls.py +++ b/conference/urls.py @@ -1,15 +1,74 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url -from views import ConferenceView +from views import ConferenceDetail, ConferenceList, ConferenceByCity, ConferenceByCountry, ConferenceByTheme,\ + ConferenceCountryCatalog, ConferenceCityCatalog, ConferenceTagCatalog, ConferenceThemeCatalog, ConferenceMembers,\ + ConferenceVisitors, ConferenceServiceView +from exposition.views import ExpositionSearchView urlpatterns = patterns('', - url(r'conference/(?P.*)/(?P\d+)/$', ConferenceView.as_view()), - url(r'conference/(?P\d+)/$', ConferenceView.as_view()), - url(r'conference/(?P.*)/$', ConferenceView.as_view()), - url(r'conference/$', ConferenceView.as_view()), - # + + + url(r'conference/add-note/(?P.*)/$', 'conference.views.add_note'), url(r'conference-add-calendar/(?P\d+)/$', 'conference.views.conference_add_calendar'), - url(r'conference-remove-calendar/(?P\d+)/$', 'conference.views.conference_remove_calendar'), url(r'conference-visit/(?P\d+)/$', 'conference.views.conference_visit'), - url(r'conference-unvisit/(?P\d+)/$', 'conference.views.conference_unvisit'), + # search + url(r'conference/search/', ExpositionSearchView.as_view()), + # country catalog + url(r'conference/country/$', ConferenceByCountry.as_view()), + url(r'conference/country/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ConferenceCountryCatalog.as_view()), + url(r'conference/country/(?P.*)/(?P\d+)/page/(?P\d+)/$', ConferenceCountryCatalog.as_view()), + url(r'conference/country/(?P.*)/page/(?P\d+)/$', ConferenceCountryCatalog.as_view()), + url(r'conference/country/(?P.*)/(?P\d+)/(?P.*)/$', ConferenceCountryCatalog.as_view()), + url(r'conference/country/(?P.*)/(?P\d+)/$', ConferenceCountryCatalog.as_view()), + url(r'conference/country/(?P.*)/$', ConferenceCountryCatalog.as_view()), + # city catalog + url(r'conference/city/$', ConferenceByCity.as_view()), + url(r'conference/city/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ConferenceCityCatalog.as_view()), + url(r'conference/city/(?P.*)/(?P\d+)/page/(?P\d+)/$', ConferenceCityCatalog.as_view()), + url(r'conference/city/(?P.*)/page/(?P\d+)/$', ConferenceCityCatalog.as_view()), + url(r'conference/city/(?P.*)/(?P\d+)/(?P.*)/$', ConferenceCityCatalog.as_view()), + url(r'conference/city/(?P.*)/(?P\d+)/$', ConferenceCityCatalog.as_view()), + url(r'conference/city/(?P.*)/$', ConferenceCityCatalog.as_view()), + # theme catalog + url(r'conference/theme/$', ConferenceByTheme.as_view()), + url(r'conference/theme/(?P.*)/country/(?P.*)/page/(?P\d+)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/country/(?P.*)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/city/(?P.*)/page/(?P\d+)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/city/(?P.*)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/(?P\d+)/page/(?P\d+)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/page/(?P\d+)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/(?P\d+)/(?P.*)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/(?P\d+)/$', ConferenceThemeCatalog.as_view()), + url(r'conference/theme/(?P.*)/$', ConferenceThemeCatalog.as_view()), + # tag catalog + url(r'conference/tag/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ConferenceTagCatalog.as_view()), + url(r'conference/tag/(?P.*)/(?P\d+)/page/(?P\d+)/$', ConferenceTagCatalog.as_view()), + url(r'conference/tag/(?P.*)/page/(?P\d+)/$', ConferenceTagCatalog.as_view()), + url(r'conference/tag/(?P.*)/(?P\d+)/(?P.*)/$', ConferenceTagCatalog.as_view()), + url(r'conference/tag/(?P.*)/(?P\d+)/$', ConferenceTagCatalog.as_view()), + url(r'conference/tag/(?P.*)/$', ConferenceTagCatalog.as_view()), + # conf additional pages + url(r'conference/(?P.*)/visitors/page/(?P\d+)/$', ConferenceVisitors.as_view()), + url(r'conference/(?P.*)/visitors/$', ConferenceVisitors.as_view()), + url(r'conference/(?P.*)/members/page/(?P\d+)/$', ConferenceMembers.as_view()), + url(r'conference/(?P.*)/members/$', ConferenceMembers.as_view()), + url(r'conference/(?P.*)/service/(?P.*)/', ConferenceServiceView.as_view()), + + # conf list + url(r'conference/(?P\d+)/(?P.*)/page/(?P\d+)/$', ConferenceList.as_view()), + url(r'conference/(?P\d+)/page/(?P\d+)/$', ConferenceList.as_view()), + url(r'conference/(?P\d+)/(?P.*)/$', ConferenceList.as_view()), + url(r'conference/(?P\d+)/$', ConferenceList.as_view()), + url(r'conference/page/(?P\d+)/$', ConferenceList.as_view()), + # conf page + url(r'conference/(?P.*)/$', ConferenceDetail.as_view()), + url(r'conference/$', ConferenceList.as_view()), + ) + + +""" + + +""" \ No newline at end of file diff --git a/conference/views.py b/conference/views.py index 7b146288..fe095af0 100644 --- a/conference/views.py +++ b/conference/views.py @@ -1,12 +1,368 @@ # -*- coding: utf-8 -*- -from django.http import HttpResponse -from models import Conference -from functions.custom_views import ExpoListView import json +import datetime +from django.conf import settings +from django.http import HttpResponse, Http404, HttpResponseRedirect +from django.contrib import messages +from django.shortcuts import get_object_or_404 +from django.contrib.contenttypes.models import ContentType +from django.views.generic import ListView, DetailView +from django.views.generic.edit import FormMixin +from django.utils.translation import ugettext as _ +from django.utils import translation +from note.models import Note +from models import Conference +from accounts.models import User +from country.models import Country +from city.models import City +from theme.models import Theme, Tag +from service.order_forms import AdvertiseForm +from functions.search_forms import ExpositionSearchForm + +MONTHES = settings.MONTHES + +class ConferenceBy(ListView): + template_name = 'conference/conference_by.html' + title1 = '' + title2 = '' + + """ + abstact class + """ + def get_context_data(self, **kwargs): + context = super(ConferenceBy, self).get_context_data(**kwargs) + context.update({'title1': self.title1, 'title2': self.title2, 'catalog': self.catalog}) + + return context + + +class ConferenceByCountry(ConferenceBy): + model = Country + title1 = _(u'По странам') + title2 = _(u'Коференции мира по странам') + catalog = 'country/' + + def get_queryset(self): + lang = translation.get_language() + return self.model.objects.select_related('conference_country')\ + .filter(conference_country__country__isnull=False, translations__language_code=lang)\ + .order_by('translations__name').distinct() + + +class ConferenceByTheme(ConferenceBy): + model = Theme + title1 = _(u'По тематикам') + title2 = _(u'Коференции мира по тематикам') + catalog = 'theme/' + + def get_queryset(self): + lang = translation.get_language() + return self.model.objects.select_related('conference_themes')\ + .filter(conference_themes__theme__isnull=False, translations__language_code=lang)\ + .order_by('translations__name').distinct() + + +class ConferenceByCity(ConferenceBy): + model = City + title1 = _(u'По городам') + title2 = _(u'Коференции мира по городам') + catalog = 'city/' + + def get_queryset(self): + lang = translation.get_language() + return self.model.objects.select_related('conference_city')\ + .filter(conference_city__city__isnull=False, translations__language_code=lang)\ + .order_by('translations__name').distinct() + + +class ConferenceCatalog(ListView): + model = Conference + paginate_by = settings.CLIENT_PAGINATION + template_name = 'client/conference/catalog.html' + search_form = ExpositionSearchForm + filter_object = None + year = None + month = None + country = None + city = None + + def get_filtered_qs(self): + # diferent for views + pass + + def get_queryset(self): + qs = self.get_filtered_qs() + + year = self.kwargs.get('year') + if year: + qs = qs.filter(data_begin__year=year) + # info for breadscrumbs + if self.country: + self.year = {'text': year, 'link': '%s%s/country/%s/%s/'% + (self.catalog_url, self.filter_object.url, self.country.url, year) + } + elif self.city: + self.year = {'text': year, 'link': '%s%s/city/%s/%s/'% + (self.catalog_url, self.filter_object.url, self.city.url, year) + } + else: + self.year = {'text': year, 'link': '%s%s/%s/'%(self.catalog_url, self.filter_object.url, year)} + + + month = self.kwargs.get('month') + monthes = {'jan': {'value': 1, 'name': _(u'Январь')}, 'feb': {'value': 2, 'name': _(u'Февраль')}, + 'mar': {'value': 3, 'name': _(u'Март')}, 'apr': {'value': 4, 'name': _(u'Апрель')}, + 'may': {'value': 5, 'name': _(u'Май')}, 'jun': {'value': 6, 'name': _(u'Июнь')}, + 'jul': {'value': 7, 'name': _(u'Июль')}, 'aug': {'value': 8, 'name': _(u'Август')}, + 'sep': {'value': 9, 'name': _(u'Сентябрь')}, 'oct': {'value': 10, 'name': _(u'Октябрь')}, + 'nov': {'value': 11, 'name': _(u'Ноябрь')}, 'dec': {'value': 12, 'name': _(u'Декабрь')}} + if month and monthes.get(month): + qs = qs.filter(data_begin__month=monthes[month]['value']) + if self.country: + self.month = {'text': monthes[month]['name'], 'link': '%s%s/country/%s/%s/%s/'% + (self.catalog_url, self.filter_object.url, self.country.url, year, month)} + elif self.city: + self.month = {'text': monthes[month]['name'], 'link': '%s%s/city/%s/%s/%s/'% + (self.catalog_url, self.filter_object.url, self.city.url, year, month)} + else: + self.month = {'text': monthes[month]['name'], 'link': '%s%s/%s/%s/'%(self.catalog_url, self.filter_object.url, year, month)} + + return qs.order_by('data_begin') + + + def get_context_data(self, **kwargs): + context = super(ConferenceCatalog, self).get_context_data(**kwargs) + context['search_form'] = self.search_form + context['filter_object'] = self.filter_object + context['year'] = self.year + context['month'] = self.month + context['catalog_url'] = self.catalog_url + return context + +class ConferenceCountryCatalog(ConferenceCatalog): + catalog_url = '/conference/country/' + def get_filtered_qs(self): + #this method used in parent get_queryset + slug = self.kwargs.get('slug') + country = get_object_or_404(Country, url=slug) + self.filter_object = country + qs = self.model.enable.filter(country=country) + return qs + +class ConferenceCityCatalog(ConferenceCatalog): + catalog_url = '/conference/city/' + def get_filtered_qs(self): + #this method used in parent get_queryset + slug = self.kwargs.get('slug') + city = get_object_or_404(City, url=slug) + qs = self.model.enable.filter(city=city) + self.filter_object = city + return qs + + +class ConferenceThemeCatalog(ConferenceCatalog): + template_name = 'conference/catalog_theme.html' + catalog_url = '/conference/theme/' + country = None + city = None + + def get_filtered_qs(self): + #this method used in parent get_queryset + slug = self.kwargs.get('slug') + country_slug = self.kwargs.get('country_slug') + city_slug = self.kwargs.get('city_slug') + theme = get_object_or_404(Theme, url=slug) + + qs = self.model.enable.filter(theme=theme) + if country_slug: + country = get_object_or_404(Country, url=country_slug) + self.country = country + qs = qs.filter(country=country) + + if city_slug: + city = get_object_or_404(City, url=city_slug) + self.city = city + qs = qs.filter(city=city) + + self.filter_object = theme + return qs + + def get_context_data(self, **kwargs): + context = super(ConferenceThemeCatalog, self).get_context_data(**kwargs) + if self.country: + context['country'] = self.country + if self.city: + context['city'] = self.city + return context + + +class ConferenceTagCatalog(ConferenceCatalog): + catalog_url = '/conference/tag/' + def get_filtered_qs(self): + #this method used in parent get_queryset + slug = self.kwargs.get('slug') + tag = get_object_or_404(Tag, url=slug) + qs = self.model.enable.filter(tag=tag) + self.filter_object = tag + return qs + + +class ConferenceVisitors(ListView): + paginate_by = settings.CLIENT_PAGINATION + model = Conference + #template_name = 'event_visitors.html' + + template_name = 'client/conference/visitors.html' + + obj = None + search_form = ExpositionSearchForm + + def get_queryset(self): + slug = self.kwargs.get('slug') + conference = get_object_or_404(self.model, url=slug) + self.obj = conference + return conference.users.all() + + def get_context_data(self, **kwargs): + context = super(ConferenceVisitors, self).get_context_data(**kwargs) + context['object'] = self.obj + context['search_form'] = self.search_form + return context + + +class ConferenceMembers(ListView): + paginate_by = settings.CLIENT_PAGINATION + model = Conference + #template_name = 'event_visitors.html' + + template_name = 'client/conference/members.html' + + obj = None + search_form = ExpositionSearchForm + + def get_queryset(self): + slug = self.kwargs.get('slug') + conference = get_object_or_404(self.model, url=slug) + self.obj = conference + return conference.company.all() + + def get_context_data(self, **kwargs): + context = super(ConferenceMembers, self).get_context_data(**kwargs) + context['object'] = self.obj + context['search_form'] = self.search_form + return context + +from service.models import Service +from service.views import order_forms +class ConferenceServiceView(FormMixin, DetailView): + model = Conference + slug_field = 'url' + service = None -class ConferenceView(ExpoListView): + def post(self, request, *args, **kwargs): + self.object = self.get_object() + service_url = self.kwargs.get('service_url') + service = get_object_or_404(Service, url=service_url) + service_form = order_forms.get(service_url) + self.form_class = service_form + form = self.get_form(service_form) + + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + + def get_context_data(self, **kwargs): + context = super(ConferenceServiceView, self).get_context_data(**kwargs) + service_url = self.kwargs.get('service_url') + service = get_object_or_404(Service, url=service_url) + self.service = service + self.template_name = service.template + form = order_forms.get(service_url) + self.form_class = form + if not form: + raise Http404 + context['form'] = self.get_form(self.form_class) + context['service'] = service + context['object'] = self.get_object() + return context + + def form_valid(self, form): + order = form.save(commit=False) + order.conference = self.object + order.save() + messages.success(self.request, _(u'Ваш запрос был успешно отправлен')) + return HttpResponseRedirect(self.request.path) + + def get_initial(self): + """ + Returns the initial data to use for forms on this view. + """ + if self.request.user.is_authenticated(): + user = self.request.user + + initial = {'person_inf': user.get_full_name(), + 'person': user.email, + 'country': user.profile.country.name if user.profile.country else '', + 'city':user.profile.city.name if user.profile.city else '', + 'phone': user.profile.phone if user.profile.phone else ''} + + + + return initial + else: + return self.initial.copy() + + + +class ConferenceDetail(DetailView): + model = Conference + slug_field = 'url' + template_name = 'client/conference/conference_detail.html' + + def get_context_data(self, **kwargs): + context = super(ConferenceDetail, self).get_context_data(**kwargs) + context['advertising_form'] = AdvertiseForm() + return context + +class ConferenceList(ListView): model = Conference - template_name = 'event_catalog.html' + paginate_by = settings.CLIENT_PAGINATION + template_name = 'client/conference/conference_list.html' + search_form = ExpositionSearchForm + catalog_url = '/conference/' + year = None + month = None + + def get_queryset(self): + if self.request.user.is_staff: + now = datetime.datetime.now().date() + qs = self.model.objects.filter(data_begin__gte=now).order_by('data_begin') + else: + qs = self.model.enable.all() + + + year = self.kwargs.get('year') + if year: + qs = qs.filter(data_begin__year=year) + # info for breadscrumbs + self.year = {'text': year, 'link': '%s%s/'%(self.catalog_url, year)} + + month = self.kwargs.get('month') + if month and MONTHES.get(month): + qs = qs.filter(data_begin__month=MONTHES[month]['value']) + self.month = {'text': MONTHES[month]['name'], 'link': '%s%s/%s/'%(self.catalog_url, year, month)} + return qs + + def get_context_data(self, **kwargs): + context = super(ConferenceList, self).get_context_data(**kwargs) + context['month'] = self.month + context['catalog_url'] = self.catalog_url + context['search_form'] = self.search_form + context['year'] = self.year + context['month'] = self.month + return context + def conference_add_calendar(request, id): args = {'success': False} @@ -14,38 +370,32 @@ def conference_add_calendar(request, id): if user.is_authenticated(): conf = Conference.objects.safe_get(id=id) - if conf: + if conf in user.calendar.get_confs(): + user.calendar.conferences.remove(conf) + args['in'] = False + else: user.calendar.conferences.add(conf) - args['success'] = True - else: - args['not_authorized'] = True + args['in'] = True args['success'] = True - - return HttpResponse(json.dumps(args), content_type='application/json') - -def conference_remove_calendar(request, id): - args = {'success': False} - if request.user: - user = request.user - conf = Conference.objects.safe_get(id=id) - if conf: - user.calendar.conferences.remove(conf) - args['success'] = True else: args['not_authorized'] = True args['success'] = True - return HttpResponse(json.dumps(args), content_type='application/json') + def conference_visit(request, id): args = {'success': False} user = request.user if user.is_authenticated(): conf = Conference.objects.safe_get(id=id) - if conf: + if user in conf.users.all(): + conf.users.remove(user) + args['in'] = False + else: conf.users.add(user) - args['success'] = True + args['in'] = True + args['success'] = True else: args['not_authorized'] = True @@ -53,18 +403,34 @@ def conference_visit(request, id): return HttpResponse(json.dumps(args), content_type='application/json') -def conference_unvisit(request, id): +def add_note(request, slug): args = {'success': False} - user = request.user - if user.is_authenticated(): - conf = Conference.objects.safe_get(id=id) - if conf: - conf.users.remove(user) - else: - args['not_authorized'] = True - args['success'] = True - return HttpResponse(json.dumps(args), content_type='application/json') + if request.user.is_authenticated(): + if request.GET: + text = request.GET['note_text'] + try: + conf = Conference.objects.get(url=slug) + except Conference.DoesNotExist: + raise Http404 + + ct = ContentType.objects.get_for_model(conf) + object_id = conf.id + user = User.objects.get(id=request.user.id) + if Note.objects.filter(user=user, content_type=ct, object_id=object_id).exists(): + Note.objects.filter(user=user, content_type=ct, object_id=object_id).update(text=text) + else: + Note.objects.create(content_type=ct, object_id=object_id, user=user, text=text) + + user.calendar.conferences.add(conf) + args['success'] = True + args['text'] = text + + else: + args['not_authorized'] = True + args['success'] = True + + return HttpResponse(json.dumps(args), content_type='application/json') \ No newline at end of file diff --git a/country/models.py b/country/models.py index 4d5a7402..4b387d58 100644 --- a/country/models.py +++ b/country/models.py @@ -13,6 +13,9 @@ from service.models import Service from exposition.models import Exposition from place_exposition.models import PlaceExposition from organiser.models import Organiser +from conference.models import Conference +from seminar.models import Seminar +from webinar.models import Webinar # func from functions.custom_fields import EnumField from functions.db import db_table_exists @@ -164,6 +167,18 @@ class Country(TranslatableModel): return len(Exposition.objects.filter(country=self.id)) + def conferences_number(self): + + return Conference.objects.filter(country=self.id).count() + + def seminars_number(self): + + return Seminar.objects.filter(country=self.id).count() + + def webinars_number(self): + + return Webinar.objects.filter(country=self.id).count() + def active_cities(self): lang = translation.get_language() return City.objects.select_related('exposition_city')\ diff --git a/exposition/forms.py b/exposition/forms.py index d3c0bd3e..be955bc1 100644 --- a/exposition/forms.py +++ b/exposition/forms.py @@ -28,6 +28,8 @@ from functions.admin_forms import AdminFilterForm places = [(item.id, item.name) for item in PlaceExposition.objects.language().all()] places.insert(0,('', 'Не выбрано')) + + class ExpositionCreateForm(forms.Form): """ Create Exposition form for creating exposition @@ -80,10 +82,6 @@ class ExpositionCreateForm(forms.Form): application_deadline = forms.DateField(label=u'Срок подачи стенда', required=False) min_stand_size = forms.CharField(label=u'Минимальный размер стенда', required=False) - #price_day = forms.CharField(label=u'Цена за 1 день', required=False) - #price_all = forms.CharField(label=u'Цена за все дни', required=False) - #price_day_bar = forms.CharField(label=u'Цена на стойке 1 день', required=False) - #price_all_bar = forms.CharField(label=u'Цена на стойке все дни', required=False) price_catalog = forms.CharField(label=u'Цена за каталог', required=False) tax = forms.BooleanField(label=u'Налог включен', initial=True, required=False) min_closed_area = forms.CharField(label=u'Минимальная цена закрытой НЕ оборудованной площади', required=False) @@ -227,6 +225,8 @@ class ExpositionCreateForm(forms.Form): if data.get('place'): exposition.place = PlaceExposition.objects.get(id=data['place']) + else: + exposition.place = None # fill translated fields and save object fill_with_signal(Exposition, exposition, data) diff --git a/exposition/management/commands/exposition_load.py b/exposition/management/commands/exposition_load.py index 8cef995e..447099d4 100644 --- a/exposition/management/commands/exposition_load.py +++ b/exposition/management/commands/exposition_load.py @@ -28,7 +28,7 @@ class Command(BaseCommand): row_list = [sheet.row_values(row_number) for row_number in range(sheet.nrows)] labels = [label for label in row_list[0]] existing = 0 - for row_number, row in enumerate(row_list[1246:]): + for row_number, row in enumerate(row_list[1:]): #print(row_number) if row[0] != '': diff --git a/exposition/models.py b/exposition/models.py index 5c7ef174..36a57f1f 100644 --- a/exposition/models.py +++ b/exposition/models.py @@ -19,6 +19,8 @@ from functions.model_mixin import EventMixin, ExpoMixin from functions.translate import fill_with_signal from photologue.models import Gallery from import_xls.model_utils import ExpoImportManager +from functions.models_methods import hvad_to_dict + AUDIENCE1 = ((None,_(u'Не выбрано')), ('experts', _(u'Специалисты')), @@ -28,11 +30,10 @@ AUDIENCE1 = ((None,_(u'Не выбрано')), AUDIENCE = (None,'experts', 'experts and consumers', 'general public') -BIT_AUDIENCE = (('experts', _(u'Специалисты')), ('experts and consumers', _(u'Специалисты и потребители')), - ('general public', _(u'Широкая публика'))) +BIT_AUDIENCE = settings.BIT_AUDIENCE -CURRENCY = ('RUB', 'USD', 'EUR', 'RMB', 'GBP', 'AED', 'SGD', 'TRY', 'CZK', 'CHF', 'SEK', 'LKR') +CURRENCY = settings.CURRENCY # check if table exist and create flags if true flags = [str(item.id) for item in Service.objects.all()] if db_table_exists('service_service') else [] @@ -284,48 +285,6 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): return code return cur - def clone(self): - """ - Return an identical copy of the instance with a new ID. - """ - if not self.pk: - raise ValueError('Instance must be saved before it can be cloned.') - - duplicate = copy.copy(self) - # Setting pk to None. Django thinking this is a new object. - duplicate.pk = None - # url must be unique - duplicate.url += '_copy' - if Exposition.objects.safe_get(url=duplicate.url): - #already has copy this instance - return - - # duplicate should not be published - duplicate.is_published = False - duplicate.cancel_by_administrator = False - - ignore_fields = ['id', 'master', 'language_code'] - duplicate.translate('ru') - tr = self._meta.translations_model.objects.get(language_code = 'ru',master__id=self.pk) - for field in duplicate._translated_field_names: - - if field in ignore_fields: - continue - - setattr(duplicate, field, getattr(tr, field)) - - duplicate.save() - # but lost all ManyToMany relations and Translations. - - # copy relations - for field in self._meta.many_to_many: - source = getattr(self, field.attname) - destination = getattr(duplicate, field.attname) - for item in source.all(): - destination.add(item) - - return duplicate - class Statistic(TranslatableModel): exposition = models.ForeignKey(Exposition, related_name='statistic') @@ -343,10 +302,6 @@ class Statistic(TranslatableModel): return hvad_to_dict(self) - -from django.core import serializers -from functions.models_methods import hvad_to_dict - class TimeTable(TranslatableModel): """ TimeTable for business program @@ -389,32 +344,7 @@ class TmpTimeTable(TranslatableModel): place = models.CharField(verbose_name='Место проведения', max_length=255, blank=True) ) - def clone(self, exposition=None): - """ - Return an identical copy of the instance with a new ID. - """ - if not self.pk: - raise ValueError('Instance must be saved before it can be cloned.') - - duplicate = copy.copy(self) - # Setting pk to None. Django thinking this is a new object. - duplicate.pk = None - duplicate.exposition = exposition - - - ignore_fields = ['id', 'master', 'language_code'] - duplicate.translate('ru') - tr = self._meta.translations_model.objects.get(language_code = 'ru',master__id=self.pk) - for field in duplicate._translated_field_names: - - if field in ignore_fields: - continue - - setattr(duplicate, field, getattr(tr, field)) - - duplicate.save() - return duplicate pre_save.connect(pre_save_handler, sender=Exposition) post_save.connect(post_save_handler, sender=Exposition) diff --git a/exposition/urls.py b/exposition/urls.py index b0f74175..47ddd7d9 100644 --- a/exposition/urls.py +++ b/exposition/urls.py @@ -16,7 +16,7 @@ urlpatterns = patterns('', url(r'exposition-visit/(?P\d+)/$', 'exposition.views.exposition_visit'), # search url(r'expo/search/', ExpositionSearchView.as_view()), - #url(r'expo/search/', 'exposition.views.search_test'), + # country catalog diff --git a/exposition/views.py b/exposition/views.py index da69a6e3..f960cf63 100644 --- a/exposition/views.py +++ b/exposition/views.py @@ -19,6 +19,7 @@ from country.models import Country from city.models import City from theme.models import Theme, Tag from note.models import Note +from service.order_forms import AdvertiseForm from functions.search_forms import ExpositionSearchForm from functions.custom_views import ExpoSearchView @@ -85,20 +86,6 @@ class ExpositionSearchView(ExpoSearchView): search_form = ExpositionSearchForm model = Exposition -""" -def search_test(request): - if request.GET: - form = ExpositionSearchForm(request.GET) - if form.is_valid(): - form.data_with_parents = form.get_form_data() - object_list = form.search() - - context = {} - context['object_list'] = object_list - context['search_form'] = form - return render_to_response('client/exposition/search.html', context) -""" - def exposition_add_calendar(request, id): args = {'success': False} @@ -132,12 +119,6 @@ def exposition_visit(request, id): args['in'] = True args['success'] = True - """ - if exp: - exp.users.add(user) - args['success'] = True - """ - else: args['not_authorized'] = True args['success'] = True @@ -145,7 +126,7 @@ def exposition_visit(request, id): return HttpResponse(json.dumps(args), content_type='application/json') #------------------------------------------------------------------------------ -from service.order_forms import AdvertiseForm + class ExpoDetail(DetailView): model = Exposition slug_field = 'url' @@ -273,12 +254,7 @@ class ExpoList(ListView): context['month'] = self.month return context -MONTHES = {'jan': {'value': 1, 'name': _(u'Январь')}, 'feb': {'value': 2, 'name': _(u'Февраль')}, - 'mar': {'value': 3, 'name': _(u'Март')}, 'apr': {'value': 4, 'name': _(u'Апрель')}, - 'may': {'value': 5, 'name': _(u'Май')}, 'jun': {'value': 6, 'name': _(u'Июнь')}, - 'jul': {'value': 7, 'name': _(u'Июль')}, 'aug': {'value': 8, 'name': _(u'Август')}, - 'sep': {'value': 9, 'name': _(u'Сентябрь')}, 'oct': {'value': 10, 'name': _(u'Октябрь')}, - 'nov': {'value': 11, 'name': _(u'Ноябрь')}, 'dec': {'value': 12, 'name': _(u'Декабрь')}} +MONTHES = settings.MONTHES class ExpoCatalog(ListView): model = Exposition diff --git a/import_xls/excel_settings.py b/import_xls/excel_settings.py index eb415e05..e7d7dcaa 100644 --- a/import_xls/excel_settings.py +++ b/import_xls/excel_settings.py @@ -339,15 +339,18 @@ event_sett = { u'Дата окончания:(YYYY-MM-DD)':{u'field': u'data_end', u'func': to_date}, u'Страна':{u'field': u'country', u'func': to_country}, u'Город':{u'field': u'city_id', u'func': to_city, 'extra_values': 'country'}, - u'Место проведения':{u'field': u'place', u'func': to_place}, + #u'Место проведения':{u'field': u'place', u'func': to_place}, u'ID Тематики':{u'field': u'theme', u'func': to_theme, u'method': True},### u'Теги':{u'field': u'tag', u'func': to_tag, u'method': True}, #u'Организатор №1':{u'field': u'organiser', u'func': to_tag},#### #u'Организатор №2':{u'field': u'organiser', u'func': to_tag},#### u'Описание события':{u'field': u'description', u'func': unicode}, + u'Основные темы':{u'field': u'main_themes', u'func': unicode}, + u'Условия и скидка':{u'field': u'discount_description', u'func': unicode}, u'Периодичность':{u'field': u'periodic', u'func': to_periodic},### u'Аудитория':{u'field': u'audience', u'func': to_audience}, u'Официальный веб-сайт':{u'field': u'web_page', u'func': to_url}, + u'Линк на регистрацию':{u'field': u'link', u'func': to_url}, u'Экспонируемые продукты':{u'field': u'products', u'func': unicode}, u'Время работы':{u'field': u'time', u'func': unicode}, u'Логотип':{u'field': u'logo', u'func': save_logo, u'method': True}, diff --git a/proj/admin.py b/proj/admin.py index 1d9ebb3c..3258345e 100644 --- a/proj/admin.py +++ b/proj/admin.py @@ -9,9 +9,11 @@ from file.models import TmpFile, FileModel from file.forms import FileModelForm, FileForm from city.models import City from theme.models import Tag +from article.models import Article from django.db.models.loading import get_model + def admin_home(request): return render_to_response('base.html') @@ -39,6 +41,24 @@ def ajax_tag(request): else: #request empty - send empty array return HttpResponse('[]', content_type='application/json') + + +def get_news(request): + """ + + return array of tags (id, name) filtered by theme values from request + + """ + term = request.GET['term'].capitalize() + if not term: + qs = Article.objects.news()[:50] + else: + qs = Article.objects.news().filter(translations__main_title__contains=term) + result = [{'id': item.id, 'label': item.main_title} for item in qs] + + return HttpResponse(json.dumps(result), content_type='application/json') + + ''' def ajax_post_file(request, obj_id=None): """ diff --git a/proj/admin_urls.py b/proj/admin_urls.py index 55e492a1..f601f170 100644 --- a/proj/admin_urls.py +++ b/proj/admin_urls.py @@ -45,6 +45,7 @@ urlpatterns = required( url(r'^ajax_delete_file/', 'proj.admin.ajax_delete_file'), url(r'^ajax_city/', 'proj.admin.ajax_city'), url(r'^ajax_tag/', 'proj.admin.ajax_tag'), + url(r'^ajax/get-news/', 'proj.admin.get_news'), # url(r'^ckeditor/', include('ckeditor.urls')), url(r'^tinymce/', include('tinymce.urls')), diff --git a/proj/settings.py b/proj/settings.py index f4090393..6b1ac110 100644 --- a/proj/settings.py +++ b/proj/settings.py @@ -26,6 +26,7 @@ DATABASES = { 'PORT': '', } } + ''' CACHES = { 'default': { @@ -76,10 +77,9 @@ USE_L10N = True # If you set this to False, Django will not use timezone-aware datetimes. USE_TZ = True -# Absolute filesystem path to the directory that will hold user-uploaded files. -# Example: "/var/www/example.com/media/" -MEDIA_ROOT = os.path.join(SITE_ROOT, 'media/')#'/home/kotzilla/Documents/qwer/proj/media/' -CKEDITOR_UPLOAD_PATH = os.path.join(SITE_ROOT, 'media/upload')#'/home/kotzilla/Documents/qwer/proj/media/upload' + +MEDIA_ROOT = os.path.join(SITE_ROOT, 'media/') +CKEDITOR_UPLOAD_PATH = os.path.join(SITE_ROOT, 'media/upload') CKEDITOR_CONFIGS = { @@ -95,37 +95,13 @@ CKEDITOR_CONFIGS = { }, } -#TINYMCE_JS_URL = os.path.join(MEDIA_ROOT, "js/tiny_mce/tiny_mce.js") -#TINYMCE_DEFAULT_CONFIG = { -# 'plugins': "table,spellchecker,paste,searchreplace", -# 'theme': "advanced", -# 'cleanup_on_startup': True, -# 'custom_undo_redo_levels': 10, -# 'width' : 565, -# 'height' : 100 -#} -#TINYMCE_SPELLCHECKER = True -#TINYMCE_COMPRESSOR = True - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash. -# Examples: "http://example.com/media/", "http://media.example.com/" + MEDIA_URL = '/media/' -# Absolute path to the directory static files should be collected to. -# Don't put anything in this directory yourself; store your static files -# in apps' "static/" subdirectories and in STATICFILES_DIRS. -# Example: "/var/www/example.com/static/" STATIC_ROOT = os.path.join(SITE_ROOT, 'static') -# URL prefix for static files. -# Example: "http://example.com/static/", "http://static.example.com/" STATIC_URL = '/static/' -# Additional locations of static files - -# List of finder classes that know how to find static files in -# various locations. STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', @@ -135,7 +111,7 @@ STATICFILES_FINDERS = ( # Make this unique, and don't share it with anybody. SECRET_KEY = '=yz1@ko%1s8bmel)c84#s*xpxn%4(1e+smdnh*@rdm*5%v!mln' -# List of callables that know how to import templates from various sources. + TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', @@ -346,7 +322,7 @@ INSTALLED_APPS = ( 'bitfield', 'djutils', 'pytils', - 'pymorphy', + #'pymorphy', 'password_reset', #'social_auth', 'social.apps.django_app.default', @@ -418,4 +394,16 @@ except ImportError, e: ADMIN_PAGINATION = 20 CLIENT_PAGINATION = 15 TEMPLATE_DEBUG = DEBUG -NO_LOGO = '/static/client/img/no-logo.png' \ No newline at end of file +NO_LOGO = '/static/client/img/no-logo.png' + +# events settings +CURRENCY = ('RUB', 'USD', 'EUR', 'RMB', 'GBP', 'AED', 'SGD', 'TRY', 'CZK', 'CHF', 'SEK', 'LKR', 'UAH', 'IDR', 'PLN','JPY') +BIT_AUDIENCE = (('experts', _(u'Специалисты')), ('experts and consumers', _(u'Специалисты и потребители')), + ('general public', _(u'Широкая публика'))) + +MONTHES = {'jan': {'value': 1, 'name': _(u'Январь')}, 'feb': {'value': 2, 'name': _(u'Февраль')}, + 'mar': {'value': 3, 'name': _(u'Март')}, 'apr': {'value': 4, 'name': _(u'Апрель')}, + 'may': {'value': 5, 'name': _(u'Май')}, 'jun': {'value': 6, 'name': _(u'Июнь')}, + 'jul': {'value': 7, 'name': _(u'Июль')}, 'aug': {'value': 8, 'name': _(u'Август')}, + 'sep': {'value': 9, 'name': _(u'Сентябрь')}, 'oct': {'value': 10, 'name': _(u'Октябрь')}, + 'nov': {'value': 11, 'name': _(u'Ноябрь')}, 'dec': {'value': 12, 'name': _(u'Декабрь')}} \ No newline at end of file diff --git a/settings/admin.py b/settings/admin.py index 60bdffb1..bda57fed 100644 --- a/settings/admin.py +++ b/settings/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import json from django.shortcuts import render_to_response from django.http import HttpResponseRedirect, HttpResponse from django.core.context_processors import csrf @@ -56,6 +57,7 @@ from conference.models import Conference from seminar.models import Seminar from webinar.models import Webinar from theme.models import Theme +from article.models import Article import datetime def main_page(request): @@ -70,10 +72,21 @@ def main_page(request): exposition_themes = Theme.objects.filter(main_page__gt=0, types=Theme.types.exposition) confrence_themes = Theme.objects.filter(main_page__gt=0, types=Theme.types.conference) seminar_themes = Theme.objects.filter(main_page__gt=0, types=Theme.types.seminar) + news = Article.objects.news().filter(main_page=1) + blogs = Article.objects.blogs().filter(main_page=1) themes = {'exposition_themes': exposition_themes, 'conference_themes': confrence_themes, 'seminar_themes':seminar_themes} - args = {'event_form':MainPageEvent(initial=events), 'theme_form':MainPageThemes(initial=themes), 'article_form':MainPageArticle(), - 'news_form':MainPageNews(), 'photoreport_form':MainPagePhotoreport(),} + + news_form = MainPageNews(initial={'main_page_news':','.join(['%d'%item.id for item in news])}) + a = [{'id': int(item.id), 'text': item.main_title} for item in news] + news_form.fields['main_page_news'].widget.attrs['data-init-text'] = json.dumps(a) + article_form = MainPageArticle(initial={'article' : blogs}) + + + + args = {'event_form' : MainPageEvent(initial=events), 'theme_form' : MainPageThemes(initial=themes), + 'article_form' : article_form, + 'news_form' : news_form, 'photoreport_form' : MainPagePhotoreport(),} args.update(csrf(request)) return render_to_response('admin/settings/main_page.html', args) \ No newline at end of file diff --git a/settings/admin_urls.py b/settings/admin_urls.py index e857b268..fb38fbeb 100644 --- a/settings/admin_urls.py +++ b/settings/admin_urls.py @@ -7,6 +7,6 @@ urlpatterns = patterns('settings.admin', url(r'^main-page/photoreports/$', 'handle_photoreports'), url(r'^main-page/themes/$', 'handle_themes'), url(r'^main-page/news/$', 'handle_news'), - url(r'^main-page/articles/$', 'handle_articles'), + url(r'^main-page/blogs/$', 'handle_articles'), url(r'^$', 'change_settings'), ) diff --git a/settings/forms.py b/settings/forms.py index 7d425b38..26e0afae 100644 --- a/settings/forms.py +++ b/settings/forms.py @@ -146,20 +146,33 @@ class MainPagePhotoreport(forms.Form): class MainPageNews(forms.Form): - news = forms.ModelMultipleChoiceField(queryset=News.objects.all()) + main_page_news = forms.CharField(label=u'Новости', widget=forms.HiddenInput(), required=False,) def save(self): data = self.cleaned_data - news = data['news'] + news = data['main_page_news'] + Article.objects.news().exclude(id__in=[item.id for item in news]).update(main_page=0) news.update(main_page=1) + def clean_main_page_news(self): + news = self.cleaned_data['main_page_news'] + if news: + news = news.split(',') + qs = Article.objects.news().filter(id__in=news) + else: + qs = Article.objects.none() + return qs + + + class MainPageArticle(forms.Form): - article = forms.ModelMultipleChoiceField(queryset=Article.objects.all()) + article = forms.ModelMultipleChoiceField(queryset=Article.objects.blogs(), required=False, + widget=forms.SelectMultiple(attrs={'style': 'width: 100%;'})) def save(self): data = self.cleaned_data - articles = data['articles'] - articles.update(main_page=1) - def save(self): - pass \ No newline at end of file + articles = data['article'] + + Article.objects.blogs().exclude(id__in=[item.id for item in articles]).update(main_page=0) + articles.update(main_page=1) \ No newline at end of file diff --git a/static/custom_js/main.js b/static/custom_js/main.js index 4826e93e..3c301189 100644 --- a/static/custom_js/main.js +++ b/static/custom_js/main.js @@ -274,7 +274,52 @@ $(document).ready(function(){ }); + $('#id_main_page_news').select2({ + placeholder: "Новости", + multiple: true, + width: '100%', + ajax: { + + url: "/admin/ajax/get-news/", + dataType: "json", + quietMillis: 200, + data: function(term, page){ + return {term: term, + page: page}; + }, + + results: function (data) { + var results = []; + $.each(data, function(index, item){ + results.push({ + id: item.id, + text: item.label + }); + }); + return {results: results}; + } + }, + initSelection : function(element, callback) { + var text = $(element).attr('data-init-text'); + var data = $.parseJSON(text); + console.log(data); + /* + var data = []; + $(element.val().split(",")).each(function(i) { + var item = this.split(':'); + data.push({ + id: item[0], + text: item[1] + }); + }); + console.log(data) + */ + callback(data); + + } + + }); // selects $('#id_tag').select2({ diff --git a/templates/admin/article/blog_form.html b/templates/admin/article/blog_form.html index eb531763..5b221c9f 100644 --- a/templates/admin/article/blog_form.html +++ b/templates/admin/article/blog_form.html @@ -18,7 +18,7 @@ {% block body %}
{% csrf_token %}
- {% if article %} Изменить {% else %} Добавить {% endif %}статью + {% if article %} Изменить {% else %} Добавить {% endif %}статью{% if article %}(на сайте){% endif %}
@@ -88,6 +88,39 @@ {# descriptions #} {% include 'admin/forms/multilang.html' with field='descriptions' form=form languages=languages %}
+ +
+
+
+

Файлы

+
+
+ {% if article %} + {# button that shows modal window with file form #} + Добавить файл + {% else %} +

Файлы можно добавлять только после введения основных данных

+ {% endif %} + {# this div shows list of files and refreshes when new file added #} +
+ + + + + + + + + + + + + {% include 'file_list.html' with files=files %} + +
idФайлИмяНазначение
+
+ +
@@ -96,4 +129,6 @@
+ +{% include 'admin/includes/file_form.html' with file_form=file_form object=article %} {% endblock %} \ No newline at end of file diff --git a/templates/admin/conference/conference.html b/templates/admin/conference/conference.html new file mode 100644 index 00000000..4f9755ae --- /dev/null +++ b/templates/admin/conference/conference.html @@ -0,0 +1,465 @@ +{% extends 'base.html' %} +{% load static %} +{% load thumbnail %} + +{% block styles %} +.hover{ + display:none; + margin-bottom:10px; + +} +.photo:hover .hover { + display: block; +} +{% endblock %} + +{# Displays exposition form and file form in modal window #} + + {% block scripts %} + + + + {# selects #} + + + {# datepicker #} + + + + + + + + + + {# datetimepicker #} + + + + + {% endblock %} + +{% block body %} +
{% csrf_token %} +
+ {% if object %} Изменить {% else %} Добавить {% endif %}конференцию{% if object %}(на сайте){% endif %} + +
+
+

Основная информация

+
+
+ {# Hidden inputs uses for comparing with TmpFile objects #} + {{ form.key }} + {# Hidden input uses in clean method for checking url #} + {{ form.exposition_id }} + {# name #} + {% with field='name' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + {# main_title #} + {% with field='main_title' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + {# data_begin #} +
+ +
{{ form.data_begin }} + {{ form.data_begin.errors }} +
+
+ {# data_end #} +
+ +
{{ form.data_end }} + {{ form.data_end.errors }} +
+
+ + {# country #} +
+ +
{{ form.country }} + {{ form.country.errors }} +
+
+ {# city #} +
+ +
{{ form.city }} + {{ form.city.errors }} +
+
+ {# place #} +
+ +
{{ form.place }} + {{ form.place.errors }} +
+
+ {# theme #} +
+ +
{{ form.theme }} + {{ form.theme.errors }} +
+
+ {# tag #} +
+ +
{{ form.tag }} + {{ form.tag.errors }} +
+
+
+
+ +
+
+

Дополнительная информация

+
+
+ {# description #} + {% with field='description' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + + {# web_page #} +
+ +
{{ form.web_page }} + {{ form.web_page.errors }} +
+
+ {# link #} +
+ +
{{ form.link }} + {{ form.link.errors }} +
+
+ {# main_themes #} + {% with field='main_themes' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + {# time #} + {% with field='time' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + {# foundation_year #} +
+ +
{{ form.foundation_year }} + {{ form.foundation_year.errors }} +
+
+ {# quality_label #} +
+ +
{{ form.quality_label }} + {{ form.quality_label.errors }} +
+
+ {# canceled #} +
+ +
{{ form.canceled }} + {{ form.canceled.errors }} +
+
+ {# moved #} +
+ +
{{ form.moved }} + {{ form.moved.errors }} +
+
+ {# expohit #} +
+ +
{{ form.expohit }} + {{ form.expohit.errors }} +
+
+ +
+
+
+
+

Условия участия

+
+
+ {# currency #} +
+ +
{{ form.currency }} + {{ form.currency.errors }} +
+
+ {# tax #} +
+ +
{{ form.tax }} + {{ form.tax.errors }} +
+
+ {# min_price #} +
+ +
{{ form.min_price }} + {{ form.min_price.errors }} +
+
+ {# max_price #} +
+ +
{{ form.max_price }} + {{ form.max_price.errors }} +
+
+ {# discount #} +
+ +
{{ form.discount }} + {{ form.discount.errors }} +
+
+ {# discount_description #} + {% with field='discount_description' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + +
+
+
+
+

Деловая программа

+
+
+ {% if object %} +
+ + + + + + + + + + + + + + + + + + + + + + {% for timetable in timetables %} + + + + + + + + + {% endfor %} + +
ПрограммаНачалоКонецСпикерыОрганизатор 
{{ timetable.programe|safe }}{{ timetable.begin|date:"o-m-d H:i" }}{{ timetable.end|date:"o-m-d H:i" }}{{ timetable.speaker }} + {{ timetable.timetable_organiser }} + + +
+
+ + Добавить program + {% else %} +

Деловую программу можно добавлять только после введения основных данных

+ {% endif %} + + +
+
+ +
+
+

Статистика

+
+ +
+ {# foundation_year #} +
+ +
{{ form.foundation_year }} + {{ form.foundation_year.errors }} +
+
+ {# area #} +
+ +
{{ form.area }} + {{ form.area.errors }} +
+
+ {# members #} +
+ +
{{ form.members }} + {{ form.members.errors }} +
+
+ {# visitors #} +
+ +
{{ form.visitors }} + {{ form.visitors.errors }} +
+
+ + {% if object %} +
+ + + + + + + + + + + + + {% for stat in object.statistic.all %} + + + + + + + + {% endfor %} + +
ГодПосетителиУчастникиПлощадь
{{ stat.year }}{{ stat.visitors }}{{ stat.members }}{{ stat.area }} Удалить
+
+ Добавить год +
+ + + {% else %} + {% endif %} + +
+
+
+

Файлы

+
+
+ {% if object %} + {# button that shows modal window with file form #} + Добавить файл + {% else %} +

Файлы можно добавлять только после введения основных данных

+ {% endif %} + {# this div shows list of files and refreshes when new file added #} +
+ + + + + + + + + + + + + {% include 'file_list.html' with files=files %} + +
idФайлИмяНазначение
+
+ +
+
+ + {% include 'admin/includes/photogallery.html' with object=object %} + +
+
+

Мета данные

+
+
+ {# keywords #} + {% with field='keywords' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + {# title #} + {% with field='title' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} + {# descriptions #} + {% with field='descriptions' form=form languages=languages %} + {% include 'admin/forms/multilang.html' %} + {% endwith %} +
+
+ + +
+ + +
+ +
+
+ + +{% include 'admin/includes/photo_form.html' with form=photo_form object=object %} +{% include 'admin/includes/file_form.html' with file_form=file_form object=object %} +{% include 'admin/includes/stat_form.html' with form=stat_form object=object %} +{% endblock %} \ No newline at end of file diff --git a/templates/admin/exposition/exposition.html b/templates/admin/exposition/exposition.html index c6543b7a..4ef1496b 100644 --- a/templates/admin/exposition/exposition.html +++ b/templates/admin/exposition/exposition.html @@ -78,8 +78,6 @@ {% block body %} -{# Uses multilang.html template for translated fields #} -
{% csrf_token %}
{% if object %} Изменить {% else %} Добавить {% endif %}выставку{% if object %}(на сайте){% endif %} diff --git a/templates/admin/settings/main_page.html b/templates/admin/settings/main_page.html index f66f7933..453cd5aa 100644 --- a/templates/admin/settings/main_page.html +++ b/templates/admin/settings/main_page.html @@ -31,7 +31,7 @@
-
+

Новости

@@ -43,12 +43,14 @@
-
+
+
+

Обзоры

-
{% csrf_token %} + {% csrf_token %} {{ article_form }}
diff --git a/templates/client/article/article.html b/templates/client/article/article.html index 8e266887..f2e6882b 100644 --- a/templates/client/article/article.html +++ b/templates/client/article/article.html @@ -17,7 +17,7 @@
- {% include 'includes/article/article_logo.html' with obj=object %} + {% include 'client/includes/article/article_logo.html' with obj=object %}

{{ object.main_title }}

{{ object.created|date:"d E Y" }}{{ object.author.get_full_name }} {{ object.description|safe }} diff --git a/templates/client/article/blog_list.html b/templates/client/article/blog_list.html index e3a9a947..574b69a2 100644 --- a/templates/client/article/blog_list.html +++ b/templates/client/article/blog_list.html @@ -44,7 +44,7 @@ {% include 'includes/article/article_preview.html' with obj=blog %}

{{ blog.main_title }}

- {{ blog.preview }} + {{ blog.preview|safe }} {{ blog.created|date:"d E Y" }}{% if blog.tag.all.exists %}{% include 'includes/article_tags.html' with obj=blog %}{% endif %}
diff --git a/templates/client/conference/catalog.html b/templates/client/conference/catalog.html new file mode 100644 index 00000000..08fcc6ce --- /dev/null +++ b/templates/client/conference/catalog.html @@ -0,0 +1,40 @@ +{% extends 'base_catalog.html' %} +{% load template_filters %} +{% load i18n %} + +{% block bread_scrumbs %} + +{% endblock %} + + +{% block page_title %} +
+

{% trans 'Конференции' %}: {{ filter_object.name }}

+
+ + {% include 'includes/exposition/catalog_filter_period.html' %} + +{% endblock %} + +{% block content_list %} + {% include 'includes/conference/conference_list.html' with object_list=object_list %} +{% endblock %} + +{% block paginator %} + {% include 'includes/catalog_paginator.html' with page_obj=page_obj %} +{% endblock %} \ No newline at end of file diff --git a/templates/client/conference/catalog_theme.html b/templates/client/conference/catalog_theme.html new file mode 100644 index 00000000..1da08725 --- /dev/null +++ b/templates/client/conference/catalog_theme.html @@ -0,0 +1,65 @@ +{% extends 'base_catalog.html' %} +{% load template_filters %} +{% load i18n %} + +{% block bread_scrumbs %} + +{% endblock %} + + +{% block page_title %} +
+

{% trans 'Конференции' %}: {{ filter_object.name }}

+
+ + {% include 'includes/exposition/catalog_filter_period.html' %} + +{% endblock %} + +{% block content_list %} + {% include 'includes/conference/conference_list.html' with object_list=object_list %} +{% endblock %} + +{% block paginator %} + {% include 'includes/catalog_paginator.html' with page_obj=page_obj %} +{% endblock %} \ No newline at end of file diff --git a/templates/client/conference/conference_by.html b/templates/client/conference/conference_by.html new file mode 100644 index 00000000..2fa2aec5 --- /dev/null +++ b/templates/client/conference/conference_by.html @@ -0,0 +1,42 @@ +{% extends 'base_catalog.html' %} +{% load static %} +{% load i18n %} +{% load template_filters %} + +{% block style %} + +{% endblock %} + + +{% block bread_scrumbs %} + +{% endblock %} + +{% block page_title %} +
+

{{ title2 }}:

+
+{% endblock %} + +{% block content_list %} + {% for obj in object_list %} + {% set cur_word = obj.name %} + {% if cur_word|slice:":1"|lower != prev_word|slice:":1"|lower and forloop.counter != 1 %} + + {% endif %} + {% if cur_word|slice:":1"|lower != prev_word|slice:":1"|lower %} +
    +
    {{ cur_word|slice:":1"|upper }}
    + {% endif %} +
  • + {{ obj.name }} ({{ obj.conferences_number }}) +
  • + {% set prev_word = obj.name %} + {% endfor %} + +{% endblock %} + diff --git a/templates/client/conference/conference_detail.html b/templates/client/conference/conference_detail.html new file mode 100644 index 00000000..f558733d --- /dev/null +++ b/templates/client/conference/conference_detail.html @@ -0,0 +1,24 @@ +{% extends 'base_catalog.html' %} +{% load i18n %} + +{% block bread_scrumbs %} + +{% endblock %} + + +{% block page_title %} +{% endblock %} + +{% block content_list %} + {% include 'client/includes/conference/conference_object.html' with event=object %} +{% endblock %} + +{% block paginator %} + +{% endblock %} \ No newline at end of file diff --git a/templates/client/conference/conference_list.html b/templates/client/conference/conference_list.html new file mode 100644 index 00000000..dafbc47e --- /dev/null +++ b/templates/client/conference/conference_list.html @@ -0,0 +1,42 @@ +{% extends 'base_catalog.html' %} +{% load i18n %} + +{% block bread_scrumbs %} + +{% endblock %} + + +{% block page_title %} +
    +

    {% trans 'Конференции' %}

    +
    + + {% include 'includes/exposition/catalog_filter_period.html' %} + + +{% endblock %} + +{% block content_list %} + + {% include 'includes/conference/conference_list.html' with object_list=object_list %} + +{% endblock %} + +{% block paginator %} + {% include 'includes/catalog_paginator.html' with page_obj=page_obj %} +{% endblock %} \ No newline at end of file diff --git a/templates/client/conference/members.html b/templates/client/conference/members.html new file mode 100644 index 00000000..42359fc6 --- /dev/null +++ b/templates/client/conference/members.html @@ -0,0 +1,25 @@ +{% extends 'base_catalog.html' %} +{% load i18n %} + +{% block bread_scrumbs %} + +{% endblock %} + + +{% block page_title %} +{% endblock %} + +{% block content_list %} + {% include 'includes/exposition/members.html' with object_list=object_list %} +{% endblock %} + +{% block paginator %} + {% include 'includes/catalog_paginator.html' with page_obj=page_obj %} +{% endblock %} \ No newline at end of file diff --git a/templates/client/conference/visitors.html b/templates/client/conference/visitors.html new file mode 100644 index 00000000..1050e608 --- /dev/null +++ b/templates/client/conference/visitors.html @@ -0,0 +1,26 @@ +{% extends 'base_catalog.html' %} +{% load i18n %} + +{% block bread_scrumbs %} + +{% endblock %} + + +{% block page_title %} +{% endblock %} + +{% block content_list %} + + {% include 'includes/exposition/visitors.html' with object_list=object_list %} +{% endblock %} + +{% block paginator %} + {% include 'includes/catalog_paginator.html' with page_obj=page_obj %} +{% endblock %} \ No newline at end of file diff --git a/templates/client/includes/article/article_logo.html b/templates/client/includes/article/article_logo.html index 13c76e5a..06faa278 100644 --- a/templates/client/includes/article/article_logo.html +++ b/templates/client/includes/article/article_logo.html @@ -2,7 +2,7 @@ {% load thumbnail %} {% if obj.logo %} - {% thumbnail obj.logo "100x100" as im %} + {% thumbnail obj.logo "100x100" format="PNG" as im %} {% endthumbnail %} {% else %} diff --git a/templates/client/includes/article/article_on_main_preview.html b/templates/client/includes/article/article_on_main_preview.html new file mode 100644 index 00000000..38fbc781 --- /dev/null +++ b/templates/client/includes/article/article_on_main_preview.html @@ -0,0 +1,11 @@ +{% load static %} +{% load thumbnail %} + + +{% if obj.get_blog_preview %} + {% thumbnail obj.get_blog_preview.file_path "220x100" as im %} + + {% endthumbnail %} +{% else %} + +{% endif %} \ No newline at end of file diff --git a/templates/client/includes/article/article_preview.html b/templates/client/includes/article/article_preview.html index 74637b04..3d005ac0 100644 --- a/templates/client/includes/article/article_preview.html +++ b/templates/client/includes/article/article_preview.html @@ -1,8 +1,8 @@ {% load static %} {% load thumbnail %} -{% if obj.logo %} - {% thumbnail obj.logo "250x180" as im %} +{% if obj.get_blog_preview %} + {% thumbnail obj.get_blog_preview.file_path "250x180" as im %} {% endthumbnail %} {% else %} diff --git a/templates/client/includes/booking_block.html b/templates/client/includes/booking_block.html index fa318eaf..8de59a1a 100644 --- a/templates/client/includes/booking_block.html +++ b/templates/client/includes/booking_block.html @@ -42,7 +42,8 @@ {{ hotel.name }} -
    6 500 руб./ночь
    +
     
    + Забронировать
diff --git a/templates/client/includes/calendar_button.html b/templates/client/includes/calendar_button.html index ef495503..aa0534c6 100644 --- a/templates/client/includes/calendar_button.html +++ b/templates/client/includes/calendar_button.html @@ -1,18 +1,5 @@ {% load i18n %} {% load template_filters %} -{% comment %} -{% if user.is_authenticated %} - {% if event|in_calendar:user %} - - {% trans 'из расписания' %} - {% else %} - {% trans 'в расписание' %} - - {% endif %} -{% else %} - {% trans 'в расписание' %} - -{% endif %} -{% endcomment %} -{% if obj|in_calendar:request.user %}{% trans 'Из расписания' %}{% else %}{% trans 'В расписание' %}{% endif %} + +{% if obj|in_calendar:request.user %}{% trans 'Из расписания' %}{% else %}{% trans 'В расписание' %}{% endif %} diff --git a/templates/client/includes/conference/conference_list.html b/templates/client/includes/conference/conference_list.html new file mode 100644 index 00000000..fc6388bd --- /dev/null +++ b/templates/client/includes/conference/conference_list.html @@ -0,0 +1,129 @@ +{% load static %} +{% load i18n %} +{% load template_filters %} + + +{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/client/includes/conference/conference_object.html b/templates/client/includes/conference/conference_object.html new file mode 100644 index 00000000..27a9e0cb --- /dev/null +++ b/templates/client/includes/conference/conference_object.html @@ -0,0 +1,396 @@ +{% load static %} +{% load i18n %} +{% load thumbnail %} +{% load template_filters %} + +{% block page_body %} +
+
+ + +
+
+
+ {% if event.main_title %} + {{ event.main_title|safe }} {{ event.name|safe }} + {% else %} + {{ event.name|safe }} + {% endif %} +
+
+ +
+ {% with obj=event %} + {% include 'client/includes/show_date_block.html' %} + {% endwith %} +
+ {% if event.place %} +
+
+
+ {{ event.place.adress }} +
+ +
+ + +
+ {% endif %} +
+
+
+ {% with event=event user=user %} + {% include 'client/includes/visit_button.html' %} + {% endwith %} + {% include 'client/includes/calendar_button.html' with obj=object %} +
+ {% with note=object|note_by_user:request.user %} + {% trans 'заметка' %} +
+
+ +
+
+ {% endwith %} +
+ + {% if request.user.is_admin %} + {% trans 'изменить' %} + {% endif %} + +
+ + + +
+
+
+
+ +
+ +
+ {% with tags=event.tag.all %} + {% for tag in tags %} + {{ tag.name }}{% if forloop.counter != tags|length %},{% endif %} + {% endfor %} + {% endwith %} + +
+
+
+
+ {% include 'client/includes/exposition/exposition_services.html' %} + + {% include 'includes/event_steps.html' with event=event %} + + {% if event.get_photos %} + {% with photos=event.get_photos|slice:"5" %} +
+ +
+ + +
+ {% endwith %} + {% endif %} + {% if event.description %} +
+ +
{% trans 'О выставке' %} {{ event.name|safe }}
+
{{ event.description|safe|linebreaks }}
+
+
+ {% endif %} +
+
{% trans 'Дополнительная информация' %}
+ +
+ {% if event.organiser.all|length > 0 %} +
{% trans 'Организатор' %}:
+
+ {% with organisers=event.organiser.all %} + {% for organiser in organisers %} + {{ organiser.name }}
+ {% endfor %} + {% endwith %} +
+ {% endif %} + {% if event.web_page %} +
{% trans 'Веб-сайт' %}:
+
+ {{ event.web_page }} +
+ {% endif %} + + {% if event.get_audience %} +
{% trans 'Аудитория' %}:
+
+ {{ event.get_audience }} +
+ {% endif %} + + {% if event.get_periodic %} +
{% trans 'Периодичность' %}:
+
{{ event.get_periodic }}
+ {% endif %} + + + {% if event.time %} +
{% trans 'Время работы' %}:
+
{{ event.time|safe }}
+ {% endif %} + +
+ +
+
+
+
+ {% with companies=event.company.all|slice:":6" %} + {% if companies %} + {# есть участники #} +
+
{% trans 'Участники' %}
+ + {% trans 'Все участники' %} +
+ + {% else %} + {# нет участников #} +
+
{% trans 'Участники' %}
+

{% trans 'Привлекайте целевых посетителей на стенд' %}

+ +

Рекламировать участника

+
+ {% endif %} + {% endwith %} +
+ +
+ {% with visitors=event.users.all|slice:":17" %} +
+
{% trans 'Посетители' %}
+
+ + {% trans 'Все посетители' %} + {% endwith %} + +

{% trans 'Пока никто не отметился на событии.' %}

+ + + {% with event=event user=user %} + {% include 'client/includes/visit_button.html' %} + {% endwith %} +
+
+ + +
+ + {% if event.members or event.visitors or event.foundation_year %} +
+ +
+ + {% if event.members %} +
{{ event.members }} {% trans 'участников' %}
+ {% endif %} + {% if event.visitors %} +
{{ event.visitors }} {% trans 'посетителей' %}
+ {% endif %} + {% if event.foundation_year %} +
{% trans 'Основано в' %} {{ event.foundation_year }} {% trans 'году' %}
+ {% endif %} +
+
+ {% endif %} + + + +
+ + {% include 'client/includes/booking_block.html' with city=event.city place=event.place %} + +
+ {% if event.get_nearest_events|slice:":6" %} +
+
{% trans 'Ближайшие выставки по тематике' %} «{{ event.theme.all.0 }}»
+ +
+ {% endif %} + + + +{% endblock %} +{% block content_text %} +{% endblock %} + +{% block popup %} + {% include 'client/popups/advertise_member.html' with form=advertising_form %} +{% endblock %} +{% block scripts %} + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/client/includes/exposition/catalog_filter_period.html b/templates/client/includes/exposition/catalog_filter_period.html index 5eb0825f..daea8cca 100644 --- a/templates/client/includes/exposition/catalog_filter_period.html +++ b/templates/client/includes/exposition/catalog_filter_period.html @@ -8,7 +8,7 @@ {% get_date %}
-
Период:
+
{% trans 'Период' %}:
{% with path=request.path|without_page now=now %} {% for month in monthes_period %} {{ month.name|lower }} {{ month.date|date:'Y' }}{% if month != monthes_period|last %},{% endif %} @@ -17,7 +17,7 @@
{% endif %} diff --git a/templates/client/includes/exposition/exposition_list.html b/templates/client/includes/exposition/exposition_list.html index 10e8ba60..935259e1 100644 --- a/templates/client/includes/exposition/exposition_list.html +++ b/templates/client/includes/exposition/exposition_list.html @@ -53,10 +53,10 @@ {% if obj.country %} {% endif %} diff --git a/templates/client/includes/exposition/exposition_object.html b/templates/client/includes/exposition/exposition_object.html index d7b77f96..a2962adc 100644 --- a/templates/client/includes/exposition/exposition_object.html +++ b/templates/client/includes/exposition/exposition_object.html @@ -110,7 +110,7 @@
{% with tags=exposition.tag.all %} {% for tag in tags %} - {{ tag.name }}, + {{ tag.name }}{% if forloop.counter != tags|length %},{% endif %} {% endfor %} {% endwith %} @@ -323,7 +323,7 @@
{% if exposition.get_nearest_events|slice:":6" %}
-
{% trans 'Ближайшие выставки по тематике' %} «{{ exposition.theme.all.0 }}»
+
{% trans 'Ближайшие выставки по тематике' %} «{{ exposition.theme.all.0 }}»
\ No newline at end of file diff --git a/templates/client/index.html b/templates/client/index.html index 01eba2e7..f281db1f 100644 --- a/templates/client/index.html +++ b/templates/client/index.html @@ -168,23 +168,7 @@
- + {% include 'client/includes/index/blogs.html' with blogs=blogs %}
diff --git a/theme/models.py b/theme/models.py index 414ebafe..cbcc0742 100644 --- a/theme/models.py +++ b/theme/models.py @@ -77,18 +77,19 @@ class Theme(TranslatableModel): def expositions_number(self): - return len(Exposition.objects.filter(theme__id=self.id)) + return Exposition.objects.filter(theme__id=self.id).count() + def conferences_number(self): - return len(Conference.objects.filter(theme__id=self.id)) + return Conference.objects.filter(theme__id=self.id).count() def seminars_number(self): - return len(Seminar.objects.filter(theme__id=self.id)) + return Seminar.objects.filter(theme__id=self.id).count() def webinars_number(self): - return len(Webinar.objects.filter(theme__id=self.id)) + return Webinar.objects.filter(theme__id=self.id).count() def events_number(self): return self.expositions_number() + self.conferences_number() + self.seminars_number() + self.webinars_number()