You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
19 KiB
373 lines
19 KiB
# -*- coding: utf-8 -*-
|
|
import copy
|
|
import datetime
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from bitfield import BitField
|
|
from django.conf import settings
|
|
from django.contrib.contenttypes import generic
|
|
from django.core.urlresolvers import reverse_lazy
|
|
from django.db import models
|
|
from django.db.models import Q
|
|
from django.db.models.signals import post_save, pre_save
|
|
from django.utils import translation
|
|
from django.utils.translation import ugettext as _
|
|
from functions.custom_fields import EnumField
|
|
from functions.db import db_table_exists
|
|
from functions.model_mixin import EventMixin, ExpoMixin
|
|
from functions.models_methods import ExpoManager, hvad_to_dict
|
|
from functions.signal_handlers import post_save_handler, pre_save_handler
|
|
from hvad.models import TranslatableModel, TranslatedFields, TranslationManager
|
|
from import_xls.model_utils import ExpoImportManager
|
|
from manager import ClientManager
|
|
from organiser.models import Organiser
|
|
from service.models import Service
|
|
from events.common import MEMBERS, VISITORS, PRICE, PRICE_EUR
|
|
|
|
|
|
AUDIENCE1 = ((None,_(u'Не выбрано')),
|
|
('experts', _(u'Специалисты')),
|
|
('experts and consumers', _(u'Специалисты и потребители')),
|
|
('general public', _(u'Широкая публика'))
|
|
)
|
|
AUDIENCE = (None,'experts', 'experts and consumers', 'general public')
|
|
|
|
|
|
BIT_AUDIENCE = settings.BIT_AUDIENCE
|
|
|
|
|
|
CURRENCY = settings.CURRENCY
|
|
|
|
# check if table exist and create flags if true
|
|
#flags = [item.url for item in Service.objects.all()] if db_table_exists('service_service') else []
|
|
flags = ['catalog', 'translator', 'participation', 'remote', 'tickets', 'visit', 'buildstand']
|
|
|
|
|
|
class Exposition(TranslatableModel, EventMixin, ExpoMixin):
|
|
"""
|
|
Create Exposition model
|
|
|
|
Uses hvad.TranslatableModel which is child of django.db.models class
|
|
"""
|
|
|
|
catalog = '/expo/'
|
|
catalog_name = _(u'Выставки:')
|
|
search_name = None
|
|
# type of event
|
|
event_type = 'expo'
|
|
|
|
url = models.SlugField(unique=True, max_length=255)
|
|
bad_url = models.BooleanField(verbose_name=_(u'Подозрение на неправильный урл'), default=False)
|
|
old_url = models.SlugField(unique=True, max_length=255)
|
|
data_begin = models.DateField(verbose_name=_(u'Дата начала'), db_index=True)
|
|
data_end = models.DateField(verbose_name=_(u'Дата окончания'))
|
|
services = BitField(flags=flags)
|
|
# relations
|
|
creator = models.ForeignKey('accounts.User', verbose_name=_(u'Создатель'), on_delete=models.SET_NULL,
|
|
related_name='exposition_creator', blank=True, null=True)
|
|
country = models.ForeignKey('country.Country', verbose_name=_(u'Страна'), on_delete=models.PROTECT,
|
|
related_name='exposition_country')
|
|
city = models.ForeignKey('city.City', verbose_name=_(u'Город'), on_delete=models.PROTECT,
|
|
related_name='exposition_city')
|
|
place = models.ForeignKey('place_exposition.PlaceExposition', verbose_name=_(u'Место проведения'),
|
|
blank=True, null=True, on_delete=models.PROTECT, related_name='exposition_place')
|
|
theme = models.ManyToManyField('theme.Theme', verbose_name=_(u'Тематики'),
|
|
related_name='exposition_themes')
|
|
tag = models.ManyToManyField('theme.Tag', verbose_name=_(u'Теги'),
|
|
blank=True, null=True, related_name='exposition_tags')
|
|
organiser = models.ManyToManyField('organiser.Organiser', verbose_name=_(u'Организатор'),
|
|
blank=True, null=True, related_name='exposition_organisers')
|
|
org = models.CharField(max_length=255, blank=True, null=True)
|
|
place_alt = models.CharField(max_length=255, blank=True, null=True)
|
|
company = models.ManyToManyField('company.Company', verbose_name=_(u'Компании'),
|
|
blank=True, null=True, related_name='exposition_companies')
|
|
users = models.ManyToManyField('accounts.User', verbose_name=_(u'Посетители выставки'),
|
|
blank=True, null=True, related_name='exposition_users')
|
|
photogallery = models.ForeignKey('photologue.Gallery', blank=True, null=True, on_delete=models.SET_NULL)
|
|
logo = models.ImageField(verbose_name='Logo', upload_to='exposition/logo/', blank=True)
|
|
rating = models.IntegerField(default=0) # добавить индекс в базе
|
|
|
|
quality_label = BitField(flags=['ufi', 'rsva', 'exporating'])
|
|
visitors = models.PositiveIntegerField(verbose_name=_(u'Посетители'), blank=True, null=True)
|
|
members = models.PositiveIntegerField(verbose_name=_(u'Участники'), blank=True, null=True)
|
|
discount = models.PositiveIntegerField(verbose_name=_(u'Скидка'), blank=True, null=True)
|
|
expohit = models.BooleanField(verbose_name=_(u'Expohit'), default=0)
|
|
# administrator can cancel exposition
|
|
canceled_by_administrator = models.BooleanField(default=0)
|
|
#can publish not immediately
|
|
is_published = models.BooleanField(default=0, db_index=True)
|
|
canceled = models.BooleanField(default=0)
|
|
moved = models.BooleanField(default=0)
|
|
|
|
# field saves information about creating and changing model
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
modified = models.DateTimeField(auto_now=True)
|
|
main_page = models.PositiveIntegerField(default=0, db_index=True)
|
|
|
|
views = models.PositiveIntegerField(default=0)
|
|
|
|
translations = TranslatedFields(
|
|
name = models.CharField(verbose_name=_(u'Название'), max_length=255),
|
|
main_title = models.TextField(verbose_name=_(u'Краткое описание'), blank=True),
|
|
description = models.TextField(verbose_name=_(u'Описание'), blank=True),
|
|
products = models.TextField(verbose_name=_(u'Экспонируемые продукты'), blank=True),
|
|
discount_description = models.TextField(verbose_name=_(u'Описание скидки'), blank=True),
|
|
time = models.TextField(verbose_name=_(u'Время работы'), blank=True),
|
|
# visit and particaption data
|
|
price_day = models.CharField(verbose_name=_(u'Стоимость билета 1 день'), max_length=255, blank=True),
|
|
price_all = models.CharField(verbose_name=_(u'Стоимость билета все дни'), max_length=255, blank=True),
|
|
price_day_bar = models.CharField(verbose_name=_(u'Стоимость на стойке 1 день'), max_length=255, blank=True),
|
|
price_all_bar = models.CharField(verbose_name=_(u'Стоимость на стойке все дни'), max_length=255, blank=True),
|
|
stat_countries = models.TextField(verbose_name=_(u'Участвующие страны'), blank=True),
|
|
pre_condition = models.CharField(verbose_name=_(u'Условия предварительной регистрации'), max_length=255, blank=True),
|
|
stand_condition = models.CharField(verbose_name=_(u'Условия регистрации на стойке'), max_length=255, blank=True),
|
|
visit_note = models.CharField(verbose_name=_(u'Примечание по посещению'), max_length=255, blank=True),
|
|
participation_note = models.TextField(verbose_name=_(u'Примечание по участии'), blank=True),
|
|
|
|
|
|
#-----meta data
|
|
title = models.CharField(max_length=250),
|
|
descriptions = models.CharField(max_length=250),
|
|
keywords = models.CharField(max_length=250),
|
|
)
|
|
|
|
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')
|
|
|
|
#about
|
|
periodic = models.FloatField(verbose_name=_(u'Переодичность'), blank=True, null=True)
|
|
audience = BitField(flags=[k for k, v in BIT_AUDIENCE])
|
|
web_page = models.CharField(verbose_name=_(u'Вебсайт'), max_length=255, blank=True)
|
|
foundation_year = models.PositiveIntegerField(verbose_name=_(u'Год основания'), blank=True, null=True)
|
|
area = models.PositiveIntegerField(verbose_name=_(u'Площадь'), blank=True, null=True)
|
|
# conditions of Participation
|
|
registration_link = models.URLField(verbose_name=_(u'Ссылка на регистрацию'), max_length=255, blank=True)
|
|
min_area = models.PositiveIntegerField(verbose_name=_(u'Минимальная площадь'), blank=True, null=True)
|
|
currency = EnumField(values=CURRENCY, default='USD')
|
|
application_deadline = models.DateField(verbose_name=_(u'Срок подачи заявки'), null=True)
|
|
min_stand_size = models.PositiveIntegerField(verbose_name=_(u'Минимальный размер стэнда'), blank=True, null=True)
|
|
price_catalog = models.PositiveIntegerField(verbose_name=_(u'Стоимость каталога'), blank=True, null=True)
|
|
tax = models.BooleanField(verbose_name=_(u'Налог'), default=1)
|
|
min_closed_area = models.PositiveIntegerField(verbose_name=_(u'Минимальная цена закрытой НЕ оборудованной площади'),
|
|
blank=True, null=True)
|
|
max_closed_area = models.PositiveIntegerField(verbose_name=_(u'Максимальная цена закрытой НЕ оборудованной площади'),
|
|
blank=True, null=True)
|
|
min_closed_equipped_area = models.PositiveIntegerField(verbose_name=_(u'Минимальная цена закрытой оборудованной площади'),
|
|
blank=True, null=True)
|
|
max_closed_equipped_area = models.PositiveIntegerField(verbose_name=_(u'Максимальная цена закрытой оборудованной площади'),
|
|
blank=True, null=True)
|
|
min_open_area = models.PositiveIntegerField(verbose_name=_(u'Минимальная цена открытой площади'),
|
|
blank=True, null=True)
|
|
max_open_area = models.PositiveIntegerField(verbose_name=_(u'Максимальная цена открытой площади'),
|
|
blank=True, null=True)
|
|
registration_payment = models.PositiveIntegerField(verbose_name=_(u'Регистрационный взнос'), blank=True, null=True)
|
|
|
|
paid_new = models.ForeignKey('expobanner.Paid', blank=True, null=True, on_delete=models.SET_NULL)
|
|
top = models.ForeignKey('expobanner.Top', blank=True, null=True, on_delete=models.SET_NULL)
|
|
main = models.ForeignKey('expobanner.MainPage', blank=True, null=True, on_delete=models.SET_NULL)
|
|
|
|
# fields for filter easy
|
|
members_choice = models.PositiveSmallIntegerField(
|
|
_(u'Участники'), choices=MEMBERS, blank=True, null=True, db_index=True)
|
|
visitors_choice = models.PositiveSmallIntegerField(
|
|
_(u'Посетители'), choices=VISITORS, blank=True, null=True, db_index=True)
|
|
price_choice = models.PositiveSmallIntegerField(
|
|
_(u'Минимальная цена'), choices=PRICE, blank=True, null=True, db_index=True)
|
|
price_rub = models.PositiveIntegerField(verbose_name=_(u'Рублевая цена'), blank=True, null=True)
|
|
price_choice_eur = models.PositiveSmallIntegerField(
|
|
_(u'Минимальная цена'), choices=PRICE_EUR, blank=True, null=True, db_index=True)
|
|
price_eur = models.PositiveIntegerField(verbose_name=_(u'Цена в евро'), blank=True, null=True)
|
|
|
|
#set manager of this model(fisrt manager is default)
|
|
objects = ExpoManager()
|
|
enable = ClientManager()
|
|
imports = ExpoImportManager()
|
|
|
|
def __unicode__(self):
|
|
return self.lazy_translation_getter('name', unicode(self.pk))
|
|
|
|
@property
|
|
def get_price(self):
|
|
return self.price_day or self.price_all
|
|
|
|
def get_services(self):
|
|
return self.get_services_detail()
|
|
# country_ids = [item for item, bool in self.country.services if bool==True]
|
|
# ids = [item for item, bool in self.services if bool==True]
|
|
|
|
# qs = Service.objects.language().filter(Q(Q(url__in=country_ids) & Q(type=Service.type.expo)) | Q(url__in=ids))
|
|
|
|
# return list(qs)
|
|
|
|
def get_services_detail(self):
|
|
excluded = ['visit']
|
|
return super(Exposition, self).get_services_detail(excluded, Service.type.expo)
|
|
|
|
def get_parent(self):
|
|
return {}
|
|
|
|
def get_absolute_url(self):
|
|
return self.get_permanent_url()
|
|
|
|
def get_news_url(self):
|
|
return reverse_lazy('news_expo', kwargs={'slug': self.url})
|
|
|
|
def get_index_text(self):
|
|
translation.activate('ru')
|
|
translations = self.translations.all()
|
|
names = ' '.join([tr.name for tr in translations])
|
|
titles = ' '.join([tr.main_title for tr in translations])
|
|
themes = ' '.join([' '.join(theme.get_all_names()) for theme in self.theme.all()])
|
|
tags = ' '.join([' '.join(tag.get_all_names()) for tag in self.tag.all()])
|
|
return names + ' ' + titles + ' ' + themes + ' ' + tags
|
|
|
|
def get_note_by_user(self, user_id):
|
|
note = self.note.filter(user__id=user_id)
|
|
try:
|
|
return note.get().text
|
|
except:
|
|
return ''
|
|
|
|
def upload_photo_url(self):
|
|
return '/admin/exposition/upload-photo/%s/' % self.id
|
|
|
|
def tags(self):
|
|
return self.tag.language().all()
|
|
|
|
def themes(self):
|
|
return self.theme.language().all()
|
|
|
|
def statistic_exists(self):
|
|
return Statistic.objects.filter(exposition=self).exists()
|
|
|
|
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(unicode(v))
|
|
|
|
return ', '.join(audience)
|
|
|
|
def get_periodic(self):
|
|
periodic = {0: '', 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 года'), 0.2: _(u'Раз в 5 лет')}
|
|
return periodic.get(self.periodic)
|
|
|
|
def get_catalog_url(self):
|
|
return self.catalog
|
|
|
|
def get_event_type(self):
|
|
return _(u'Выставки')
|
|
|
|
def get_calendar_url(self):
|
|
return '/exposition-add-calendar/%s/'%self.id
|
|
|
|
def get_visit_url(self):
|
|
return '/exposition-visit/%s/'%self.id
|
|
|
|
def get_note_url(self):
|
|
return '/expo/add-note/%s/'%self.url
|
|
|
|
def get_timetables_days(self):
|
|
tables = list(self.business_program.all())
|
|
days = []
|
|
for t in tables:
|
|
d = t.begin.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
|
|
if not d in days:
|
|
days.append(d)
|
|
|
|
return days
|
|
|
|
def get_currency_html(self):
|
|
cur = self.currency
|
|
currency_codes = {'EUR':'€', 'USD':'$', 'RUB':'руб.'}
|
|
code = currency_codes.get(cur)
|
|
if code:
|
|
return code
|
|
return cur
|
|
|
|
def theme_ids(self):
|
|
return [item['id'] for item in self.theme.all().values('id')]
|
|
|
|
def clicks(self):
|
|
return self.paid_new.tickets.clicks() + self.paid_new.participation.clicks() + self.paid_new.official.clicks()
|
|
|
|
def get_paid_change_url(self):
|
|
return reverse_lazy('expobanner-update_paid', kwargs={'pk': self.paid_new_id})
|
|
|
|
def get_objectstat_views(self):
|
|
return sum(self.objectstats_set.all().values_list('value', flat=True))
|
|
|
|
|
|
class Statistic(TranslatableModel):
|
|
exposition = models.ForeignKey(Exposition, related_name='statistic')
|
|
year = models.PositiveIntegerField(verbose_name=_(u'Год'))
|
|
members = models.PositiveIntegerField(verbose_name=_(u'Посетители'), blank=True, null=True)
|
|
visitors = models.PositiveIntegerField(verbose_name=_(u'Участники'), blank=True, null=True)
|
|
area = models.PositiveIntegerField(verbose_name=_(u'Площадь'), blank=True, null=True)
|
|
countries_number = models.PositiveIntegerField(verbose_name=_(u'Количество стран'), blank=True, null=True)
|
|
|
|
translations = TranslatedFields(
|
|
countries = models.TextField(blank=True)
|
|
)
|
|
|
|
def to_dict(self):
|
|
return hvad_to_dict(self)
|
|
|
|
|
|
class TimeTable(TranslatableModel):
|
|
"""
|
|
TimeTable for business program
|
|
|
|
"""
|
|
exposition = models.ForeignKey(Exposition, related_name='business_program')
|
|
begin = models.DateTimeField(verbose_name=_(u'Начало'))
|
|
end = models.DateTimeField(verbose_name=_(u'Конец'))
|
|
timetable_organiser = models.ForeignKey(Organiser, null=True, blank=True)
|
|
#
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
modified = models.DateTimeField(auto_now=True)
|
|
#translated fields
|
|
translations = TranslatedFields(
|
|
name = models.CharField(verbose_name=_(u'Название программы'), max_length=255, blank=True),
|
|
programe = models.TextField(verbose_name=_(u'Программа'), blank=True),
|
|
speaker = models.CharField(verbose_name=_(u'Организатор'), max_length=255, blank=True),
|
|
place = models.CharField(verbose_name=_(u'Место проведения'), max_length=255, blank=True)
|
|
)
|
|
|
|
def to_dict(self):
|
|
#obj = serializers.serialize('json', [self,])
|
|
return hvad_to_dict(self)
|
|
|
|
class TmpTimeTable(TranslatableModel):
|
|
exposition = models.ForeignKey(Exposition, null=True, blank=True)
|
|
begin = models.DateTimeField(verbose_name=_(u'Начало'))
|
|
end = models.DateTimeField(verbose_name=_(u'Конец'))
|
|
timetable_organiser = models.ForeignKey(Organiser, null=True, blank=True)
|
|
# key uses for checking keys from new objects.
|
|
key = models.CharField(max_length=255, blank=True)
|
|
#
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
modified = models.DateTimeField(auto_now=True)
|
|
#translated fields
|
|
translations = TranslatedFields(
|
|
name = models.CharField(verbose_name=_(u'Название программы'), max_length=255, blank=True),
|
|
programe = models.TextField(verbose_name=_(u'Программа')),
|
|
speaker = models.CharField(verbose_name=_(u'Спикеры'), max_length=255, blank=True),
|
|
place = models.CharField(verbose_name=_(u'Место проведения'), max_length=255, blank=True)
|
|
)
|
|
|
|
|
|
def logo_name(instance, filename):
|
|
url = instance.expo.url
|
|
return '/'.join(['exposition', url, url+'_org_logo.jpg'])
|
|
|
|
|
|
pre_save.connect(pre_save_handler, sender=Exposition)
|
|
post_save.connect(post_save_handler, sender=Exposition)
|
|
post_save.connect(post_save_handler, sender=TimeTable)
|
|
post_save.connect(post_save_handler, sender=TmpTimeTable)
|
|
post_save.connect(post_save_handler, sender=Statistic)
|
|
|