diff --git a/accounts/admin.py b/accounts/admin.py index 50504df9..1ebe3a35 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -1,29 +1,25 @@ # -*- coding: utf-8 -*- +import random +import json +from hashlib import md5 from django.shortcuts import render_to_response +from django.conf import settings from django.http import HttpResponseRedirect, HttpResponse -from django.template import RequestContext from django.core.context_processors import csrf from django.contrib.auth.decorators import login_required -import random from django.utils.translation import ugettext as _ #models and forms from models import User -from forms import UserForm, UserCreationForm, ChangePasswordForm, EmailAnnouncementForm +from forms import UserForm, UserCreationForm, ChangePasswordForm, EmailAnnouncementForm, UserFilterForm #custom views -from functions.custom_views import objects_list - -from hashlib import md5 -import json - +from functions.admin_views import AdminView, AdminListView +class UserListView(AdminListView): + template_name = 'admin/accounts/user_list.html' + form_class = UserFilterForm + model = User -def user_all(request): - """ - Return list of all users with pagination - """ - return objects_list(request, User, 'user_all.html') - def user_change(request, url): """ Return form of user and post it on the server. @@ -38,12 +34,30 @@ def user_change(request, url): return HttpResponseRedirect('/admin/accounts/all') if request.POST: + # bug with saving staff users(set is_staff to False) + staff = user.is_staff + form = UserForm(request.POST, instance=user) if form.is_valid(): - form.save() + + user = form.save() + if staff: + # + user.is_staff = True + user.save() return HttpResponseRedirect('/admin/accounts/all') + else: + form.fields['city'].widget.attrs['data-init-text'] = user.profile.city.name + else: - form = UserForm(instance=user) + profile = user.profile + data = {'country':profile.country_id, 'city': profile.city_id, + 'title': profile.title, 'descriptions': profile.descriptions, + 'keywords': profile.keywords, 'phone': profile.phone, 'web_page': profile.web_page, + 'about': profile.about} + + form = UserForm(instance=user,initial=data) + if user.profile.city: form.fields['city'].widget.attrs['data-init-text'] = user.profile.city.name @@ -51,6 +65,7 @@ def user_change(request, url): args.update(csrf(request)) args['form'] = form + args['object'] = user return render_to_response('user_change.html', args) @@ -95,26 +110,6 @@ def create_md5(request): return render_to_response('create_admin.html', args) -from django.core.mail import EmailMessage - - -def registration(request): - if request.POST: - form = UserCreationForm(request.POST) - if form.is_valid(): - user = form.save() - email = EmailMessage('Subject', 'Body', to=['%s'%user.email]) - email.send() - return HttpResponseRedirect('/admin/accounts/registration') - else: - form = UserCreationForm() - - args = {} - args.update(csrf(request)) - - args['form'] = form - - return render_to_response('registration.html', args) def generatePassword(): """ @@ -131,7 +126,7 @@ def generatePassword(): newPassword.append(SYMBOLS[random.randrange(0, len(SYMBOLS))]) return ''.join(newPassword) -from django.conf import settings + def reset_password_email(request): """ @@ -174,4 +169,4 @@ def change_password(request): success.update(errors) return HttpResponse(json.dumps(success), content_type='application/json') else: - return HttpResponse(json.dumps(success), content_type='application/json') + return HttpResponse(json.dumps(success), content_type='application/json') \ No newline at end of file diff --git a/accounts/admin_urls.py b/accounts/admin_urls.py index f1d8a39c..e4e99efc 100644 --- a/accounts/admin_urls.py +++ b/accounts/admin_urls.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, url +from admin import UserListView urlpatterns = patterns('', #url(r'^registration/$', 'accounts.admin.registration'), - #url(r'^create_admin/$', 'accounts.admin.create_admin'), + #url(r'^create_admin/$', 'accounts.admin.create_admin'), #url(r'^create_md5user/$', 'accounts.admin.create_md5'), url(r'^change/(.*)/$', 'accounts.admin.user_change'), -# url(r'^change/(?P\d+).*/$', 'accounts.views.user_change'), - url(r'^all/$', 'accounts.admin.user_all'), + url(r'^all/$', UserListView.as_view()), url(r'^reset_password_email/$', 'accounts.admin.reset_password_email'), ) \ No newline at end of file diff --git a/accounts/edit_forms.py b/accounts/edit_forms.py index 7635e9f2..ee602621 100644 --- a/accounts/edit_forms.py +++ b/accounts/edit_forms.py @@ -59,7 +59,7 @@ class HomeForm(forms.ModelForm): class WorkForm(forms.ModelForm): position = forms.CharField(label=_(u'Укажите вашу должность'), - required=False, widget=forms.TextInput(attrs={'style': 'height: 42px;'})) + required=False, widget=forms.TextInput()) company = forms.CharField(label=_(u'Место работы'), widget=forms.HiddenInput(attrs={'class': 'select2'})) def __init__(self, *args, **kwargs): diff --git a/accounts/forms.py b/accounts/forms.py index f00557f8..5e08cb92 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -11,6 +11,18 @@ from organiser.models import Organiser #functions from functions.form_check import translit_with_separator, is_latin + +def clean_relation_field(inst, field_name, model): + id = inst.cleaned_data[field_name] + if id: + try: + return model.objects.get(id=id) + except model.DoesNotExist: + return None + return None + + + class UserCreationForm(forms.ModelForm): password1 = forms.CharField(label='Пароль', widget=forms.PasswordInput(render_value=False)) password2 = forms.CharField(label='Повторите пароль', widget=forms.PasswordInput(render_value=False)) @@ -19,6 +31,7 @@ class UserCreationForm(forms.ModelForm): model = User fields = ('email', 'first_name', 'last_name') + def clean_email(self): """ checking if user already exist @@ -57,22 +70,46 @@ class UserChangeForm(forms.ModelForm): def clean_password(self): return self.initial['password'] + + class UserForm(forms.ModelForm): - email = forms.EmailField(widget=forms.TextInput(attrs={'disabled' : True}), required=False) - country = forms.ModelChoiceField(label='Страна', queryset=Country.objects.all(), empty_label=None, required=False) + #email = forms.EmailField(widget=forms.TextInput(attrs={'disabled' : True}), required=False) + country = forms.ChoiceField(label='Страна', choices=[(item.id, item.name) for item in Country.objects.all()], required=False) city = forms.CharField(label='Город', widget=forms.HiddenInput()) - company = forms.ModelChoiceField(label='Компания', queryset=Company.objects.all(), empty_label='', required=False) - organiser = forms.ModelChoiceField(label='Организатор', queryset=Organiser.objects.all(), empty_label='', required=False) + company = forms.ChoiceField(label='Компания', choices=[(item.id, item.name) for item in Company.objects.language().all()], required=False) + organiser = forms.ChoiceField(label='Организатор', choices=[(item.id, item.name) for item in Organiser.objects.language().all()], required=False) title = forms.CharField(widget=forms.TextInput(attrs={'style':'width: 550px'}), required=False) descriptions = forms.CharField(widget=forms.TextInput(attrs={'style':'width: 550px'}), required=False) keywords = forms.CharField(widget=forms.TextInput(attrs={'style':'width: 550px'}), required=False) + phone = forms.CharField(widget=forms.TextInput(attrs={'style':'width: 550px'}), required=False) + web_page = forms.URLField(required=False) + about = forms.CharField(widget=forms.Textarea()) class Meta: model = User - exclude = ('last_login', 'password', 'is_active', 'is_admin', 'is_superuser', 'is_staff' + exclude = ('username', 'email','last_login', 'password', 'is_active', 'is_admin', 'is_superuser', 'is_staff' 'date_joined', 'date_registered', 'date_modified') + def save(self, force_insert=False, force_update=False, commit=True): + + user = super(UserForm, self).save(commit=False) + data = self.cleaned_data + profile = user.profile + profile.country = data.get('country') + profile.city = data.get('city') + profile.title = data.get('title', '') + profile.descriptions = data.get('descriptions', '') + profile.keywords = data.get('keywords', '') + profile.phone = data.get('phone') + profile.web_page = data.get('web_page') + profile.about = data.get('about') + if commit: + user.save() + profile.save() + return user + + """ def clean_url(self): url = self.cleaned_data.get('url') try: @@ -82,6 +119,21 @@ class UserForm(forms.ModelForm): except: return url raise forms.ValidationError('Такой урл уже занят') + """ + + def clean_organiser(self): + return clean_relation_field(self, 'organiser', Organiser) + + + def clean_company(self): + return clean_relation_field(self, 'company', Company) + + def clean_country(self): + return clean_relation_field(self, 'country', Country) + + def clean_city(self): + return clean_relation_field(self, 'city', City) + def clean_phone(self): """ @@ -99,7 +151,7 @@ class UserForm(forms.ModelForm): return phone else: raise forms.ValidationError('Введите правильный код страны') - + """ def clean_web_page(self): cleaned_data = super(UserForm, self).clean() web_page = cleaned_data.get('web_page') @@ -112,6 +164,7 @@ class UserForm(forms.ModelForm): return web_page except: return forms.ValidationError('Введите правильный адрес страници') + """ class ChangePasswordForm(forms.Form): """ @@ -232,3 +285,21 @@ class RecoveryForm(forms.Form): +class UserFilterForm(forms.Form): + model = User + email = forms.CharField(label=_(u'Email'), required=False) + def filter(self): + """ + + return filtered queryset + form must be cleaned before calling this method + + """ + data = self.cleaned_data + email = data['email'] + model = self.model + qs = model.objects.all() + if email: + qs = qs.filter(email__contains=email) + + return qs \ No newline at end of file diff --git a/accounts/models.py b/accounts/models.py index f3b3481e..2eaad1bb 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- +import random, string from django.db import models -from hvad.models import TranslatableModel, TranslatedFields +from django.core.validators import email_re +from django.db.models import Q from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin from django.core.mail import send_mail from django.utils import timezone from django.utils.translation import ugettext as _ from django.db.models.signals import post_save +from django.db.models.loading import get_model #custom functions from functions.form_check import translit_with_separator -import random, string -from django.core.validators import email_re -from django.db.models import Q + + """ from django.contrib.auth.hashers import check_password @@ -96,9 +98,14 @@ class User(AbstractBaseUser, PermissionsMixin): Implementing a fully featured User model with admin-compliant permissions. + stored main data about users + Email, first name, last name and password are required. Other fields are optional. + """ + catalog = '/user/' + email = models.EmailField( verbose_name = u'Email', max_length = 255, @@ -167,29 +174,60 @@ class User(AbstractBaseUser, PermissionsMixin): return True def get_expositions_number(self): - return len(self.exposition_users.all()) + # 1 query + return self.exposition_users.all().count() def get_conferences_number(self): - return len(self.conference_users.all()) + # 1 query + return self.conference_users.all().count() def get_seminars_number(self): - return len(self.seminar_users.all()) + # 1 query + return self.seminar_users.all().count() def get_webinars_number(self): - return len(self.webinar_users.all()) + # 1 query + return self.webinar_users.all().count() def get_events_number(self): + # 4 query n = self.get_expositions_number() + self.get_conferences_number() + self.get_seminars_number() + self.get_webinars_number() return n def get_permanent_url(self): if self.url: - return '/user/'+self.url+'/' - return '/user/'+str(self.id)+'/' + + return self.catalog+self.url+'/' + return self.catalog+str(self.id)+'/' + + def get_expos(self): + """ + return information about expos and them related data by 1 query + + """ + return self.exposition_users.language().select_related('country', 'city', 'place').all() + + def get_confs(self): + """ + return information about confs and them related data by 1 query + + """ + return self.conference_users.language().select_related('country', 'city', 'place').all() + + def get_seminars(self): + """ + return information about seminars and them related data by 1 query + + """ + return self.seminar_users.language().select_related('country', 'city').all() class Profile(models.Model): + """ + stores additional information about users + + """ user = models.OneToOneField(User) country = models.ForeignKey('country.Country', verbose_name='Страна', blank=True, null=True, on_delete=models.PROTECT, related_name='users') @@ -213,19 +251,53 @@ class Profile(models.Model): keywords = models.CharField(max_length=255, blank=True) -from dateutil.relativedelta import relativedelta - class Calendar(models.Model): + """ + Store information about events, which user can add to own calendar + every user has one calendar + """ user = models.OneToOneField(User) expositions = models.ManyToManyField('exposition.Exposition', null=True) conferences = models.ManyToManyField('conference.Conference', null=True) seminars = models.ManyToManyField('seminar.Seminar', null=True) webinars = models.ManyToManyField('webinar.Webinar', null=True) + def get_expos(self): + # 1 query + return list(self.expositions.language().all()) + + def get_confs(self): + # 1 query + return list(self.conferences.language().all()) + + def get_seminars(self): + # 1 query + return list(self.seminars.language().all()) + + def get_webinars(self): + # 1 query + return list(self.webinars.language().all()) + + def get_events(self): - events = list(self.expositions.all()) + list(self.conferences.all()) + list(self.seminars.all()) + list(self.webinars.all()) + # 4 query + events = self.get_expos() + self.get_confs() + self.get_seminars() + self.get_webinars() return events + def check_in_calendar(self, event): + """ + check if event in calendar + 1 query + """ + event_type = event.event_type + if event_type == 'expo': + return event in self.get_expos() + elif event_type == 'conf': + return event in self.get_confs() + elif event_type == 'seminar': + return event in self.get_seminars() + elif event_type == 'webinar': + return event in self.get_webinars() def events_by_month(self, day): exp = list(self.expositions.filter((Q(data_begin__month=day.month) & Q(data_begin__year=day.year))\ @@ -240,12 +312,51 @@ class Calendar(models.Model): return exp+con+sem+web +class EventFilter(models.Model): + user = models.OneToOneField(User) + theme = models.ManyToManyField('theme.Theme', null=True) + tag = models.ManyToManyField('theme.Tag', null=True) + area = models.ManyToManyField('country.Area', null=True) + country = models.ManyToManyField('country.Country', null=True) + city = models.ManyToManyField('city.City', null=True) + fr = models.DateField(blank=True, null=True) + to = models.DateField(blank=True, null=True) + + def get_queryset(self): + Exposition = get_model('exposition', 'Exposition') + qs = Exposition.objects.all() + themes = self.theme.all() + tags = self.tag.all() + countries = self.country.all() + areas = self.area.all() + cities = self.city.all() + + if themes.count(): + qs = qs.filter(theme__in=list(themes)) + + if tags.count(): + qs = qs.filter(tag__in=list(tags)) + + if cities.count(): + qs = qs.filter(city__in=list(cities)) + + if areas.count(): + qs = qs.filter(country__area__in=list(areas)) + + if countries.count(): + qs = qs.filter(country__in=list(countries)) + + return qs.order_by('data_begin') + + + def create_user_inf(sender, instance, created, **kwargs): if created: Calendar.objects.create(user=instance) Profile.objects.create(user=instance) + EventFilter.objects.create(user=instance) post_save.connect(create_user_inf, sender=User) diff --git a/accounts/signals.py b/accounts/signals.py deleted file mode 100644 index 8b137891..00000000 --- a/accounts/signals.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/accounts/urls.py b/accounts/urls.py index 396c93c2..e7842ee5 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -2,8 +2,8 @@ from django.conf.urls import patterns, url from django.contrib.auth.decorators import login_required from views import SettingsView, ProfileView, CalendarView, UserView, UserExpositionsView, UserConferenceView, UserSeminarView -from views import NameView, HomeView, AvatarView, WorkView, AboutCompanyView, PhoneView, EmailView, WebPageView,\ - SocialView, AboutView, ProfileCompanyView +from views import NameView, HomeView, AvatarView, WorkView, AboutCompanyView, PhoneView, WebPageView,\ + SocialView, AboutView, ProfileCompanyView, Feed # @@ -16,6 +16,8 @@ urlpatterns = patterns('', url(r'^profile/company/$', login_required(ProfileCompanyView.as_view())), url(r'^profile/settings/$', login_required(SettingsView.as_view())), url(r'^profile/calendar/$', login_required(CalendarView.as_view())), + url(r'^profile/feed/page/(?P\d+)/$', Feed.as_view()), + url(r'^profile/feed/$', login_required(Feed.as_view())), url(r'^user/(?P.*)/expositions/(?P\d+)/$', UserExpositionsView.as_view()), url(r'^user/(?P.*)/expositions/$', UserExpositionsView.as_view()), url(r'^user/(?P.*)/seminars/(?P\d+)/$', UserSeminarView.as_view()), @@ -29,21 +31,16 @@ urlpatterns = patterns('', #url(r'^profile/messages/(?P.*)/$', login_required(MessagesView.as_view())), #url(r'^profile/messages/$', login_required(MessagesView.as_view())), - # + # ajax url(r'^profile/update/name/$', login_required(NameView.as_view())), url(r'^profile/update/home/$', login_required(HomeView.as_view())), url(r'^profile/update/avatar/$', login_required(AvatarView.as_view())), url(r'^profile/update/work/$', login_required(WorkView.as_view())), url(r'^profile/update/about-company/$', login_required(AboutCompanyView.as_view())), url(r'^profile/update/phone/$', login_required(PhoneView.as_view())), - url(r'^profile/update/email/$', login_required(EmailView.as_view())), url(r'^profile/update/web-page/$', login_required(WebPageView.as_view())), url(r'^profile/update/social/$', login_required(SocialView.as_view())), url(r'^profile/update/about/$', login_required(AboutView.as_view())), - url(r'^profile/change-password/', 'accounts.views.change_password'), - # - url(r'^accounts/get/calendar/(?P\d+)/(?P\d+)/$', 'accounts.views.get_calendar'), - url(r'^accounts/get-tag-users/$', 'accounts.views.user_for_tag'), ) diff --git a/accounts/views.py b/accounts/views.py index bc1ffa41..dddad811 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,23 +1,28 @@ # -*- coding: utf-8 -*- -from django.shortcuts import render_to_response, get_object_or_404 +import json, datetime +import calendar as python_calendar +from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.contrib.auth.decorators import login_required from django.utils.translation import ugettext as _, get_language from django.utils import timezone -# forms +from django_messages.forms import SendForm +from django.views.generic import TemplateView, FormView, ListView +from sorl.thumbnail import get_thumbnail from forms import ChangePasswordForm, EmailAnnouncementForm -from company.forms import CompanyFormClient, CreateCompanyForm +from company.forms import CreateCompanyForm +from models import User from edit_forms import AvatarForm, NameForm, HomeForm, WorkForm, AboutCompanyForm, PhoneForm, EmailForm,\ WebPageForm, SocialForm, AboutForm -from models import User -# python -import json, datetime -import calendar as python_calendar -from django.views.generic import TemplateView, FormView, ListView -from sorl.thumbnail import get_thumbnail + class SettingsView(TemplateView): + """ + display template with user settings like: + password, email notifications, social settings, subscription + + """ template_name = 'accounts/settings.html' def get_context_data(self, **kwargs): context = super(SettingsView, self).get_context_data(**kwargs) @@ -26,69 +31,64 @@ class SettingsView(TemplateView): return context class CalendarView(TemplateView): + """ + display template with user calendar(one month) + """ + template_name = 'accounts/calendar.html' def get_context_data(self, **kwargs): + """ + get events by 1 month and return to template + + return additional variables: + - events - events in current months + - days - days in current month + - current_day + + """ context = super(CalendarView, self).get_context_data(**kwargs) now = timezone.now().replace(microsecond=0, second=0, minute=0, hour=0) context['current_day'] = now year = self.request.GET.get('year') month = self.request.GET.get('month') - # cheack if its int if year: year = int(year) if month: month = int(month) if not year or not month: + # events in current months number_of_days = python_calendar.monthrange(now.year, now.month)[1] + # number of days in current month days = [timezone.make_aware(datetime.datetime(now.year, now.month, i+1), timezone.get_default_timezone()) for i in range(number_of_days)] - context['days'] = days - # -------------------- calendar = self.request.user.calendar + # events in current month context['events'] = calendar.events_by_month(now) else: number_of_days = python_calendar.monthrange(year, month)[1] days = [timezone.make_aware(datetime.datetime(year, month, i+1), timezone.get_default_timezone()) for i in range(number_of_days)] + # number of days in current month context['days'] = days calendar = self.request.user.calendar now = now.replace(year=year, month=month, day=1) + # events in current month context['events'] = calendar.events_by_month(now) return context -def events_handeler(obj): - response = {'name': obj.name, 'country': obj.country.name, 'city': obj.city.name} - if obj.place: - response['place'] = obj.place.name - return response - - -@login_required -def get_calendar(request, year, month): - now = timezone.now().replace(microsecond=0, second=0, minute=0, hour=0) - day = timezone.make_aware(datetime.datetime(year=int(year), month=int(month), day=1), timezone.get_default_timezone()) - calendar = request.user.calendar - - number_of_days = python_calendar.monthrange(day.year, day.month)[1] - days = [timezone.make_aware(datetime.datetime(day.year, day.month, i+1), timezone.get_default_timezone()) for i in range(number_of_days)] - args = {} - dthandler = lambda obj: (obj.isoformat() if isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date) else None) - - args['days'] = json.dumps(days, default=dthandler) - args['current_day'] = json.dumps(now, default=dthandler) - events = calendar.events_by_month(day) - - args['events'] = json.dumps(events, default=events_handeler) - - return HttpResponse(json.dumps(args), content_type='application/json') class ProfileView(TemplateView): + """ + display template with user information forms + + in template forms handles dynamically by ajax + """ template_name = 'accounts/new_profile.html' def get_context_data(self, **kwargs): @@ -116,6 +116,10 @@ from company.edit_forms import NameForm as CompNameForm, HomeForm as CompHomeFor FoundationForm as CompFound, SpecializationForm as CompSpec, AddressForm as CompAddress class ProfileCompanyView(TemplateView): + """ + display template with user company information forms + in template forms handles dynamically by ajax + """ template_name = 'accounts/fill_company.html' def get_context_data(self, **kwargs): @@ -147,11 +151,12 @@ class ProfileCompanyView(TemplateView): -class MessagesView(TemplateView): - template_name = 'accounts/messages.html' -from django_messages.forms import SendForm + class UserView(TemplateView): + """ + display user information for another users + """ template_name = 'accounts/user.html' def get_context_data(self, **kwargs): @@ -168,45 +173,9 @@ class UserView(TemplateView): return context -@login_required -def change_password(request): - """ - Change current user password if new password is valid - """ - success = {'success': False} - if request.POST: - form = ChangePasswordForm(request.POST) - if form.is_valid(): - user = request.user - if(user.check_password(form.cleaned_data.get('old_password'))): - user.set_password(form.cleaned_data.get('new_password')) - user.save() - success['success'] = True - success['message'] = _(u'Пароль изменен') - return HttpResponse(json.dumps(success), content_type='application/json') - else: - errors = {'errors': [_(u'Не правильный пароль')]} - success.update(errors) - return HttpResponse(json.dumps(success), content_type='application/json') - else: - errors = [err[0] for err in form.errors.values()] - errors = {'errors': errors} - success.update(errors) - return HttpResponse(json.dumps(success), content_type='application/json') - else: - return HttpResponse(json.dumps(success), content_type='application/json') - - - - -def user_for_tag(request): - - users = [{'id': user.id, 'label': user.get_full_name(), 'value': user.get_full_name() } for user in User.objects.all()] - return HttpResponse(json.dumps(users), content_type='application/json') - class ProfileInvalidView(FormView): """ - abstract view + abstract view. handles form errors. return errors in json """ def form_invalid(self, form): response = {'success': False} @@ -216,7 +185,8 @@ class ProfileInvalidView(FormView): class BaseProfileView(ProfileInvalidView): """ - abstract view + abstract view for ajax calls. handles form with instance profile + return json """ def form_valid(self, form): profile = self.request.user.profile @@ -226,73 +196,87 @@ class BaseProfileView(ProfileInvalidView): return HttpResponse(json.dumps(response), content_type='application/json') class WorkView(ProfileInvalidView): + """ + instance user + """ form_class = WorkForm def form_valid(self, form): user = self.request.user form = self.form_class(self.request.POST, instance=user) - form.save() - response = {'success': True} + user = form.save() + company = user.company + + + response = {'success': True, 'url':company.get_permanent_url()} return HttpResponse(json.dumps(response), content_type='application/json') class AvatarView(BaseProfileView): + """ + instance profile. save user avatar. + + if call is ajax- return json data, else redirect to profile page + """ form_class = AvatarForm def form_valid(self, form): profile = self.request.user.profile - if not self.request.FILES: - response = {'success': False, 'message':'files is empty'} - return HttpResponse(json.dumps(response), content_type='application/json') form = self.form_class(self.request.POST, self.request.FILES, instance=profile) form.save() if self.request.is_ajax(): im = get_thumbnail(profile.avatar, '100x100', crop='center') response = {'success': True, 'url': im.url} - #response = {'success': True} - return HttpResponse(json.dumps(response), content_type='application/json') else: return HttpResponseRedirect('/profile/') class HomeView(BaseProfileView): + """ + instance profile + """ form_class = HomeForm class AboutCompanyView(BaseProfileView): + """ + instance profile + """ form_class = AboutCompanyForm class PhoneView(BaseProfileView): + """ + instance profile + """ form_class = PhoneForm class WebPageView(BaseProfileView): + """ + instance profile + """ form_class = WebPageForm class SocialView(BaseProfileView): + """ + instance profile + """ form_class = SocialForm class AboutView(BaseProfileView): + """ + instance profile + """ form_class = AboutForm - -class EmailView(ProfileInvalidView): - form_class = EmailForm - - def form_valid(self, form): - user = self.request.user - form = self.form_class(self.request.POST, instance=user) - #form.save() - response = {'success': False} - - return HttpResponse(json.dumps(response), content_type='application/json') - - class NameView(ProfileInvalidView): + """ + instance user + """ form_class = NameForm def form_valid(self, form): @@ -303,10 +287,6 @@ class NameView(ProfileInvalidView): return HttpResponse(json.dumps(response), content_type='application/json') -def test(request): - - return HttpResponse('test') - from exposition.models import Exposition @@ -321,7 +301,7 @@ def get_user(url): class UserEventView(ListView): model = Exposition template_name = 'accounts/user_events.html' - paginate_by = 2 + paginate_by = 10 obj = None event_type = None @@ -330,7 +310,7 @@ class UserEventView(ListView): url = self.kwargs.get('url') user = get_user(url) self.obj = user - return user.exposition_users.all() + return user.exposition_users.language().select_related('country').all() def get_context_data(self, **kwargs): context = super(UserEventView, self).get_context_data(**kwargs) @@ -339,28 +319,78 @@ class UserEventView(ListView): return context class UserExpositionsView(UserEventView): + """ + return template with list of expos that user joined + """ event_type = _(u'Выставки') def get_queryset(self): url = self.kwargs.get('url') user = get_user(url) self.obj = user - return user.exposition_users.all() + return user.get_expos() + class UserConferenceView(UserEventView): + """ + return template with list of confs that user joined + """ event_type = _(u'Конференции') def get_queryset(self): url = self.kwargs.get('url') user = get_user(url) self.obj = user - return user.conference_users.all() + return user.get_confs() class UserSeminarView(UserEventView): + """ + return template with list of seminars that user joined + """ event_type = _(u'Семинары') def get_queryset(self): url = self.kwargs.get('url') user = get_user(url) self.obj = user - return user.seminar_users.all() \ No newline at end of file + return user.get_seminars() + +@login_required +def change_password(request): + """ + Change current user password if new password is valid + """ + success = {'success': False} + if request.POST: + form = ChangePasswordForm(request.POST) + if form.is_valid(): + user = request.user + if(user.check_password(form.cleaned_data.get('old_password'))): + user.set_password(form.cleaned_data.get('new_password')) + user.save() + success['success'] = True + success['message'] = _(u'Пароль изменен') + return HttpResponse(json.dumps(success), content_type='application/json') + else: + errors = {'errors': [_(u'Не правильный пароль')]} + success.update(errors) + return HttpResponse(json.dumps(success), content_type='application/json') + else: + errors = [err[0] for err in form.errors.values()] + errors = {'errors': errors} + success.update(errors) + return HttpResponse(json.dumps(success), content_type='application/json') + else: + return HttpResponse(json.dumps(success), content_type='application/json') + + +class Feed(ListView): + template_name = 'client/accounts/feed.html' + paginate_by = 5 + model = Exposition + + def get_queryset(self): + filter = self.request.user.eventfilter + qs = filter.get_queryset() + + return qs diff --git a/city/forms.py b/city/forms.py index b1d5fc9a..d2f8ff62 100644 --- a/city/forms.py +++ b/city/forms.py @@ -94,10 +94,10 @@ class CityForm(forms.Form): #city.country = Country.objects.get(id=data['country'].id)# .id cause select uses queryset # url generate for city: "country_name-city_name" - if data.get('name_en'): - city.url = '%s-'%translit_with_separator(city.country.name) + translit_with_separator(data['name_en']).lower() - else: - city.url = '%s-'%translit_with_separator(city.country.name) + translit_with_separator(data['name_ru']).lower() + #if data.get('name_en'): + #city.url = '%s-'%translit_with_separator(city.country.name) + translit_with_separator(data['name_en']).lower() + #else: + #city.url = '%s-'%translit_with_separator(city.country.name) + translit_with_separator(data['name_ru']).lower() #city.url = '%s'%translit_with_separator(data['name_ru'].strip()).lower() #city.save() @@ -114,7 +114,7 @@ class CityForm(forms.Form): name_ru = self.cleaned_data.get('name_ru') name_en = self.cleaned_data.get('name_en') country = self.cleaned_data.get('country') - + """ city = City.objects.filter( url='%s-%s'%(translit_with_separator(country.name), translit_with_separator(name_en)) ) @@ -126,6 +126,7 @@ class CityForm(forms.Form): msg = 'Город с таким названием уже существует' self._errors['name_ru'] = ErrorList([msg]) del self.cleaned_data['name_ru'] + """ return self.cleaned_data diff --git a/city/models.py b/city/models.py index ae446d6e..f2e312bf 100644 --- a/city/models.py +++ b/city/models.py @@ -85,6 +85,14 @@ class City(TranslatableModel): return len(Exposition.objects.filter(city=self.id)) + def get_parent(self): + parent = {'text' : self.country.name, 'id': self.country.id, 'name': 'co', + 'parent':{'text' : self.country.area.name, 'id': self.country.area.id, 'name': 'area'}} + return parent + + def get_sub_categories(self): + return [] + diff --git a/company/admin.py b/company/admin.py index 97014623..0230c396 100644 --- a/company/admin.py +++ b/company/admin.py @@ -14,7 +14,7 @@ from file.models import FileModel, TmpFile from file.forms import FileModelForm #custom views from functions.custom_views import objects_list, add_object_with_file, delete_object -from functions.admin_views import AdminListView +from functions.admin_views import AdminListView, AdminView def company_all(request): @@ -67,7 +67,7 @@ def company_change(request, url): #fill form with data from database data = {'url':company.url, 'staff_number':company.staff_number, 'address': company.address, 'phone':company.phone, 'fax':company.fax, 'web_page':company.web_page, - 'email':company.email, 'social':company.social, 'foundation': company.foundation, + 'email':company.email, 'foundation': company.foundation, 'company_id':company.id} if company.country: @@ -112,4 +112,48 @@ def company_change(request, url): class CompanyListView(AdminListView): template_name = 'admin/company/company_list.html' form_class = CompanyFilterForm - model = Company \ No newline at end of file + model = Company + +class CompanyView(AdminView): + form_class = CompanyForm + model = Company + template_name = 'admin/company/company.html' + success_url = '/admin/company/all/' + + def get_form(self, form_class): + if self.request.POST: + return super(CompanyView, self).get_form(form_class) + obj = self.set_obj() + if obj: + data = {'url':obj.url, 'staff_number':obj.staff_number, 'address': obj.address, + 'phone':obj.phone, 'fax':obj.fax, 'web_page':obj.web_page, + 'email':obj.email, 'foundation': obj.foundation, + 'company_id':obj.id} + + + if obj.country: + data['country'] = obj.country_id + + if obj.city: + data['city'] = obj.city_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()]) + + 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] = trans_obj.name + data['description_%s' % code] = trans_obj.description + data['specialization_%s' % code] = trans_obj.specialization + data['address_inf_%s' % code] = trans_obj.address_inf + data['representation_%s' % code] = trans_obj.representation + 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.filter(theme__in=data['theme'])] + return form + else: + return form_class() \ No newline at end of file diff --git a/company/admin_urls.py b/company/admin_urls.py index 4f3f38ad..5c29249b 100644 --- a/company/admin_urls.py +++ b/company/admin_urls.py @@ -1,11 +1,16 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url -from admin import CompanyListView +from admin import CompanyListView, CompanyView urlpatterns = patterns('company.admin', - url(r'^add.*/$', 'company_add'), - url(r'^delete/(?P.*)/$', 'company_delete'), - url(r'^change/(?P.*).*/$', 'company_change'), - #url(r'^all/$', 'company_all'), url(r'^all/$', CompanyListView.as_view()), + url(r'^$', CompanyView.as_view()), + url(r'^(?P.*)/$', CompanyView.as_view()), + + #url(r'^add.*/$', 'company_add'), + #url(r'^delete/(?P.*)/$', 'company_delete'), + #url(r'^change/(?P.*).*/$', 'company_change'), + #url(r'^all/$', 'company_all'), + #url(r'^all/$', CompanyListView.as_view()), + ) \ No newline at end of file diff --git a/company/edit_forms.py b/company/edit_forms.py index e17eb776..fd4dab69 100644 --- a/company/edit_forms.py +++ b/company/edit_forms.py @@ -19,7 +19,7 @@ class NameForm(BaseForm): class SpecializationForm(BaseForm): translation = True - specialization = forms.CharField(label=_(u'Описание компании'), widget=forms.TextInput(attrs={'style': 'width: 420px;'})) + specialization = forms.CharField(label=_(u'Описание компании'), widget=forms.TextInput()) class Meta: model = Company._meta.translations_model fields = ('specialization',) @@ -114,7 +114,7 @@ class DescriptionForm(BaseForm): class AddressForm(BaseForm): translation = True - address_inf = forms.CharField(label=_(u'Адрес компании'), widget=forms.TextInput(attrs={'style': 'width: 420px;'})) + address_inf = forms.CharField(label=_(u'Адрес компании'), widget=forms.TextInput()) class Meta: model = Company._meta.translations_model fields = ('address_inf',) diff --git a/company/models.py b/company/models.py index 279a1ca2..ee471847 100644 --- a/company/models.py +++ b/company/models.py @@ -2,7 +2,7 @@ from django.db import models from hvad.models import TranslatableModel, TranslatedFields, TranslationManager from django.contrib.contenttypes import generic -# +from django.utils import translation from django.utils.translation import ugettext as _ from functions.custom_fields import LocationField from functions.models_methods import ExpoManager @@ -16,6 +16,9 @@ class Company(TranslatableModel, ExpoMixin): Uses hvad.TranslatableModel which is child of django.db.models class """ + catalog = '/members/' + catalog_name = _(u'Участники:') + search_name = None objects = ExpoManager() files = generic.GenericRelation('file.FileModel', content_type_field='content_type', object_id_field='object_id') @@ -62,11 +65,20 @@ class Company(TranslatableModel, ExpoMixin): def __unicode__(self): return self.lazy_translation_getter('name', self.pk) + def get_index_text(self): + translation.activate('ru') + translations = self.translations.all() + names = ' '.join([tr.name for tr in translations]) + specializations = ' '.join([tr.specialization 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 + ' ' + specializations + ' ' + themes + ' ' + tags + def get_catalog_url(self): return '/members/' def get_permanent_url(self): - url = '%smember-%s'%(self.get_catalog_url(), self.url) + url = '%smember-%s'%(self.catalog, self.url) return url def get_expositions_number(self): diff --git a/company/search_indexes.py b/company/search_indexes.py index effe4c55..1d197d9e 100644 --- a/company/search_indexes.py +++ b/company/search_indexes.py @@ -1,46 +1,38 @@ +# -*- coding: utf-8 -*- from haystack import indexes from models import Company -""" -class CompanyIndex(indexes.SearchIndex, indexes.Indexable): +from functions.search_mixin import ExpoSearchMixin + + +class CompanyExpositionIndex(indexes.SearchIndex, indexes.Indexable, ExpoSearchMixin): text = indexes.CharField(document=True, use_template=True) where = indexes.MultiValueField() + content_auto = indexes.EdgeNgramField() + area_id = indexes.IntegerField() + country_id = indexes.IntegerField() + city_id = indexes.IntegerField() theme = indexes.MultiValueField() tag = indexes.MultiValueField() - country = indexes.CharField(model_attr='country', null=True) - city = indexes.CharField(model_attr='city', null=True) - - def prepare_country(self, obj): - if obj.country: - return '%s'%obj.country.id - return '' - - def prepare_city(self, obj): - if obj.city: - return '%s'%obj.city.id - return '' + url = indexes.CharField() + form_name = indexes.CharField() + # translated fields + name_en = indexes.CharField() + name_ru = indexes.CharField() + catalog_name_en = indexes.CharField() + catalog_name_ru = indexes.CharField() - def prepare_theme(self, obj): + def prepare_form_name(self, obj): + return None - return [str(th.id) for th in obj.theme.filter()] + def prepare_catalog_name_en(self, obj): + return u'Members' - def prepare_tag(self, obj): - return [str(tag.id) for tag in obj.tag.filter()] - - def prepare_where(self, obj): - country = [] - city = [] - if obj.country: - country = [tr.name for tr in obj.country.translations.all()] - if obj.city: - city = [tr.name for tr in obj.city.translations.all()] - - return country + city + def prepare_catalog_name_ru(self, obj): + return u'Участники' def get_model(self): return Company def index_queryset(self, using=None): - return self.get_model().objects.filter() - -""" \ No newline at end of file + return self.get_model().objects.filter() \ No newline at end of file diff --git a/company/views.py b/company/views.py index 5e7231dd..e5cfb0c0 100644 --- a/company/views.py +++ b/company/views.py @@ -49,7 +49,6 @@ class CompanyView(ExpoListView): def get_context_data(self, **kwargs): context = super(CompanyView, self).get_context_data(**kwargs) - context['search_action'] = '/members/search/' context['type'] = 'members search' return context diff --git a/conference/models.py b/conference/models.py index 44b985dd..52221bd3 100644 --- a/conference/models.py +++ b/conference/models.py @@ -24,6 +24,9 @@ class Conference(TranslatableModel, EventMixin, ExpoMixin): Uses hvad.TranslatableModel which is child of django.db.models class """ + catalog = '/conference/' + # type of event + event_type = 'conf' #set manager of this model objects = ExpoManager() diff --git a/core/views.py b/core/views.py index 7d5859ee..8683d1ab 100644 --- a/core/views.py +++ b/core/views.py @@ -9,7 +9,8 @@ from functions.views_help import split_params from django.utils.translation import ugettext as _ -from forms import PlaceSearchForm +#from forms import PlaceSearchForm +from functions.search_forms import PlaceSearchForm from functions.search_forms import EventSearchForm from haystack.query import EmptySearchQuerySet diff --git a/country/admin.py b/country/admin.py index c15f8d50..ede3a8cf 100644 --- a/country/admin.py +++ b/country/admin.py @@ -11,6 +11,7 @@ from file.models import FileModel from file.forms import FileModelForm #custom views from functions.custom_views import objects_list, add_object_with_file, delete_object, filtered_list +from functions.admin_views import paginate_results from functions.forms import AdminSearchForm from functions.admin_views import AdminListView @@ -111,7 +112,15 @@ def country_change(request, url): return render_to_response('country_add.html', args) + class CountryListView(AdminListView): template_name = 'admin/country/country_list.html' form_class = CountryFilterForm - model = Country \ No newline at end of file + model = Country + + def get_context_data(self, **kwargs): + context = super(AdminListView, self).get_context_data(**kwargs) + qs = self.model.objects.all() + result = paginate_results(qs, page=self.request.GET.get('page')) + context['object_list'] = result + return context \ No newline at end of file diff --git a/country/models.py b/country/models.py index 42ad7358..6e954537 100644 --- a/country/models.py +++ b/country/models.py @@ -72,6 +72,10 @@ class Area(TranslatableModel): objects = [{'text':item.name, 'id':item.id, 'name':'co', 'sub':True} for item in self.countries()] return objects + def get_parent(self): + parent = {} + return parent + @@ -165,6 +169,10 @@ class Country(TranslatableModel): objects = [{'text':item.name, 'id':item.id, 'name':'ci', 'sub': False} for item in self.active_cities()] return objects + def get_parent(self): + parent = {'text' : self.area.name, 'id': self.area.id, 'name': 'area'} + return parent + post_save.connect(post_save_handler, sender=Country) \ No newline at end of file diff --git a/exposition/admin.py b/exposition/admin.py index 98fe8b5d..e5309b23 100644 --- a/exposition/admin.py +++ b/exposition/admin.py @@ -13,7 +13,7 @@ from forms import ExpositionCreateForm, ExpositionDeleteForm, TimeTableForm, Sta 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 @@ -237,9 +237,28 @@ def exposition_change(request, url): class ExpositionView(AdminView): form_class = ExpositionCreateForm model = Exposition - success_url = 'admin/exposition/all/' + success_url = '/admin/exposition/all/' template_name = 'admin/exposition/exposition.html' + def form_valid(self, form): + StatisticFormSet = formset_factory(StatisticForm) + formset_statistic = StatisticFormSet(self.request.POST) + self.set_obj() + expo = form.save(obj=self.obj) + + # delete old halls + Statistic.objects.filter(exposition=getattr(expo, 'id')).delete() + + for item in formset_statistic.forms: + # saves new statistic if its valid and not empty + if item.is_valid() and item.has_changed(): + statistic = item.save(commit=False) + statistic.exposition = expo + statistic.save() + + + return HttpResponseRedirect(self.success_url) + def get_form(self, form_class): if self.request.POST: return super(ExpositionView, self).get_form(form_class) @@ -256,6 +275,7 @@ class ExpositionView(AdminView): 'min_stand_size':obj.min_stand_size, 'application_deadline':obj.application_deadline, 'min_open_area':obj.min_open_area, 'max_open_area':obj.max_open_area, 'registration_payment':obj.registration_payment, 'exposition_id':obj.id, + 'registration_link': obj.registration_link, 'expohit': obj.expohit, 'discount': obj.discount, 'canceled': obj.canceled, 'moved': obj.moved, 'logo': obj.logo, 'visitors': obj.visitors, 'members': obj.members, @@ -264,6 +284,9 @@ class ExpositionView(AdminView): if obj.place: data['place'] = obj.place.id + if obj.area: + data['area'] = obj.area + 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()]) @@ -280,6 +303,12 @@ class ExpositionView(AdminView): data['main_title_%s' % code] = trans_obj.main_title data['time_%s' % code] = trans_obj.time data['products_%s' % code] = trans_obj.products + + data['price_day_%s' % code] = trans_obj.price_day + data['price_all_%s' % code] = trans_obj.price_all + data['price_day_bar_%s' % code] = trans_obj.price_day_bar + data['price_all_bar_%s' % code] = trans_obj.price_all_bar + data['discount_description_%s' % code] = trans_obj.discount_description data['stat_countries_%s' % code] = trans_obj.stat_countries data['pre_condition_%s' % code] = trans_obj.pre_condition @@ -292,7 +321,7 @@ class ExpositionView(AdminView): 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.filter(theme__in=data['theme'])] + 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() @@ -301,6 +330,22 @@ class ExpositionView(AdminView): def get_context_data(self, **kwargs): context = super(ExpositionView, self).get_context_data(**kwargs) obj = self.set_obj() + if obj: + StatisticFormSet = modelformset_factory(Statistic, form=StatisticForm, exclude=('exposition',)) + # get existing statistic + statistic = Statistic.objects.filter(exposition=getattr(obj, 'id')) + # fill HallFormSet + formset_statistic = StatisticFormSet(queryset=statistic) + context['file_form'] = FileForm(initial={'model': 'exposition.Exposition'}) + files = FileModel.objects.filter(content_type=ContentType.objects.get_for_model(obj),object_id=getattr(obj, 'id')) + context['files'] = files + + else: + StatisticFormSet = formset_factory(StatisticForm) + formset_statistic = StatisticFormSet() + + context['formset_statistic'] = formset_statistic + context['photo_form'] = PhotoForm() context['timetable_form'] = TimeTableForm() context['timetables'] = TimeTable.objects.filter(exposition=obj) diff --git a/exposition/admin_urls.py b/exposition/admin_urls.py index 5e1c266b..402dd4ed 100644 --- a/exposition/admin_urls.py +++ b/exposition/admin_urls.py @@ -5,14 +5,14 @@ from admin import ExpositionListView, ExpositionView urlpatterns = patterns('exposition.admin', url(r'^upload-photo/(?P.*)/$', 'upload_exposition_photo'), - url(r'^add.*/$', 'exposition_add'), - url(r'^delete/(?P.*)/$', 'exposition_delete'), - url(r'^change/(?P.*)/$', 'exposition_change'), + #url(r'^add.*/$', 'exposition_add'), + #url(r'^delete/(?P.*)/$', 'exposition_delete'), + #url(r'^change/(?P.*)/$', 'exposition_change'), url(r'^all/$', ExpositionListView.as_view()), url(r'^switch/(?P.*)/(?P.*)$', 'exposition_switch'), - url(r'^copy/(?P.*)$', 'exposition_copy'), - - url(r'^$', ExpositionView.as_view()), + #url(r'^copy/(?P.*)$', 'exposition_copy'), url(r'^(?P.*)/$', ExpositionView.as_view()), + url(r'^$', ExpositionView.as_view()), + ) diff --git a/exposition/forms.py b/exposition/forms.py index 4a4c48dd..cede88a6 100644 --- a/exposition/forms.py +++ b/exposition/forms.py @@ -6,6 +6,7 @@ from tinymce.widgets import TinyMCE from django.core.exceptions import ValidationError from django.forms.util import ErrorList from django.core.validators import validate_email, URLValidator +from django.utils.translation import ugettext as _ #models from models import Exposition, TimeTable, TmpTimeTable, AUDIENCE1, CURRENCY, Statistic, BIT_AUDIENCE from theme.models import Tag @@ -25,7 +26,8 @@ from functions.form_check import translit_with_separator from settings.settings import date_formats 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 @@ -41,16 +43,20 @@ class ExpositionCreateForm(forms.Form): public = [(item1, item2) for item1, item2 in BIT_AUDIENCE] currencies = [(item, item) for item in CURRENCY] - data_begin = forms.DateField(label=u'Дата начала') - data_end = forms.DateField(label=u'Дата окночания') + 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.ModelMultipleChoiceField(label=u'Организаторы', queryset=Organiser.objects.all(), required=False) - company = forms.ModelMultipleChoiceField(label=u'Компании', queryset=Company.objects.all(), required=False) - country = forms.ModelChoiceField(label=u'Страна', queryset=Country.objects.all(), empty_label=None) - theme = forms.ModelMultipleChoiceField(label=u'Тематики', queryset=Theme.objects.all()) - place = forms.ModelChoiceField(label=u'Место проведения', queryset=PlaceExposition.objects.all(), - empty_label='', required=False) + organiser = forms.MultipleChoiceField(label=u'Организаторы', required=False, + choices=[(item.id, item.name) for item in Organiser.objects.language().all()]) + + company = forms.MultipleChoiceField(label=u'Компании', required=False, + choices=[(item.id, item.name) for item in Company.objects.language().all()] ) + country = forms.ChoiceField(label=u'Страна', choices=[(c.id, c.name) for c in Country.objects.all()]) + 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.CharField(label=u'Теги', widget=forms.HiddenInput(), required=False) @@ -59,11 +65,13 @@ class ExpositionCreateForm(forms.Form): periodic = forms.ChoiceField(label=u'Периодичность', choices=PERIODIC, required=False) audience = forms.MultipleChoiceField(label=u'Аудитория', choices=public, initial='', required=False) web_page = forms.CharField(label=u'Веб страница', required=False) + registration_link = forms.CharField(label=u'Ссылка на регистрацию', required=False) foundation_year = forms.CharField(label=u'Год основания', required=False) members = forms.CharField(label=u'Участники', required=False) visitors = forms.CharField(label=u'Посетители', required=False) - min_area = forms.CharField(label=u'Минимальная плошадь', required=False) + min_area = forms.CharField(label=u'Минимальная площадь', required=False) discount = forms.CharField(label=u'Cкидка(%)', required=False) + area = forms.CharField(label=u'Площадь', required=False) quality_label = forms.MultipleChoiceField(label=u'Метки', required=False, choices=[('ufi', 'UFI'), ('rsva', 'РСВЯ'), ('exporating', 'ExpoRating')], widget=forms.CheckboxSelectMultiple()) @@ -72,10 +80,10 @@ 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_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) @@ -114,6 +122,17 @@ class ExpositionCreateForm(forms.Form): required=False, widget=CKEditorWidget) self.fields['time_%s' % code] = forms.CharField(label=u'Время работы', required=False, widget=CKEditorWidget) + + self.fields['price_day_%s' % code] = forms.CharField(label=u'Стоимость билета 1 день', required=False, + widget=forms.TextInput(attrs={'style':'width: 550px'})) + + self.fields['price_all_%s' % code] = forms.CharField(label=u'Стоимость билета все дни', required=False, + widget=forms.TextInput(attrs={'style':'width: 550px'})) + self.fields['price_day_bar_%s' % code] = forms.CharField(label=u'Стоимость на стойке 1 день', required=False, + widget=forms.TextInput(attrs={'style':'width: 550px'})) + self.fields['price_all_bar_%s' % code] = forms.CharField(label=u'Стоимость на стойке все дни', required=False, + widget=forms.TextInput(attrs={'style':'width: 550px'})) + self.fields['products_%s' % code] = forms.CharField(label=u'Экспонируемые продукты', required=False, widget=CKEditorWidget) self.fields['discount_description_%s' % code] = forms.CharField(label=u'Описание скидки', @@ -136,11 +155,6 @@ class ExpositionCreateForm(forms.Form): widget=forms.TextInput(attrs={'style':'width: 550px'})) self.fields['descriptions_%s' % code] = forms.CharField(label=u'Meta description', required=False, max_length=255, widget=forms.TextInput(attrs={'style':'width: 550px'})) - #creates select inputs ind fill it - #!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.MultipleChoiceField(label='Услуги', choices=services, required=False) - def save(self, obj=None): """ @@ -162,24 +176,24 @@ class ExpositionCreateForm(forms.Form): #simple fields exposition.url = translit_with_separator(data['name_ru'].strip()).lower() - exposition.logo = data['logo'] + if data.get('logo'): + exposition.logo = data['logo'] + exposition.data_begin = data['data_begin'] exposition.data_end = data['data_end'] exposition.periodic = data['periodic'] exposition.web_page= data['web_page'] + exposition.registration_link = data['registration_link'] exposition.foundation_year = data['foundation_year'] exposition.members = data['members'] exposition.visitors = data['visitors'] + exposition.area = data['area'] exposition.min_area = data['min_area'] exposition.currency = data['currency'] exposition.application_deadline = data['application_deadline'] exposition.min_stand_size = data['min_stand_size'] - exposition.price_day = data['price_day'] - exposition.price_all = data['price_all'] - exposition.price_all_bar = data['price_all_bar'] - exposition.price_day_bar = data['price_day_bar'] exposition.price_catalog = data['price_catalog'] exposition.tax = data['tax'] exposition.min_closed_area = data['min_closed_area'] @@ -207,36 +221,26 @@ class ExpositionCreateForm(forms.Form): exposition.audience = audience - if data.get('country'): - exposition.country = Country.objects.get(id=data['country'].id)#.id cause select uses queryset + exposition.country = Country.objects.get(id=data['country']) + exposition.city = City.objects.get(id=data['city']) - if data.get('city'): - exposition.city = City.objects.get(id=data['city']) if data.get('place'): - exposition.place = PlaceExposition.objects.get(id=data['place'].id)#.id cause select uses queryset + exposition.place = PlaceExposition.objects.get(id=data['place']) # fill translated fields and save object fill_with_signal(Exposition, exposition, data) #fill manytomany fields - for item in data['theme']: - exposition.theme.add(item.id)#.id cause select uses queryset + + exposition.theme.add(*data['theme']) exposition.tag.add(*Tag.objects.filter(id__in=data['tag'])) - #for item in data['tag']: - # exposition.tag.add(item) - # uses because in the next loop data will be overwritten - for item in data['organiser']: - exposition.organiser.add(item) + exposition.organiser.add(*Organiser.objects.filter(id__in=data.get('organiser', []))) + exposition.company.add(*Company.objects.filter(id__in=data.get('company', []))) - for item in data['company']: - exposition.company.add(item) exposition.save() - # save files - check_tmp_files(exposition, data.get('key')) - check_tmp_timetables(exposition, data.get('key')) return exposition def clean(self): @@ -280,6 +284,30 @@ class ExpositionCreateForm(forms.Form): raise forms.ValidationError(e.messages[0]) return web_page + def clean_registration_link(self): + """ + checking web_page + """ + cleaned_data = super(ExpositionCreateForm, self).clean() + registration_link = cleaned_data.get('registration_link') + if not registration_link: + return '' + + validate = URLValidator() + try: + validate(registration_link) + except(forms.ValidationError),e: + raise forms.ValidationError(e.messages[0]) + return registration_link + + def clean_area(self): + """ + checking foundation_year + """ + cleaned_data = super(ExpositionCreateForm, self).clean() + area = cleaned_data.get('area').strip() + return is_positive_integer(area) + def clean_foundation_year(self): """ checking foundation_year @@ -438,6 +466,7 @@ class StatisticForm(forms.ModelForm): year = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:70px'})) visitors = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:70px'}), required=False) members = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:70px'}), required=False) + area = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:70px'}), required=False) class Meta: model = Statistic exclude = ('exposition') @@ -464,9 +493,8 @@ class TimeTableForm(forms.Form): widget=forms.TextInput(attrs={'style':'width: 150px'})) end = forms.DateTimeField(label='Время окончания', input_formats=date_formats, widget=forms.TextInput(attrs={'style':'width: 150px'})) - timetable_organiser = forms.ModelChoiceField(label='Организатор', queryset=Organiser.objects.all(), empty_label='', required=False) - # uses for comparing with TmpTimetable key - key = forms.CharField(required=False, widget=forms.HiddenInput()) + timetable_organiser = forms.ChoiceField(label='Организатор', required=False, + choices=[(item.id, item.name) for item in Organiser.objects.language().all()]) def __init__(self, *args, **kwargs): """ @@ -484,7 +512,7 @@ class TimeTableForm(forms.Form): self.fields['name_%s' % code] = forms.CharField(label='Название программы', required=required, widget=forms.TextInput(attrs={'style':'width: 550px'})) self.fields['programe_%s' % code] = forms.CharField(label='Программа', required=required, - widget=TinyMCE()) + widget=CKEditorWidget) self.fields['speaker_%s' % code] = forms.CharField(label='Спикеры', required=False, widget=forms.TextInput(attrs={'style':'width: 550px'})) self.fields['place_%s' % code] = forms.CharField(label='Место проведения', required=False, @@ -493,14 +521,13 @@ class TimeTableForm(forms.Form): def save(self,exposition=None): data = self.cleaned_data if not exposition: - timetable = TmpTimeTable() - timetable.key = data.get('key') + return None else: timetable = TimeTable() # simple fields if data.get('timetable_organiser'): - timetable.timetable_organiser = data['timetable_organiser'] + timetable.timetable_organiser_id = data['timetable_organiser'] timetable.exposition = exposition timetable.begin = data.get('begin') timetable.end = data.get('end') @@ -513,5 +540,40 @@ class TimeTableForm(forms.Form): return timetable +monthes = [('', ''), + (1, _(u'Январь')), (2, _(u'Февраль')), (3, _(u'Март')), (4, _(u'Апрель')), + (5, _(u'Май')), (6, _(u'Июнь')), (7, _(u'Июль')), (8, _(u'Август')), + (9, _(u'Сентябрь')), (10, _(u'Октябрь')), (11, _(u'Ноябрь')), (12, _(u'Декабрь'))] + class ExpositionFilterForm(AdminFilterForm): - model = Exposition \ No newline at end of file + + model = Exposition + country = forms.MultipleChoiceField(label=u'Страна', choices=[(c.id, c.name) for c in Country.objects.all()], + required=False) + #city = forms.CharField(label=u'Город', widget=forms.HiddenInput(), required=False) + year = forms.CharField(label=u'Год', required=False) + month = forms.ChoiceField(label=u'Месяц',choices=[(item[0], item[1]) for item in monthes], required=False) + + + def filter(self): + data = self.cleaned_data + qs = super(ExpositionFilterForm, self).filter() + exact_name = data['exact_name'] + if exact_name: + + return self.model.objects.filter(translations__name=exact_name).distinct() + country = data['country'] + # city = data['city'] + year = data['year'] + month = data['month'] + if country: + qs = qs.filter(country__in=country) + # if city: + # qs = qs.filter(city__in=city) + if year: + qs = qs.filter(data_begin__year=year) + if month: + qs = qs.filter(data_begin__month=month) + + return qs + diff --git a/exposition/management/commands/convert_expo_logo.py b/exposition/management/commands/convert_expo_logo.py index ea78109b..1bc8c056 100644 --- a/exposition/management/commands/convert_expo_logo.py +++ b/exposition/management/commands/convert_expo_logo.py @@ -1,10 +1,5 @@ from django.core.management.base import BaseCommand, CommandError from exposition.models import Exposition -from django.contrib.sites.models import Site -from photologue.models import Gallery, Photo -from file.models import FileModel -from django.core.files import File -from django.core.files.temp import NamedTemporaryFile import urllib2 from django.conf import settings from django.utils import translation diff --git a/exposition/management/commands/exposition_load.py b/exposition/management/commands/exposition_load.py index 515b103c..4959d163 100644 --- a/exposition/management/commands/exposition_load.py +++ b/exposition/management/commands/exposition_load.py @@ -1,396 +1,85 @@ # -*- coding: utf-8 -*- -from django.core.management.base import BaseCommand, CommandError -from city.models import City -from country.models import Country -from exposition.models import Exposition -import xlrd, xlwt -#from import_xls.excel_settings import event_sett -from django.conf import settings -from import_xls.import_forms import google_address - -def to_int(val): - """ - Reverse function to get_int - return None if value isnt integer - """ - try: - return int(val) - except ValueError: - return None - -def to_country(value): - try: - #query = get_translation_aware_manager(Country) - #country = query.filter(name=value)[0] - country = Country.objects.filter(translations__name=value.strip())[0] - return country - except IndexError: - return None - -def to_city(value, lang, country): - try: - # get city by name - #objects = get_translation_aware_manager(City) - # except IndexError if no found - city = City.objects.filter(translations__name=value.strip(), country=country)[0] - return city - except IndexError: - return None - -from theme.models import Theme, Tag -def to_theme(obj, value): - if isinstance(value, float) or isinstance(value, int): - if (value - int(value) > 0): - value = str(value) - else: - value = str(int(value)) - theme_ids = value.split('.') - else: - theme_ids = value.split(',') - theme_objects = [] - for id in theme_ids: - try: - theme = Theme.objects.language('ru').get(id=int(id)) - theme_objects.append(theme) - obj.theme.add(theme) - except Theme.DoesNotExist, ValueError: - pass - - - return theme_objects - -from hvad.utils import get_translation_aware_manager -def to_tag(obj,value): - if value == [""]: - return None - - names = value.split(',') - tags = [] - for name in names: - objects = get_translation_aware_manager(Tag) - tag = objects.filter(name=name) - #tag = Tag.objects.language('ru').filter(transations__name=name) - if tag: - tags.append(tag[0]) - obj.tag.add(tag[0]) - else: - continue - return tags - -import time -def to_date(value): - if not value: - return None - - if isinstance(value, unicode) or isinstance(value, str): - - t = time.strptime(value, "%d.%m.%Y") - if isinstance(value, float): - t = xlrd.xldate_as_tuple(value, 0)+(0,0,0) - - return time.strftime("%Y-%m-%d", t) - -def to_currency(value): - if value=='USD': - return value - if value=='EUR': - return value - if value=='RUB': - return 'RUB' - return 'USD' - -from django.core.validators import validate_email, URLValidator - -def to_url(url): - validate = URLValidator() - try: - validate(url) - except: - return '' - return url - -def to_email(email): - try: - validate_email(email) - except: - return '' - return email - -from file.models import FileModel -import urllib2 +import xlrd +from django.core.management.base import BaseCommand from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from functions.files import get_alternative_filename - - -def save_logo(obj, value, purpose): - """ - if not obj.id: - return None - """ - - - urls = value.split(';') - - - for url in urls: - - - - - if url[0]!=('/'): - url = '/'+url - url = 'http://www.expomap.ru'+url - - file_name = url.split('/')[-1] - alt_name = get_alternative_filename(settings.MEDIA_ROOT+'imgs/', file_name) - - download_to = settings.MEDIA_ROOT+'imgs/'+alt_name - - try: - response = urllib2.urlopen(url, timeout=3) - except: - continue - - with open(download_to,'wb') as f: - f.write(response.read()) - f.close() - - file_name ='imgs/'+alt_name - content_type = ContentType.objects.get_for_model(obj) - file = FileModel(file_path=file_name, file_type='JPG', purpose=purpose, - content_type=content_type, object_id=obj.id) - - file.save() - -from place_exposition.models import EXPOSITION_TYPE -def to_type(value): - for t in EXPOSITION_TYPE: - if value == t[1]: - return t[0] - return 'Exposition complex' - -def to_phone(value): - if value: - if isinstance(value, float) or isinstance(value, int): - return value - else: - deduct = ('-','(',')','.',' ', '+') - for elem in deduct: - value = value.replace(elem, '') - - value = to_int(value) - - return value - -from place_exposition.models import PlaceExposition -def to_place(value): - try: - place = PlaceExposition.objects.get(id=value) - return place - except: - - return None -from exposition.models import BIT_AUDIENCE -def to_audience(value, model=Exposition): - if value: - l = value.split(', ') - aud = {'Trade visitors':'experts', 'Trade visitors and Public':'experts and consumers', - 'General public': 'general public'} - if l: - new_list = [] - for value in l: - for item1, item2 in BIT_AUDIENCE: - if aud.get(value) == item2: - new_list.append(item1) - if new_list: - - return reduce(lambda x,y: x|y, (getattr(model.audience, item) for item in new_list)) - return 0 - -def to_periodic(value): - per = {'Оnce a year': 1.0, 'Every 2 years':0.5, 'Twice a year':2.0} - return per.get(value, 0) - -from organiser.models import Organiser -def to_organiser(obj, value): - org = Organiser.objects.filter(translations__name=value) - if org: - obj.organiser.add(org[0]) - else: - org = Organiser() - org.translate('ru') - org.name = value - org.save() - - -event_sett = { - u'ID':{u'field': u'id', u'func': to_int}, - u'Название':{u'field': u'name', u'func': unicode}, - u'Краткое описание':{u'field': u'main_title', u'func': unicode}, - u'Дата начала:(YYYY-MM-DD)':{u'field': u'data_begin', u'func': to_date}, - 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', u'func': to_city, 'extra_values': 'country'}, - 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_organiser, u'method': True},#### - u'Организатор №2':{u'field': u'organiser', u'func': to_organiser, u'method': True},#### - u'Описание события':{u'field': u'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'logo', u'func': save_logo, u'method': True, u'purpose': 'logo'}, - u'Официальный веб-сайт':{u'field': u'web_page', u'func': to_url}, - u'Экспонируемые продукты':{u'field': u'products', u'func': unicode}, - u'Время работы':{u'field': u'time', u'func': unicode}, - u'Валюта':{u'field': u'currency', u'func': to_currency}, - # - u'Посетители_билет (1 день)':{u'field': u'price_day', u'func': to_int}, - u'Посетители_билет (все дни)':{u'field': u'price_all', u'func': to_int}, - #u'Условия':{u'field': u'periodic', u'func': unicode}, - u'Посетители_билет (1 день)(на стойке)':{u'field': u'price_day_bar', u'func': to_int},##?? - u'Посетители_билет (все дни)(на стойке)':{u'field': u'price_all_bar', u'func': to_int},##?? - #u'Условия':{u'field': u'periodic', u'func': unicode},##?? - #u'Примечание':{u'field': u'periodic', u'func': unicode}, - u'Каталог':{u'field': u'price_catalog', u'func': to_int}, - u'Налог включен':{u'field': u'tax', u'func': bool}, - u'Год основания':{u'field': u'foundation_year', u'func': to_int}, - #u'Данные за год':{u'field': u'periodic', u'func': to_int}, - u'Посетители':{u'field': u'visitors', u'func': to_int}, - u'Участники':{u'field': u'members', u'func': to_int}, - #u'Страны':{u'field': u'periodic', u'func': unicode},##?? - u'Площадь':{u'field': u'periodic', u'func': to_int}, - u'Min_Raw кв.м.':{u'field': u'min_closed_area', u'func': to_int}, - u'Max_Raw кв.м.':{u'field': u'max_closed_area', u'func': to_int}, - u'Min_Pack кв.м.':{u'field': u'min_closed_equipped_area', u'func': to_int}, - u'Max_Pack кв.м.':{u'field': u'max_closed_equipped_area', u'func': to_int}, - u'Открытая площадь':{u'field': u'max_open_area', u'func': to_int}, - u'Мин. Площадь кв.м.':{u'field': u'min_open_area', u'func': to_int}, - u'Регистрационный взнос':{u'field': u'registration_payment', u'func': to_int}, - #u'Примечание':{u'field': u'periodic', u'func': unicode}, - u'Крайний срок подачи заявки':{u'field': u'application_deadline', u'func': to_date}, - u'UFI':{u'field': u'quality_label', u'func': bool, },## - u'РСВЯ':{u'field': u'quality_label', u'func': bool},## - u'EXPORATING':{u'field': u'quality_label', u'func': bool},## - u'Отменена':{u'field': u'canceled', u'func': bool}, - u'ExpoHIT':{u'field': u'expohit', u'func': bool}, - #u'Фото':{u'field': u'photo', u'func': save_photo, u'method': True}, +from exposition.models import Exposition +from import_xls.excel_settings import event_sett -} +CHINA_FILE = settings.MEDIA_ROOT+'/import/expo_china_ru.xlsx' +GERMANY_FILE = settings.MEDIA_ROOT+'/import/expo_germany_ru.xlsx' +# 391 row not imported(same url) +ITALY_FILE = settings.MEDIA_ROOT+'/import/expo_italy_ru.xlsx' +# moscow 3 exps +F = settings.MEDIA_ROOT+'/import/exp.xlsx' class Command(BaseCommand): def handle(self, *args, **options): - f = open(settings.MEDIA_ROOT+'/import/Places Eng Upd.xlsx', 'r') + f = open(F, '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]] - print(123) + for row_number, row in enumerate(row_list[1:]): - - for row_number, row in enumerate(row_list): - # go through all rows in file - if row_number > 0: - # first field is label - if row[0] != '': - # in first column ids - - try: - object = Exposition.objects.language('en').get(id=int(row[0])) - except ValueError: - object = Exposition() - - object.translate('en') - - except Exposition.DoesNotExist: - object = Exposition(id= int(row[0])) - - object.translate('en') - else: - # if id blank - its a new place + if row[0] != '': + # in first column ids + try: + object = Exposition.objects.language('ru').get(id=int(row[0])) + except ValueError: object = Exposition() + object.translate('ru') + + except Exposition.DoesNotExist: + object = Exposition(id= int(row[0])) + object.translate('ru') + else: + # if id blank - its a new place + object = Exposition() + 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']) - object.translate('en') - methods = [] - flag = False - for col_number, cell in enumerate(row): - - # go through row cells - # field name current cell - 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, 'en', getattr(object, extra_value)) - else: - value = func(cell) - #if field_name =='adress': - # setattr(object, 'address', google_address(value)) - if field_name=='city' and not value: - print('error city') - flag = True - continue - if not field_name=='quality_label': - setattr(object, field_name, value) - else: - if value: - if setting.get('l') == 'ufi': - object.quality_label.ufi = True - if setting.get('l') == 'rsva': - object.quality_label.rsva = True - if setting.get('l') == 'exporating': - object.quality_label.exporating = True + else: + value = func(cell) + setattr(object, field_name, value) + object.save() + print('post save %s'% str(object)) - if not flag: - object.save() - """ + for method in methods: + func = method['func'] + if method.get('purpose'): try: - print('pre save %s'% str(object)) - object.save() + func(object, method['value'], method['purpose']) except: - print('saving error') continue - """ - print('post save %s'% str(object)) else: - print('bad city') - - for method in methods: - func = method['func'] - if method.get('purpose'): - try: - func(object, method['value'], method['purpose']) - except: - continue - else: - try: - func(object, method['value']) - except: - continue + func(object, method['value']) \ No newline at end of file diff --git a/exposition/manager.py b/exposition/manager.py index 288ff35d..4e8ee528 100644 --- a/exposition/manager.py +++ b/exposition/manager.py @@ -1,9 +1,12 @@ +import datetime from hvad.models import TranslationManager + class ClientManager(TranslationManager): def get_query_set(self): - return super(ClientManager, self).get_query_set().filter(is_published=True) + now = datetime.datetime.now().date() + return super(ClientManager, self).get_query_set().filter(is_published=True, data_begin__gte=now).order_by('data_begin') """ diff --git a/exposition/models.py b/exposition/models.py index 24826302..2ab9ceb9 100644 --- a/exposition/models.py +++ b/exposition/models.py @@ -3,22 +3,22 @@ import copy, datetime from django.db import models from django.db.models.signals import post_save, pre_save from django.utils.translation import ugettext as _ +from django.utils import translation from django.conf import settings from hvad.models import TranslatableModel, TranslatedFields, TranslationManager - from django.contrib.contenttypes import generic from bitfield import BitField from service.models import Service from functions.db import db_table_exists from organiser.models import Organiser from manager import ClientManager -# from functions.custom_fields import EnumField from functions.signal_handlers import post_save_handler, pre_save_handler from functions.models_methods import ExpoManager 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 AUDIENCE1 = ((None,_(u'Не выбрано')), ('experts', _(u'Специалисты')), @@ -32,7 +32,7 @@ BIT_AUDIENCE = (('experts', _(u'Специалисты')), ('experts and consume ('general public', _(u'Широкая публика'))) -CURRENCY = ('RUB', 'USD', 'EUR') +CURRENCY = ('RUB', 'USD', 'EUR', 'RMB', 'GBP') # 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 [] @@ -44,11 +44,14 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): Uses hvad.TranslatableModel which is child of django.db.models class """ - # main + catalog = '/expo/' + catalog_name = _(u'Выставки:') + search_name = None + # type of event + event_type = 'expo' - url = models.SlugField(unique=True) - #logo = fo + url = models.SlugField(unique=True, max_length=255) data_begin = models.DateField(verbose_name='Дата начала') data_end = models.DateField(verbose_name='Дата окончания') services = BitField(flags=flags) @@ -100,11 +103,15 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): discount_description = models.TextField(verbose_name='Описание скидки', blank=True), time = models.TextField(verbose_name='Время работы', blank=True), # visit and particaption data + price_day = models.CharField(verbose_name='Стоимость билета 1 день', max_length=255, blank=True), + price_all = models.CharField(verbose_name='Стоимость билета все дни', max_length=255, blank=True), + price_day_bar = models.CharField(verbose_name='Стоимость на стойке 1 день', max_length=255, blank=True), + price_all_bar = models.CharField(verbose_name='Стоимость на стойке все дни', max_length=255, blank=True), stat_countries = models.TextField(verbose_name='Участвующие страны', blank=True), pre_condition = models.CharField(verbose_name='Условия предварительной регистрации', max_length=255, blank=True), stand_condition = models.CharField(verbose_name='Условия регистрации на стойке', max_length=255, blank=True), visit_note = models.CharField(verbose_name='Примечание по посещению', max_length=255, blank=True), - participation_note = models.CharField(verbose_name='Примечание по участии', max_length=255, blank=True), + participation_note = models.TextField(verbose_name='Примечание по участии', blank=True), #-----meta data @@ -114,25 +121,20 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): ) 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='Переодичность', 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) foundation_year = models.PositiveIntegerField(verbose_name='Год основания', blank=True, null=True) + area = models.PositiveIntegerField(verbose_name='Площадь', blank=True, null=True) # conditions of Participation registration_link = models.URLField(verbose_name='Ссылка на регистрацию', max_length=255, blank=True) min_area = models.PositiveIntegerField(verbose_name='Минимальная площадь', blank=True, null=True) currency = EnumField(values=CURRENCY, default='USD') application_deadline = models.DateField(verbose_name='Срок подачи заявки', null=True) min_stand_size = models.PositiveIntegerField(verbose_name='Минимальный размер стэнда', blank=True, null=True) - price_day = models.PositiveIntegerField(verbose_name='Стоимость билета 1 день', blank=True, null=True) - price_all = models.PositiveIntegerField(verbose_name='Стоимость билета все дни', blank=True, null=True) - # Условие - price_day_bar = models.PositiveIntegerField(verbose_name='Стоимость на стойке 1 день', blank=True, null=True) - price_all_bar = models.PositiveIntegerField(verbose_name='Стоимость на стойке все дни', blank=True, null=True) - # условие - # примечание price_catalog = models.PositiveIntegerField(verbose_name='Стоимость каталога', blank=True, null=True) tax = models.BooleanField(verbose_name='Налог', default=1) min_closed_area = models.PositiveIntegerField(verbose_name='Минимальная цена закрытой НЕ оборудованной площади', @@ -148,12 +150,10 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): max_open_area = models.PositiveIntegerField(verbose_name='Максимальная цена открытой площади', blank=True, null=True) registration_payment = models.PositiveIntegerField(verbose_name='Регистрационный взнос', blank=True, null=True) - # примечание - - # statistic enable = ClientManager() + imports = ExpoImportManager() #set manager of this model objects = ExpoManager() @@ -161,6 +161,26 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): def __unicode__(self): return self.lazy_translation_getter('name', unicode(self.pk)) + def get_parent(self): + return {} + + 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 @@ -184,6 +204,12 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): return gallery + def tags(self): + return self.tag.language().all() + + def statistic_exists(self): + return Statistic.objects,filter(exposition=self).exists() + def upload_photo(self, photo ,gallery=None): """ @@ -195,9 +221,9 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): gallery.photos.add(photo) - def get_index_text(self): - names = [tr.name for tr in self.translations.all()] - return names +# def get_index_text(self): +# names = [tr.name for tr in self.translations.all()] +# return names def get_audience(self): checked = [item for item, bool in self.audience if bool==True] @@ -250,6 +276,14 @@ class Exposition(TranslatableModel, EventMixin, ExpoMixin): 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 clone(self): """ Return an identical copy of the instance with a new ID. @@ -298,6 +332,7 @@ class Statistic(models.Model): year = models.PositiveIntegerField(verbose_name='Год') members = models.PositiveIntegerField(verbose_name='Посетители') visitors = models.PositiveIntegerField(verbose_name='Участники') + area = models.PositiveIntegerField(verbose_name='Площадь') from django.core import serializers from functions.models_methods import hvad_to_dict diff --git a/exposition/search_indexes.py b/exposition/search_indexes.py index 670f82bd..906c835e 100644 --- a/exposition/search_indexes.py +++ b/exposition/search_indexes.py @@ -1,10 +1,14 @@ +# -*- coding: utf-8 -*- +from django.utils import translation from haystack import indexes from models import Exposition +from functions.search_mixin import ExpoSearchMixin -class ExpositionIndex(indexes.SearchIndex, indexes.Indexable): +class ExpositionIndex(indexes.SearchIndex, indexes.Indexable, ExpoSearchMixin): text = indexes.CharField(document=True, use_template=True) where = indexes.MultiValueField() + url = indexes.CharField() data_begin = indexes.DateField(model_attr='data_begin') data_end = indexes.DateField(model_attr='data_end') theme = indexes.MultiValueField() @@ -12,31 +16,36 @@ class ExpositionIndex(indexes.SearchIndex, indexes.Indexable): country_id = indexes.IntegerField() city_id = indexes.IntegerField() area_id = indexes.IntegerField() + content_auto = indexes.EdgeNgramField() + form_name = indexes.CharField() + # translated fields + name_en = indexes.CharField() + name_ru = indexes.CharField() + catalog_name_en = indexes.CharField() + catalog_name_ru = indexes.CharField() - def prepare_area_id(self, obj): - return obj.country.area.id + def prepare_form_name(self, obj): + return None - def prepare_country_id(self, obj): - return obj.country.id + def prepare_catalog_name_en(self, obj): + return u'Expos' - def prepare_city_id(self, obj): - return obj.city.id - - def prepare_theme(self, obj): - return [th.id for th in obj.theme.filter()] - - def prepare_tag(self, obj): - return [th.id for th in obj.tag.filter()] - - def prepare_where(self, obj): - country = [tr.name for tr in obj.country.translations.all()] - city = [tr.name for tr in obj.city.translations.all()] - - return country + city + def prepare_catalog_name_ru(self, obj): + return u'Выставки' def get_model(self): return Exposition def index_queryset(self, using=None): - return self.get_model().objects.filter(is_published=True) \ No newline at end of file + return self.get_model().objects.filter(is_published=True) + + def get_name(self): + return 123 + lang = translation.get_language() + if lang == 'ru': + return self.name_ru + elif lang=='en': + return self.name_en + else: + return self.name_ru \ No newline at end of file diff --git a/exposition/urls.py b/exposition/urls.py index 39b99d05..934d3ac5 100644 --- a/exposition/urls.py +++ b/exposition/urls.py @@ -1,34 +1,80 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url -from views import ExpositionView, ExpositionVisitors, ExpositionMembers, ExpositionStatistic, ExpositionPrice,\ +from views import ExpositionStatistic, ExpositionPrice,\ ExpositionProgramme, ExpositionSearchView, ExpositionByCountry, ExpositionByTheme, ExpositionByCity -from django.http import HttpResponse +from django.http import HttpResponse from views import ExpositionServiceView +from views import ExpoCountryCatalog, ExpoCityCatalog, ExpoThemeCatalog, ExpoTagCatalog, ExpoList, ExpoDetail,\ + ExpoVisitors, ExpoMembers -def test(request): - return HttpResponse('123') urlpatterns = patterns('', + # search url(r'expo/search/', ExpositionSearchView.as_view()), - url(r'expo/country/', ExpositionByCountry.as_view()), - url(r'expo/city/', ExpositionByCity.as_view()), - url(r'expo/theme/', ExpositionByTheme.as_view()), - - url(r'expo/(?P.*)/(?P\d+)/$', ExpositionView.as_view()), - url(r'expo/(?P\d+)/$', ExpositionView.as_view()), - # - url(r'expo/(?P.*)/service/(?P.*)/$', ExpositionServiceView.as_view()), - url(r'expo/(?P.*)/statistic/$', ExpositionStatistic.as_view()), - url(r'expo/(?P.*)/price/$', ExpositionPrice.as_view()), - url(r'expo/(?P.*)/program/$', ExpositionProgramme.as_view()), - # - url(r'expo/(?P.*)/visitors/$', ExpositionVisitors.as_view()), - url(r'expo/(?P.*)/members/$', ExpositionMembers.as_view()), - url(r'expo/(?P.*)/$', ExpositionView.as_view()), - url(r'expo/$', ExpositionView.as_view()), - # + # country catalog + url(r'expo/country/$', ExpositionByCountry.as_view()), + url(r'expo/country/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ExpoCountryCatalog.as_view()), + url(r'expo/country/(?P.*)/(?P\d+)/page/(?P\d+)/$', ExpoCountryCatalog.as_view()), + url(r'expo/country/(?P.*)/page/(?P\d+)/$', ExpoCountryCatalog.as_view()), + url(r'expo/country/(?P.*)/(?P\d+)/(?P.*)/$', ExpoCountryCatalog.as_view()), + url(r'expo/country/(?P.*)/(?P\d+)/$', ExpoCountryCatalog.as_view()), + url(r'expo/country/(?P.*)/$', ExpoCountryCatalog.as_view()), + # city catalog + url(r'expo/city/$', ExpositionByCity.as_view()), + url(r'expo/city/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ExpoCityCatalog.as_view()), + url(r'expo/city/(?P.*)/(?P\d+)/page/(?P\d+)/$', ExpoCityCatalog.as_view()), + url(r'expo/city/(?P.*)/page/(?P\d+)/$', ExpoCityCatalog.as_view()), + url(r'expo/city/(?P.*)/(?P\d+)/(?P.*)/$', ExpoCityCatalog.as_view()), + url(r'expo/city/(?P.*)/(?P\d+)/$', ExpoCityCatalog.as_view()), + url(r'expo/city/(?P.*)/$', ExpoCityCatalog.as_view()), + # theme catalog + url(r'expo/theme/$', ExpositionByTheme.as_view()), + url(r'expo/theme/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ExpoThemeCatalog.as_view()), + url(r'expo/theme/(?P.*)/(?P\d+)/page/(?P\d+)/$', ExpoThemeCatalog.as_view()), + url(r'expo/theme/(?P.*)/page/(?P\d+)/$', ExpoThemeCatalog.as_view()), + url(r'expo/theme/(?P.*)/(?P\d+)/(?P.*)/$', ExpoThemeCatalog.as_view()), + url(r'expo/theme/(?P.*)/(?P\d+)/$', ExpoThemeCatalog.as_view()), + url(r'expo/theme/(?P.*)/$', ExpoThemeCatalog.as_view()), + # tag catalog + url(r'expo/tag/(?P.*)/(?P\d+)/(?P.*)/page/(?P\d+)/$', ExpoTagCatalog.as_view()), + url(r'expo/tag/(?P.*)/(?P\d+)/page/(?P\d+)/$', ExpoTagCatalog.as_view()), + url(r'expo/tag/(?P.*)/page/(?P\d+)/$', ExpoTagCatalog.as_view()), + url(r'expo/tag/(?P.*)/(?P\d+)/(?P.*)/$', ExpoTagCatalog.as_view()), + url(r'expo/tag/(?P.*)/(?P\d+)/$', ExpoTagCatalog.as_view()), + url(r'expo/tag/(?P.*)/$', ExpoTagCatalog.as_view()), + # expo additional pages + url(r'expo/(?P.*)/statistic/$', ExpositionStatistic.as_view()), + url(r'expo/(?P.*)/price/$', ExpositionPrice.as_view()), + url(r'expo/(?P.*)/program/$', ExpositionProgramme.as_view()), + url(r'expo/(?P.*)/visitors/page/(?P\d+)/$', ExpoVisitors.as_view()), + url(r'expo/(?P.*)/visitors/$', ExpoVisitors.as_view()), + url(r'expo/(?P.*)/members/page/(?P\d+)/$', ExpoMembers.as_view()), + url(r'expo/(?P.*)/members/$', ExpoMembers.as_view()), + url(r'expo/(?P.*)/service/(?P.*)/', ExpositionServiceView.as_view()), + + # expo list + url(r'expo/(?P\d+)/(?P.*)/page/(?P\d+)/$', ExpoList.as_view()), + url(r'expo/(?P\d+)/page/(?P\d+)/$', ExpoList.as_view()), + url(r'expo/(?P\d+)/(?P.*)/$', ExpoList.as_view()), + url(r'expo/(?P\d+)/$', ExpoList.as_view()), + url(r'expo/page/(?P\d+)/$', ExpoList.as_view()), + # expo page + url(r'expo/(?P.*)/$', ExpoDetail.as_view()),# event + url(r'expo/$', ExpoList.as_view()), + + + url(r'expo/add-note/(?P.*)/$', 'exposition.views.add_note'), + + + + + + #url(r'expo/(?P.*)/service/(?P.*)/$', ExpositionServiceView.as_view()), + + #url(r'expo/(?P.*)/service/(?P.*)/$', ExpositionServiceView.as_view()), + #url(r'expo/(?P.*)/statistic/$', ExpositionStatistic.as_view()), url(r'exposition-add-calendar/(?P\d+)/$', 'exposition.views.exposition_add_calendar'), url(r'exposition-visit/(?P\d+)/$', 'exposition.views.exposition_visit'), diff --git a/exposition/views.py b/exposition/views.py index 999ba1af..4c019d58 100644 --- a/exposition/views.py +++ b/exposition/views.py @@ -1,54 +1,63 @@ # -*- coding: utf-8 -*- +import json +import datetime from django.http import HttpResponseRedirect, HttpResponse from django.contrib import messages -#models -from models import Exposition -from functions.custom_views import ExpoListView, ExpoMixin, EventDetail, single_page_filter -from django.views.generic import ListView, DetailView, FormView -from haystack.query import EmptySearchQuerySet -from functions.search_forms import ExpositionSearchForm -from service.views import order_forms +from django.contrib.contenttypes.models import ContentType +from django.conf import settings +from django.views.generic import ListView, DetailView +from django.utils.translation import ugettext as _ from django.shortcuts import get_object_or_404 from django.http import Http404 from django.utils import translation -# +#models +from models import Exposition +from service.views import order_forms from service.models import Service from country.models import Country from city.models import City -from company.models import Company from theme.models import Theme, Tag -import json -from django.utils.translation import ugettext as _ +from note.models import Note +from functions.custom_views import ExpoSearchView +from functions.search_forms import ExpositionSearchForm +from functions.custom_views import ExpoSearchView -class ExpositionByCountry(ListView): - model = Country - template_name = 'exposition/exposition_by_country.html' - def get_queryset(self): - lang = translation.get_language() - return self.model.objects.select_related('exposition_country')\ - .filter(exposition_country__country__isnull=False, translations__language_code=lang)\ - .order_by('translations__name').distinct() class ExpositionBy(ListView): template_name = 'exposition/exposition_by.html' title1 = '' title2 = '' + """ abstact class """ def get_context_data(self, **kwargs): context = super(ExpositionBy, self).get_context_data(**kwargs) - context.update({'title1': self.title1, 'title2': self.title2}) + context.update({'title1': self.title1, 'title2': self.title2, 'catalog': self.catalog}) return context +class ExpositionByCountry(ExpositionBy): + model = Country + title1 = _(u'По странам') + title2 = _(u'Выставки мира по странам') + catalog = 'country/' + + def get_queryset(self): + lang = translation.get_language() + return self.model.objects.select_related('exposition_country')\ + .filter(exposition_country__country__isnull=False, translations__language_code=lang)\ + .order_by('translations__name').distinct() + + class ExpositionByTheme(ExpositionBy): model = Theme title1 = _(u'По тематикам') title2 = _(u'Выставки мира по тематикам') + catalog = 'theme/' def get_queryset(self): lang = translation.get_language() @@ -57,12 +66,11 @@ class ExpositionByTheme(ExpositionBy): .order_by('translations__name').distinct() - - class ExpositionByCity(ExpositionBy): model = City title1 = _(u'По городам') title2 = _(u'Выставки мира по городам') + catalog = 'city/' def get_queryset(self): lang = translation.get_language() @@ -70,259 +78,346 @@ class ExpositionByCity(ExpositionBy): .filter(exposition_city__city__isnull=False, translations__language_code=lang)\ .order_by('translations__name').distinct() -class ExpositionServiceView(FormView, ExpoMixin): - params = None + +class ExpositionSearchView(ExpoSearchView): + #paginate_by = 10 + template_name = 'exposition/search.html' search_form = ExpositionSearchForm model = Exposition - obj = None - service = None - def get_form_class(self): - url = self.kwargs.get('url') - form = order_forms.get(url) - if not form: - raise Http404 +def exposition_add_calendar(request, id): + args = {'success': False} + user = request.user - service = get_object_or_404(Service, url=url) - self.service = service - self.template_name = service.template - params = self.get_params() - for param in params: + if user.is_authenticated(): + exp = Exposition.objects.safe_get(id=id) + if exp in user.calendar.get_expos(): + user.calendar.expositions.remove(exp) + args['in'] = False + else: + user.calendar.expositions.add(exp) + args['in'] = True + args['success'] = True + else: + args['not_authorized'] = True + args['success'] = True + + return HttpResponse(json.dumps(args), content_type='application/json') - if param.get('type') == 'country': - country = Country.objects.safe_get(url=param.get('url')) - if country: - param['name'] = country.name +def exposition_visit(request, id): + args = {'success': False} + user = request.user + if user.is_authenticated(): + exp = Exposition.objects.safe_get(id=id) + if user in exp.users.all(): + exp.users.remove(user) + args['in'] = False + else: + exp.users.add(user) + args['in'] = True - if param.get('type') == 'city': - city = City.objects.safe_get(url=param.get('url')) - if city: - param['name'] = city.name + args['success'] = True + """ + if exp: + exp.users.add(user) + args['success'] = True + """ - if param.get('type') == 'theme': - theme = Theme.objects.safe_get(url=param.get('url')) - if theme: - param['name'] = theme.name + else: + args['not_authorized'] = True + args['success'] = True - if param.get('type') == 'tag': - tag = Tag.objects.safe_get(url=param.get('url')) - if tag: - param['name'] = tag.name + return HttpResponse(json.dumps(args), content_type='application/json') +#------------------------------------------------------------------------------ - if param.get('type') == 'year': - param['name'] = param.get('url') - if param.get('type') == 'month': - monthes = {'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6, - 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12} +class ExpoDetail(DetailView): + model = Exposition + slug_field = 'url' + template_name = 'client/exposition/exposition_detail.html' - param['name'] = param.get('url') +class ExpositionProgramme(DetailView): + model = Exposition + slug_field = 'url' + template_name = 'client/exposition/programm.html' - if param.get('type') == 'member' and self.model != Company: - param['name'] = param.get('url') - company = Company.objects.safe_get(url=param.get('url')) - if company: - param['name'] = company.name - if param.get('type') == single_page_filter.get(self.model): - try: - self.obj = self.model.objects.get(url=param.get('url')) +class ExpositionPrice(DetailView): + model = Exposition + slug_field = 'url' + template_name = 'client/exposition/price.html' - except self.model.DoesNotExist: - raise Http404 - param['name'] = self.obj.name +class ExpositionStatistic(DetailView): + model = Exposition + slug_field = 'url' + template_name = 'client/exposition/statistic.html' - params.append({'type':'service', 'name':service.name}) - self.params = params - return form +from django.views.generic.edit import FormMixin, ModelFormMixin +class ExpositionServiceView(FormMixin, DetailView): + model = Exposition + slug_field = 'url' + service = None + + 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(ExpositionServiceView, self).get_context_data(**kwargs) - context['search_form'] = ExpositionSearchForm() - context['search_action'] = '/expositions/search/' - context['event'] = self.obj - context['service'] = self.service - context['filter'] = self.params + 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): - form.save() + order = form.save(commit=False) + order.exposition = self.object + order.save() messages.success(self.request, _(u'Ваш запрос был успешно отправлен')) return HttpResponseRedirect(self.request.path) - #def form_invalid(self, form): - # return HttpResponse(form.errors) + 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 ''} -class ExpositionSearchView(ListView): - paginate_by = 10 - template_name = 'exposition/search.html' - search_form = ExpositionSearchForm + return initial + else: + return self.initial.copy() + + +class ExpoList(ListView): model = Exposition + paginate_by = settings.CLIENT_PAGINATION + template_name = 'client/exposition/exposition_list.html' + search_form = ExpositionSearchForm + catalog_url = '/expo/' + year = None + month = None def get_queryset(self): - - if self.request.GET: - form = self.search_form(self.request.GET) - if form.is_valid(): - return form.search() - else: - return EmptySearchQuerySet() + 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: - return EmptySearchQuerySet() + qs = self.model.enable.all() - def get_context_data(self, **kwargs): - context = super(ExpositionSearchView, self).get_context_data(**kwargs) + 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)} - context['search_form'] = ExpositionSearchForm(self.request.GET) - queries = self.request.GET.copy() - if queries.has_key('page'): - del queries['page'] - context['queries'] = queries - context['search_action'] = '/expo/search/' + 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(ExpoList, 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 +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'Декабрь')}} -class ExpositionView(ExpoListView): +class ExpoCatalog(ListView): model = Exposition - template_name = 'event_catalog.html' + paginate_by = settings.CLIENT_PAGINATION + template_name = 'exposition/catalog.html' search_form = ExpositionSearchForm - order = 'data_begin' + filter_object = None + year = None + month = 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 + 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']) + 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(ExpositionView, self).get_context_data(**kwargs) - context['search_action'] = '/expositions/search/' + context = super(ExpoCatalog, 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 ExpositionVisitors(ExpositionView): +class ExpoCountryCatalog(ExpoCatalog): + catalog_url = '/expo/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 ExpoCityCatalog(ExpoCatalog): + catalog_url = '/expo/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 ExpoThemeCatalog(ExpoCatalog): + catalog_url = '/expo/theme/' + def get_filtered_qs(self): + #this method used in parent get_queryset + slug = self.kwargs.get('slug') + theme = get_object_or_404(Theme, url=slug) + qs = self.model.enable.filter(theme=theme) + self.filter_object = theme + return qs + + +class ExpoTagCatalog(ExpoCatalog): + catalog_url = '/expo/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 ExpoVisitors(ListView): + paginate_by = settings.CLIENT_PAGINATION model = Exposition - template_name = 'event_visitors.html' + #template_name = 'event_visitors.html' - def get_queryset(self): + template_name = 'client/exposition/visitors.html' + + obj = None + search_form = ExpositionSearchForm - params = self.get_params() - for param in params: - if param.get('type') == 'event': - exp = Exposition.objects.safe_get(url=param.get('url')) - #query = exp.users - param['name'] = exp.name - params.append({'type':'visitors', 'name':_(u'Посетители')}) + def get_queryset(self): + slug = self.kwargs.get('slug') + exposition = get_object_or_404(self.model, url=slug) + self.obj = exposition + return exposition.users.all() - self.params = params - return exp.users.all() + def get_context_data(self, **kwargs): + context = super(ExpoVisitors, self).get_context_data(**kwargs) + context['object'] = self.obj + context['search_form'] = self.search_form + return context -class ExpositionMembers(ExpoListView): +class ExpoMembers(ListView): + paginate_by = settings.CLIENT_PAGINATION model = Exposition - template_name = 'event_members.html' + #template_name = 'event_visitors.html' - def get_queryset(self): + template_name = 'client/exposition/members.html' - params = self.get_params() - for param in params: - if param.get('type') == 'event': - exp = Exposition.objects.safe_get(url=param.get('url')) - param['name'] = exp.name - #query = exp.users - params.append({'type':'members', 'name':_(u'Участники')}) - self.params = params - return exp.company.all() - -class ExpositionStatistic(ExpoListView): - model = Exposition - template_name = 'exposition_statistic.html' - def get_queryset(self): - params = self.get_params() - for param in params: - if param.get('type') == 'event': - exp = Exposition.objects.filter(url=param.get('url')) - param['name'] = exp[0].name - #query = exp.users - self.single_page = True - params.append({'type':'statistic', 'name':_(u'Статистика')}) - self.params = params - return exp - -class ExpositionProgramme(ExpoListView): - model = Exposition - template_name = 'exposition_programm.html' - def get_queryset(self): - params = self.get_params() - for param in params: - if param.get('type') == 'event': - exp = Exposition.objects.filter(url=param.get('url')) - param['name'] = exp[0].name - #query = exp.users - self.single_page = True - params.append({'type':'programme', 'name':_(u'Программа')}) - self.params = params - return exp - -class ExpositionPrice(ExpoListView): - model = Exposition - template_name = 'exposition_price.html' - def get_queryset(self): - params = self.get_params() - for param in params: - if param.get('type') == 'event': - exp = Exposition.objects.filter(url=param.get('url')) - param['name'] = exp[0].name - #query = exp.users - self.single_page = True - params.append({'type':'price', 'name':_(u'Стоимость посещения и участия')}) - self.params = params - return exp + obj = None + search_form = ExpositionSearchForm + def get_queryset(self): + slug = self.kwargs.get('slug') + exposition = get_object_or_404(self.model, url=slug) + self.obj = exposition + return exposition.company.all() + def get_context_data(self, **kwargs): + context = super(ExpoMembers, self).get_context_data(**kwargs) + context['object'] = self.obj + context['search_form'] = self.search_form + return context -def exposition_add_calendar(request, id): +def add_note(request, slug): args = {'success': False} - user = request.user - - if user.is_authenticated(): - exp = Exposition.objects.safe_get(id=id) - if exp in user.calendar.expositions.all(): - user.calendar.expositions.remove(exp) - args['in'] = False - else: - user.calendar.expositions.add(exp) - args['in'] = True - args['success'] = True - else: - args['not_authorized'] = True - args['success'] = True - return HttpResponse(json.dumps(args), content_type='application/json') + if request.GET: + text = request.GET['note_text'] + try: + e = Exposition.objects.get(url=slug) + except Exposition.DoesNotExist: + raise Http404 -def exposition_visit(request, id): - args = {'success': False} - user = request.user - if user.is_authenticated(): - exp = Exposition.objects.safe_get(id=id) - if user in exp.users.all(): - exp.users.remove(user) - args['in'] = False + ct = ContentType.objects.get_for_model(e) + object_id = e.id + user = request.user + 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: - exp.users.add(user) - args['in'] = True + Note.objects.create(content_type=ct, object_id=object_id, user=user, text=text) + + user.calendar.expositions.add(e) - args['success'] = True - """ - if exp: - exp.users.add(user) - args['success'] = True - """ - else: - args['not_authorized'] = True args['success'] = True + args['text'] = text return HttpResponse(json.dumps(args), content_type='application/json') \ No newline at end of file diff --git a/file/forms.py b/file/forms.py index db550935..8816ec37 100644 --- a/file/forms.py +++ b/file/forms.py @@ -12,6 +12,73 @@ from functions.translate import populate, fill_trans_fields_all from PIL import Image import pytils, re + + +class FileForm(forms.Form): + file_path = forms.FileField(label='Выберите файл') + model = forms.CharField(required=False, widget=forms.HiddenInput()) + purposes = [('scheme teritory','Схема територии'),('preview','Превью')] + purpose = forms.ChoiceField(label='Назаначение', choices=purposes) + + def __init__(self, *args, **kwargs): + """ + creates dynamical translated fields + """ + super(FileForm, self).__init__(*args, **kwargs) + # creates translated form fields, example: name_ru, name_en + # len(10) is a hack for detect if settings.LANGUAGES is not configured it return all langs + if len(settings.LANGUAGES) in range(10): + for lid, (code, name) in enumerate(settings.LANGUAGES): + # using enumerate for detect iteration number + # first iteration is a default lang so it required fields + required = True if lid == 0 else False + self.fields['file_name_%s'%code] = forms.CharField(label='Имя файла',required=False, widget=forms.TextInput(attrs={'placeholder': 'Имя'})) + self.fields['description_%s'%code] = forms.CharField(label='Описание', required=False, widget=forms.Textarea()) + + def save(self, request, obj=None): + """ + + """ + data = self.cleaned_data + if not obj: + return None + else: + file_obj = FileModel() + file_obj.content_type = ContentType.objects.get_for_model(obj) + file_obj.object_id = getattr(obj, 'id') + + #change all symbols than are not letter or number to '_'from file name + # and translit name + file_name = u'%s'%request['file_path'].name + file_name = pytils.translit.translify(file_name) + file_name = re.sub('[^\w\-_\.]', '_', file_name) + file_name = re.sub('_+', '_', file_name) + #file_name = re.sub('_+', '_', re.sub('[^\w\-_\. ]', '_', pytils.translit.translify(u'%s'%request['file_path'].name))) + request['file_path'].name = file_name + + #------- + file_obj.file_path = request['file_path'] + + file_obj.purpose = data['purpose'] + #type of file + type = str(data['file_path']).split('.')[-1] + #if type is image save fields with image size + if type.upper() in IMG_TYPES: + f = Image.open(data['file_path']) + file_obj.img_width, file_obj.img_height = f.size + #saves file_type + try: + file_obj.file_type = type + file_obj.save() + except: + file_obj.file_type = 'OTHER' + file_obj.save() + + #fills and saves translated fields + fill_trans_fields_all(FileModel, file_obj, data) + + return file_obj + class FileModelForm(forms.Form): """ Create FileModel form diff --git a/functions/admin_forms.py b/functions/admin_forms.py index 83878772..e1a78da9 100644 --- a/functions/admin_forms.py +++ b/functions/admin_forms.py @@ -23,7 +23,9 @@ class AdminFilterForm(forms.Form): class for filtering lists in admin panel """ model = None # which models work with - name = forms.CharField(label=_(u'Имя'), required=False) + exact_name = forms.CharField(label=_(u'Название'), required=False) + name = forms.CharField(label=_(u'Часть названия'), required=False) + def filter(self): """ @@ -32,11 +34,16 @@ class AdminFilterForm(forms.Form): form must be cleaned before calling this method """ + model = self.model data = self.cleaned_data name = data['name'] - model = self.model + exact_name = data['exact_name'] + if exact_name: + qs = model.objects.filter(translations__name=name).distinct() + return qs + qs = model.objects.all() if name: - qs = qs.filter(translations__name__contains=name) + qs = qs.filter(translations__name__contains=name).distinct() return qs \ No newline at end of file diff --git a/functions/admin_views.py b/functions/admin_views.py index 3cadc997..5a0f1802 100644 --- a/functions/admin_views.py +++ b/functions/admin_views.py @@ -10,11 +10,15 @@ class AdminView(FormView): def set_obj(self): url = self.kwargs.get('url') - - #rewrwe + # if url: - obj = get_object_or_404(self.model, url=url) - self.obj = obj + try: + obj = self.model.objects.get(url=url) + self.obj = obj + except: + raise Http404 + pass + # obj = get_object_or_404(self.model, url=url) return obj slug = self.kwargs.get('slug') if slug: @@ -117,7 +121,7 @@ class AdminListView(FormView): def get_context_data(self, **kwargs): context = super(AdminListView, self).get_context_data(**kwargs) - qs = self.model.objects.all() + qs = self.model.objects.language().all().order_by('name') result = paginate_results(qs, page=self.request.GET.get('page')) context['object_list'] = result return context diff --git a/functions/custom_views.py b/functions/custom_views.py index a9f9ebf2..cce1e3f4 100644 --- a/functions/custom_views.py +++ b/functions/custom_views.py @@ -221,7 +221,8 @@ single_page_filter = {Exposition:'event', Conference:'event', Seminar:'event', W - +from meta.models import MetaSetting +from meta.views import Meta class ExpoListView(ExpoMixin, ListView): """ """ @@ -299,6 +300,13 @@ class ExpoListView(ExpoMixin, ListView): context['filter'] = self.params context['single_page'] = self.single_page context['search_form'] = self.search_form + if self.single_page: + s = MetaSetting.objects.get(name='expo') + obj = context['object_list'][0] + + params = s.generate_meta(obj) + m = Meta(**params) + context['meta'] = m return context from country.models import Country @@ -306,4 +314,42 @@ from country.models import Country class EventDetail(ExpoMixin, DetailView): def get_object(self, queryset=None): obj = Country.objects.filter()[0] - return obj \ No newline at end of file + return obj + + +from haystack.query import EmptySearchQuerySet + +class ExpoSearchView(ListView): + paginate_by = 10 + template_name = None + search_form = None + model = None + + def get_queryset(self): + + if self.request.GET: + form = self.search_form(self.request.GET) + if form.is_valid(): + + return form.search() + else: + return EmptySearchQuerySet() + else: + return EmptySearchQuerySet() + + + def get_context_data(self, **kwargs): + context = super(ExpoSearchView, self).get_context_data(**kwargs) + form = self.search_form(self.request.GET) + if self.request.GET: + if form.is_valid(): + form.data_with_parents = form.get_form_data() + + + context['search_form'] = form + queries = self.request.GET.copy() + if queries.has_key('page'): + del queries['page'] + context['queries'] = queries + + return context \ No newline at end of file diff --git a/functions/form_check.py b/functions/form_check.py index b815c239..61c7cab3 100644 --- a/functions/form_check.py +++ b/functions/form_check.py @@ -15,7 +15,12 @@ def is_positive_integer(data, else: raise ValidationError(msg) + +from slugify import slugify + def translit_with_separator(string, separator='-'): + + return slugify(string, to_lower=True) """ Trsanslit string and replace "bad" symbols for separator diff --git a/functions/model_mixin.py b/functions/model_mixin.py index 59ced194..7f5950c6 100644 --- a/functions/model_mixin.py +++ b/functions/model_mixin.py @@ -4,9 +4,9 @@ from service.models import Service class ExpoMixin(object): - def get_index_text(self): - names = [tr.name for tr in self.translations.all()] - return names +# def get_index_text(self): +# names = [tr.name for tr in self.translations.all()] +# return names def get_logo(self): @@ -29,9 +29,12 @@ class ExpoMixin(object): class EventMixin(object): def get_permanent_url(self): - url = '%sevent-%s'%(self.get_catalog_url(), self.url) + url = '%s%s/'%(self.get_catalog_url(), self.url) return url + + + def get_logo(self): return self.logo """ @@ -75,9 +78,8 @@ class EventMixin(object): def get_services(self): ids = [item for item, bool in self.country.services if bool==True] - return [Service.objects.get(url=id) for id in ids] - - + ##sasa + return list(Service.objects.language().filter(url__in=ids).order_by('sort')) def duration_days(self, month=None): if not month: diff --git a/functions/search_forms.py b/functions/search_forms.py index e15c6ae2..ed0c58e8 100644 --- a/functions/search_forms.py +++ b/functions/search_forms.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- import pytils +import ast +import json from django import forms from django.db.models import Q +from django.utils import translation from django.utils.translation import ugettext_lazy as _ from haystack.query import EmptySearchQuerySet, SearchQuerySet - +from country.models import Area +from theme.models import Theme from exposition.models import Exposition from conference.models import Conference from seminar.models import Seminar @@ -13,127 +17,86 @@ from company.models import Company from theme.models import Theme, Tag from country.models import Country from city.models import City -import ast +from place_exposition.models import PlaceExposition +from place_conference.models import PlaceConference class AbstactSearchForm(forms.Form): - q = forms.CharField(label=_(u'Поиск'), required=False) + q = forms.CharField(label=_(u'Я ищу'), required=False) w = forms.CharField(label=_(u'Где'), required=False) + models = None + data_with_parents = None + def get_form_data(self): + data = self.cleaned_data + area = data.get('area') + co = data.get('co') + ci = data.get('ci') + th = data.get('th') + tg = data.get('tg') + if area: + ar = Area.objects.filter(id__in=area) + areas = [{'name':'area', 'id':item.id, 'parent':None, 'text':item.name} for item in ar] + else: + areas = [] - -class CompanySearchForm(AbstactSearchForm): - th = forms.MultipleChoiceField(label=_(u'Тематика'), required=False, - choices=[(theme.id, theme.name) for theme in Theme.objects.all()]) - tg = forms.MultipleChoiceField(label=_(u'Теги'), required=False, - choices=[(tag.id, tag.name) for tag in Tag.objects.all()]) - - - c = forms.CharField(label=_(u'Страна'), required=False, widget=forms.SelectMultiple()) - city = forms.CharField(label=_(u'город'), required=False, widget=forms.SelectMultiple()) - - def clean_city(self): - - city = self.cleaned_data.get('city') - if city: - res = ast.literal_eval(city) - return res - return city - - def clean_c(self): - country = self.cleaned_data.get('c') - if country: - res = ast.literal_eval(country) - return res - return country - - - - def search(self): - q = self.cleaned_data.get('q') - w = self.cleaned_data.get('w') - th = self.cleaned_data.get('th') - tg = self.cleaned_data.get('tg') - c = self.cleaned_data.get('c') - city = self.cleaned_data.get('city') - - sqs = SearchQuerySet().models(Company) - - if q: - - sqs = sqs.auto_query(q) - - if w: - sqs = sqs.filter(where__contains=w) if th: + themes = Theme.objects.filter(id__in=th) + ths = [{'name':'th', 'id':item.id, 'parent':None, 'text':item.name} for item in themes] + else: + ths = [] - sqs = sqs.filter(theme__in=th) if tg: - sqs = sqs.filter(tag__in=tg) - if c: - sqs = sqs.filter(country__in=c) - if city: - sqs = sqs.filter(country__in=c) - - - - return sqs - - -from country.models import Area -from theme.models import Theme -from django.utils import translation - -class ExpositionSearchForm(forms.Form): - search_url = '/expo/search/' - q = forms.CharField(label=_(u'Поиск'), required=False) - w = forms.CharField(label=_(u'Где'), required=False) - - th = forms.MultipleChoiceField(label=_(u'Тематика'), choices=[(item.id, item.name) for item in Theme.objects.all()], - required=False, widget=forms.CheckboxSelectMultiple()) - tg = forms.CharField(label=_(u'Теги'), required=False, widget=forms.CheckboxSelectMultiple()) - - area = forms.MultipleChoiceField(label=_(u'Регион'), choices=[(item.id, item.name) for item in Area.objects.all_sorted()], - required=False, widget=forms.CheckboxSelectMultiple()) - co = forms.MultipleChoiceField(label=_(u'Страна'), required=False, widget=forms.CheckboxSelectMultiple(), - choices=[(item.id, item.name) for item in Country.objects.select_related('exposition_country')\ - .filter(exposition_country__country__isnull=False, translations__language_code=translation.get_language())\ - .order_by('translations__name').distinct()] - ) - - ci = forms.MultipleChoiceField(label=_(u'Город'), required=False, widget=forms.CheckboxSelectMultiple(), - choices=[(item.id, item.name) for item in City.objects.select_related('exposition_city')\ - .filter(exposition_city__city__isnull=False, translations__language_code=translation.get_language())\ - .order_by('translations__name').distinct()] - ) - - fr = forms.DateField(required=False, - widget=forms.DateInput(attrs={'class': 'date', 'id': 'dateFrom', - 'placeholder': _(u'дд.мм.гггг')})) - to = forms.DateField(required=False, - widget=forms.DateInput(attrs={'class': 'date', 'id': 'dateTo', - 'placeholder': _(u'дд.мм.гггг')})) - - def __init__(self, *args, **kwargs): - super(ExpositionSearchForm, self).__init__(*args, **kwargs) - self.theme_classes = {item.id:item.generate_search_class() for item in Theme.objects.all()} - """ - theme_with_tags = {} - - for tag in list(Tag.objects.language().filter()): - if tag.theme_id in theme_with_tags: - theme_with_tags[tag.theme_id].append({'name': tag.name, 'id':tag.id}) - else: - theme_with_tags[tag.theme_id] = [{'name': tag.name, 'id':tag.id}] - + tgs = [] + for item in tg: + try: + tag = Tag.objects.language().get(id=item) + tgs.append({'name':'th', 'id':tag.theme_id, 'text': tag.theme.name, 'children':{ + 'id': item, 'name':'tg', 'text': tag.name + } + }) + except: + continue + else: + tgs = [] - #dsds - self.theme_with_tags = theme_with_tags - """ + if co: + cos = [] + for item in co: + try: + country = Country.objects.get(id=item) + cos.append({'name':'area', 'id':country.area_id, 'text': country.area.name, 'children':{ + 'id': item, 'name':'co', 'text': country.name + } + }) + except: + continue + else: + cos = [] + if ci: + cis = [] + for item in ci: + try: + city = City.objects.language().get(id=item) + + cis.append({'name':'area', 'id': city.country.area_id, 'text': city.country.area.name, 'children':{ + 'id': city.country_id, 'name':'co', 'text': city.country.name, 'children':{ + 'name':'ci', 'id':item, 'text':city.name + } + } + }) + except: + continue + else: + cis = [] + finale_list = areas + cos + cis + ths + tgs + result = {'inputs': finale_list} + result = json.dumps(result) + return result def get_places_display(self): @@ -192,13 +155,117 @@ class ExpositionSearchForm(forms.Form): res = ast.literal_eval(tg) return res return tg + """ + def clean_co(self): + + co = self.cleaned_data.get('co') + if co: + res = ast.literal_eval(co) + return res + return co + """ + def search(self): + + if not self.is_valid(): + return EmptySearchQuerySet() + q = self.cleaned_data.get('q') + w = self.cleaned_data.get('w') + fr = self.cleaned_data.get('fr') + to = self.cleaned_data.get('to') + th = self.cleaned_data.get('th') + tg = self.cleaned_data.get('tg') + co = self.cleaned_data.get('co') + ci = self.cleaned_data.get('ci') + area = self.cleaned_data.get('area') + + sqs = SearchQuerySet().models(*self.models).all() + + if q: + sqs = sqs.autocomplete(content_auto=q) + if w: + sqs = sqs.filter(where__contains=w) + if fr: + sqs = sqs.filter(data_begin__gte=fr) + if to: + sqs = sqs.filter(data_begin__lte=to) + if th: + sqs = sqs.filter(theme__in=th) + if tg: + sqs = sqs.filter(tag__in=tg) + + place_filter = None + if area: + if place_filter: + place_filter = place_filter | Q(area_id__in=area) + else: + place_filter = Q(area_id__in=area) + #sqs = sqs.filter(area_id__in=area) + if co: + if place_filter: + place_filter = place_filter | Q(country_id__in=co) + else: + place_filter = Q(country_id__in=co) + + #sqs = sqs.filter(country_id__in=co) + if ci: + if place_filter: + place_filter = place_filter | Q(city_id__in=ci) + else: + place_filter = Q(city_id__in=ci) + + #sqs = sqs.filter(city_id__in=ci) + if place_filter: + sqs = sqs.filter(place_filter) + + return sqs#.order_by('data_begin') + + + + + +class ExpositionSearchForm(AbstactSearchForm): + search_url = '/expo/search/' + autocomplete_url = '/search-form/autosearch/exposition/' + title = _(u'ПОИСК СОБЫТИЙ') + models = [Exposition] + + th = forms.MultipleChoiceField(label=_(u'Тематика'), choices=[(item.id, item.name) for item in Theme.active.all()], + required=False, widget=forms.CheckboxSelectMultiple()) + tg = forms.CharField(label=_(u'Теги'), required=False, widget=forms.CheckboxSelectMultiple()) + #co = forms.CharField(label=_(u'Страна'), required=False, widget=forms.CheckboxSelectMultiple()) + #tg = forms.CharField(label=_(u'Теги'), required=False, widget=forms.CheckboxSelectMultiple()) + area = forms.MultipleChoiceField(label=_(u'Регион'), choices=[(item.id, item.name) for item in Area.objects.all_sorted()], + required=False, widget=forms.CheckboxSelectMultiple()) + co = forms.MultipleChoiceField(label=_(u'Страна'), required=False, widget=forms.CheckboxSelectMultiple(), + choices=[(item.id, item.name) for item in Country.objects.select_related('exposition_country')\ + .filter(exposition_country__country__isnull=False, translations__language_code=translation.get_language())\ + .order_by('translations__name').distinct()] + ) + + ci = forms.MultipleChoiceField(label=_(u'Город'), required=False, widget=forms.CheckboxSelectMultiple(), + choices=[(item.id, item.name) for item in City.objects.select_related('exposition_city')\ + .filter(exposition_city__city__isnull=False, translations__language_code=translation.get_language())\ + .order_by('translations__name').distinct()] + ) + + fr = forms.DateField(required=False, + widget=forms.DateInput(attrs={'class': 'date', 'id': 'dateFrom', + 'placeholder': _(u'дд.мм.гггг')})) + to = forms.DateField(required=False, + widget=forms.DateInput(attrs={'class': 'date', 'id': 'dateTo', + 'placeholder': _(u'дд.мм.гггг')})) + + def __init__(self, *args, **kwargs): + super(ExpositionSearchForm, self).__init__(*args, **kwargs) + self.theme_classes = {item.id:item.generate_search_class() for item in Theme.objects.all()} def search(self): if not self.is_valid(): return EmptySearchQuerySet() + data = self.cleaned_data q = self.cleaned_data.get('q') w = self.cleaned_data.get('w') fr = self.cleaned_data.get('fr') @@ -212,7 +279,7 @@ class ExpositionSearchForm(forms.Form): sqs = SearchQuerySet().models(Exposition).all() if q: - sqs = sqs.auto_query(q) + sqs = sqs.autocomplete(content_auto=q) if w: sqs = sqs.filter(where__contains=w) if fr: @@ -226,7 +293,6 @@ class ExpositionSearchForm(forms.Form): place_filter = None - if area: if place_filter: place_filter = place_filter | Q(area_id__in=area) @@ -250,12 +316,94 @@ class ExpositionSearchForm(forms.Form): if place_filter: sqs = sqs.filter(place_filter) + return sqs.order_by('data_begin') + + +class PlaceSearchForm(AbstactSearchForm): + search_url = '/places/search/' + autocomplete_url = '/search-form/autosearch/place/' + title = _(u'ПОИСК МЕСТ') + models = [PlaceExposition, PlaceConference] + # place fields + area = forms.MultipleChoiceField(label=_(u'Регион'), choices=[(item.id, item.name) for item in Area.objects.all_sorted()], + required=False, widget=forms.CheckboxSelectMultiple()) + co = forms.MultipleChoiceField(label=_(u'Страна'), required=False, widget=forms.CheckboxSelectMultiple(), + choices=[(item.id, item.name) for item in Country.objects.select_related('exposition_country')\ + .filter(exposition_country__country__isnull=False, translations__language_code=translation.get_language())\ + .order_by('translations__name').distinct()] + ) + + ci = forms.MultipleChoiceField(label=_(u'Город'), required=False, widget=forms.CheckboxSelectMultiple(), + choices=[(item.id, item.name) for item in City.objects.select_related('exposition_city')\ + .filter(exposition_city__city__isnull=False, translations__language_code=translation.get_language())\ + .order_by('translations__name').distinct()] + ) + # + place_type = forms.MultipleChoiceField(label=_(u'Тип'), required=False, choices=[]) + def get_place_type_display(self): + return _(u'Не важно') + def search(self): + pass - return sqs.order_by('data_begin') +class CompanySearchForm(AbstactSearchForm): + search_url = '/members/search/' + autocomplete_url = '/search-form/autosearch/company/' + title = _(u'ПОИСК УЧАСТНИКОВ') + models = [Company] + th = forms.MultipleChoiceField(label=_(u'Тематика'), choices=[(item.id, item.name) for item in Theme.objects.all()], + required=False, widget=forms.CheckboxSelectMultiple()) + tg = forms.CharField(label=_(u'Теги'), required=False, widget=forms.CheckboxSelectMultiple()) + + area = forms.MultipleChoiceField(label=_(u'Регион'), choices=[(item.id, item.name) for item in Area.objects.all_sorted()], + required=False, widget=forms.CheckboxSelectMultiple()) + co = forms.MultipleChoiceField(label=_(u'Страна'), required=False, widget=forms.CheckboxSelectMultiple(), + choices=[(item.id, item.name) for item in Country.objects.select_related('exposition_country')\ + .filter(exposition_country__country__isnull=False, translations__language_code=translation.get_language())\ + .order_by('translations__name').distinct()] + ) + + ci = forms.MultipleChoiceField(label=_(u'Город'), required=False, widget=forms.CheckboxSelectMultiple(), + choices=[(item.id, item.name) for item in City.objects.select_related('exposition_city')\ + .filter(exposition_city__city__isnull=False, translations__language_code=translation.get_language())\ + .order_by('translations__name').distinct()] + ) + + def __init__(self, *args, **kwargs): + super(CompanySearchForm, self).__init__(*args, **kwargs) + self.theme_classes = {item.id:item.generate_search_class() for item in Theme.objects.all()} + + def search(self): + q = self.cleaned_data.get('q') + w = self.cleaned_data.get('w') + th = self.cleaned_data.get('th') + tg = self.cleaned_data.get('tg') + c = self.cleaned_data.get('c') + city = self.cleaned_data.get('city') + + sqs = SearchQuerySet().models(Company) + + if q: + + sqs = sqs.auto_query(q) + + if w: + sqs = sqs.filter(where__contains=w) + if th: + + sqs = sqs.filter(theme__in=th) + if tg: + sqs = sqs.filter(tag__in=tg) + if c: + sqs = sqs.filter(country__in=c) + if city: + sqs = sqs.filter(country__in=c) + + return sqs + class EventSearchForm(forms.Form): q = forms.CharField(label=_(u'Поиск'), required=False) @@ -283,5 +431,4 @@ class EventSearchForm(forms.Form): if w: sqs = sqs.filter(where__contains=w) - return sqs - + return sqs \ No newline at end of file diff --git a/functions/search_mixin.py b/functions/search_mixin.py new file mode 100644 index 00000000..0e4bcd59 --- /dev/null +++ b/functions/search_mixin.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from haystack import indexes + +class ExpoSearchMixin(object): + """ + + """ + + def prepare_where(self, obj): + if obj.country: + country = [tr.name for tr in obj.country.translations.all()] + else: + country = '' + if obj.city: + city = [tr.name for tr in obj.city.translations.all()] + else: + city = '' + + return country + city + + def prepare_content_auto(self, obj): + """ + object must have get_index_text method which generate text for searching + """ + return obj.get_index_text() + + + def prepare_area_id(self, obj): + if obj.country: + return obj.country.area.id + return None + + def prepare_country_id(self, obj): + if obj.country: + return obj.country.id + return None + + def prepare_city_id(self, obj): + if obj.city: + return obj.city.id + return None + + def prepare_theme(self, obj): + return [th.id for th in obj.theme.filter()] + + def prepare_tag(self, obj): + return [th.id for th in obj.tag.filter()] + + + def prepare_url(self, obj): + return obj.get_permanent_url() + + def prepare_name_en(self, obj): + try: + return obj.translations.get(language_code = 'en').name + except: + return '' + + def prepare_name_ru(self, obj): + try: + return obj.translations.get(language_code = 'ru').name + except: + return '' + + diff --git a/import_xls/admin.py b/import_xls/admin.py index 75a50c13..00ebe370 100644 --- a/import_xls/admin.py +++ b/import_xls/admin.py @@ -80,10 +80,16 @@ class ExportPlaceExposition(ExportView): - -class ImportEvent(ImportView): +from exposition.models import Exposition +class ImportEvent(FormView): form_class = ImportEventForm success_url = '/admin/import-event' + def form_valid(self, form): + result = form.save_file() + Exposition.imports.create_by_dict(result[0]) + messages.success(self.request, 'Success') + + return super(ImportView, self).form_valid(form) class ImportTheme(ImportView): form_class = ImportThemeForm diff --git a/import_xls/excel_settings.py b/import_xls/excel_settings.py index b510049b..a6f19251 100644 --- a/import_xls/excel_settings.py +++ b/import_xls/excel_settings.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from accounts.models import User -from country.models import Country -from city.models import City -from theme.models import Theme, Tag -from hvad.utils import get_translation_aware_manager +# bad practice of importing, but to many functions must be imported +from .utils import * def get_bool(value): if value: @@ -15,20 +12,6 @@ def get_int(value): return '' return value -from bitfield import BitHandler -from exposition.models import BIT_AUDIENCE -def get_audience(value): - if isinstance(value, BitHandler): - l = [k for k, v in value.iteritems() if v] - if l: - new_list = [] - for value in l: - for item1, item2 in BIT_AUDIENCE: - if value == item1: - new_list.append(item2) - - return ', '.join(new_list) - return def get_theme(value): objects = value.theme.all() @@ -46,7 +29,6 @@ def get_tag(tag, theme): result += '['+','.join(tag_list)+']' return result -from place_exposition.models import EXPOSITION_TYPE def get_place_type(value): for t in EXPOSITION_TYPE: @@ -136,120 +118,6 @@ field_settings = [ - -def to_int(val): - """ - Reverse function to get_int - return None if value isnt integer - """ - try: - return int(val) - except ValueError: - return None - - - -def to_country(value): - try: - query = get_translation_aware_manager(Country) - country = query.filter(name=value)[0] - return country - except IndexError: - return None - - - -def to_city(value, lang, country): - try: - # get city by name - #objects = get_translation_aware_manager(City) - # except IndexError if no found - city = City.objects.filter(translations__name=value, country=country)[0] - return city - except IndexError: - return None - - -from exposition.models import Exposition -def to_audience(value, model=Exposition): - if value: - l = value.split(', ') - if l: - new_list = [] - for value in l: - for item1, item2 in BIT_AUDIENCE: - if value == item2: - new_list.append(item1) - if new_list: - - return reduce(lambda x,y: x|y, (getattr(model.audience, item) for item in new_list)) - return 0 - - - -def to_theme(obj, value): - if isinstance(value, float) or isinstance(value, int): - if (value - int(value) > 0): - value = str(value) - else: - value = str(int(value)) - theme_ids = value.split('.') - else: - theme_ids = value.split(',') - theme_objects = [] - for id in theme_ids: - try: - theme = Theme.objects.language('ru').get(id=int(id)) - theme_objects.append(theme) - obj.theme.add(theme) - except Theme.DoesNotExist, ValueError: - pass - - - return theme_objects -""" -def to_tag(value, lang, themes): - if value == [""]: - return None - - value = value.replace('[', '') - themes = themes.all() - # list tags by themes - value = value.split(']') - - arr = [] - for theme_number, theme in enumerate(themes): - for val in value[theme_number].split(','): - if val: - try: - tag = Tag.objects.language(lang).get(id=int(val), theme=theme) - arr.append(tag) - except Tag.DoesNotExist, ValueError: - pass - - return arr -""" -def to_tag(obj,value): - if value == [""]: - return None - - names = value.split(',') - tags = [] - for name in names: - objects = get_translation_aware_manager(Tag) - tag = objects.filter(name=name) - #tag = Tag.objects.language('ru').filter(transations__name=name) - if tag: - tags.append(tag[0]) - obj.tag.add(tag[0]) - else: - continue - print(tags) - print(names) - return tags - - - def to_theme_type(st): if not st: return 15 @@ -259,19 +127,9 @@ def to_theme_type(st): flag = reduce(lambda x,y: x|y, (getattr(Theme.types, item) for item in types)) return flag -import time, xlrd -def to_date(value): - if not value: - return None - if isinstance(value, unicode) or isinstance(value, str): - t = time.strptime(value, "%d.%m.%Y") - if isinstance(value, float): - t = xlrd.xldate_as_tuple(value, 0)+(0,0,0) - - return time.strftime("%Y-%m-%d", t) def to_currency(value): if value=='USD': @@ -294,6 +152,7 @@ from django.core.validators import validate_email, URLValidator def to_url(url): validate = URLValidator() + try: validate(url) except: @@ -388,7 +247,7 @@ def to_phone(value): value = value.replace(elem, '') value = to_int(value) - + print(value) return value @@ -465,52 +324,43 @@ place_exp_sett = { u'Мобильное приложение':{u'field': u'mobile_application', u'func': bool}, } -from place_exposition.models import PlaceExposition -def to_place(value): - try: - place = PlaceExposition.objects.get(id=value) - return place - except: - - return None - event_sett = { u'ID':{u'field': u'id', u'func': to_int}, + u'Url':{u'field': u'url', u'func': unicode}, u'Название':{u'field': u'name', u'func': unicode}, u'Краткое описание':{u'field': u'main_title', u'func': unicode}, u'Дата начала:(YYYY-MM-DD)':{u'field': u'data_begin', u'func': to_date}, 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', u'func': to_city, 'extra_values': 'country'}, - 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'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'periodic', 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'logo', u'func': save_file, u'method': True, u'purpose': 'logo'}, u'Официальный веб-сайт':{u'field': u'web_page', 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}, u'Валюта':{u'field': u'currency', u'func': unicode}, - # - u'Посетители_билет (1 день)':{u'field': u'price_day', u'func': to_int}, - u'Посетители_билет (все дни)':{u'field': u'price_all', u'func': to_int}, - u'Условия':{u'field': u'periodic', u'func': unicode}, - #u'Посетители_билет (1 день)':{u'field': u'price_day_bar', u'func': to_int},##?? - #u'Посетители_билет (все дни)':{u'field': u'price_all_bar', u'func': to_int},##?? - #u'Условия':{u'field': u'periodic', u'func': unicode},##?? - #u'Примечание':{u'field': u'periodic', u'func': unicode}, + u'Стоимость билета 1 день':{u'field': u'price_day', u'func': unicode}, + u'Стоимость билета все дни':{u'field': u'price_all', u'func': unicode}, + u'Условия предварительной регистрации':{u'field': u'pre_condition', u'func': unicode}, + u'Стоимость на стойке 1 день':{u'field': u'price_day_bar', u'func': unicode}, + u'Стоимость на стойке все дни':{u'field': u'price_all_bar', u'func': unicode}, + u'Условия регистрации на стойке':{u'field': u'stand_condition', u'func': unicode}, + u'Примечание по посещению':{u'field': u'visit_note', u'func': unicode}, u'Каталог':{u'field': u'price_catalog', u'func': to_int}, u'Налог включен':{u'field': u'tax', u'func': bool}, u'Год основания':{u'field': u'foundation_year', u'func': to_int}, - #u'Данные за год':{u'field': u'periodic', u'func': to_int}, + #???u'Данные за год':{u'field': u'periodic', u'func': to_int}, u'Посетители':{u'field': u'visitors', u'func': to_int}, u'Участники':{u'field': u'members', u'func': to_int}, - #u'Страны':{u'field': u'periodic', u'func': unicode},##?? - u'Площадь':{u'field': u'periodic', u'func': to_int}, + u'Страны':{u'field': u'stat_countries', u'func': unicode}, + u'Площадь':{u'field': u'area', u'func': to_int}, u'Min_Raw кв.м.':{u'field': u'min_closed_area', u'func': to_int}, u'Max_Raw кв.м.':{u'field': u'max_closed_area', u'func': to_int}, u'Min_Pack кв.м.':{u'field': u'min_closed_equipped_area', u'func': to_int}, @@ -518,16 +368,13 @@ event_sett = { u'Открытая площадь':{u'field': u'max_open_area', u'func': to_int}, u'Мин. Площадь кв.м.':{u'field': u'min_open_area', u'func': to_int}, u'Регистрационный взнос':{u'field': u'registration_payment', u'func': to_int}, - #u'Примечание':{u'field': u'periodic', u'func': unicode}, + u'Примечание по участии':{u'field': u'participation_note', u'func': unicode}, u'Крайний срок подачи заявки':{u'field': u'application_deadline', u'func': to_date}, - u'UFI':{u'field': u'quality_label', u'func': bool},## - u'РСВЯ':{u'field': u'quality_label', u'func': bool},## - u'EXPORATING':{u'field': u'quality_label', u'func': bool},## + u'UFI':{u'field': u'quality_label', u'func': check_quality_label, u'bitfield':True, u'label': 'ufi'}, + u'РСВЯ':{u'field': u'quality_label', u'func': check_quality_label, u'bitfield':True, u'label': 'rsva'}, + u'EXPORATING':{u'field': u'quality_label', u'func': check_quality_label, u'bitfield':True, u'label': 'exporating'}, u'Отменена':{u'field': u'canceled', u'func': bool}, u'ExpoHIT':{u'field': u'expohit', u'func': bool}, - u'Фото':{u'field': u'photo', u'func': save_photo, u'method': True}, - - } import_settings={ diff --git a/import_xls/import_forms.py b/import_xls/import_forms.py index d815573b..de1a6aa0 100644 --- a/import_xls/import_forms.py +++ b/import_xls/import_forms.py @@ -209,6 +209,9 @@ class ImportPlaceExpositionForm(ImportForm): class ImportPlaceConferenceForm(ImportForm): model = PlaceConference + +from import_xls.excel_settings import event_sett + class ImportEventForm(ImportForm): """ extended form for importing one type of event @@ -230,13 +233,32 @@ class ImportEventForm(ImportForm): sheet = book.sheet_by_index(0) row_list = [sheet.row_values(row_number) for row_number in range(sheet.nrows)] # all field names in excel file (must be in second row) - field_names = [name for name in row_list[1]] + field_names = [name for name in row_list[0]] # model model = get_model(data['event'].split('.')[0], data['event'].split('.')[1]) + list_dicts = [] + + for row_number, row in enumerate(row_list[1:]): + d = {} + for col_number, cell in enumerate(row): + # through row + field_name = field_names[col_number] + setting = event_sett.get(field_name) + if setting: + d[setting['field']] = {'value':cell} + + + d['lang'] = lang + list_dicts.append(d) + + return list_dicts + + + for row_number, row in enumerate(row_list): # go through all rows in file - if row_number > 1: + if row_number > 0: # first two fields are verbose name and name if row[0] != '': # in first column ids @@ -254,15 +276,27 @@ class ImportEventForm(ImportForm): object = model() object.translate(lang) - + d = {} for col_number, cell in enumerate(row): + # through row + + field_name = field_names[col_number] + setting = import_settings.get(field_name) + d[setting] = cell + + list_dicts.append(d) + + + """ # go through row cells # field name current cell field_name = field_names[col_number] if field_name =='theme': # need save object before saving manytomany field - object.save() + #object.save() setting = import_settings.get(field_name) + d[setting] = cell + if setting is not None: # if setting exist for this field func = setting.get('func') @@ -283,8 +317,10 @@ class ImportEventForm(ImportForm): value = func(cell) if value: setattr(object, field_name, value) + """ - object.save() + #object.save() + sadsa diff --git a/import_xls/model_utils.py b/import_xls/model_utils.py new file mode 100644 index 00000000..8b35be8a --- /dev/null +++ b/import_xls/model_utils.py @@ -0,0 +1,22 @@ +from hvad.models import TranslationManager + + +class ExpoImportManager(TranslationManager): + def create_by_dict(self, d): + model = self.model + id = d['id'] + lang = d['lang'] + if id: + expo = self.language(lang).get(id=id) + else: + expo = model() + expo.translate(lang) + + + aaaa + + # save simple values + # save relations + + + diff --git a/import_xls/utils.py b/import_xls/utils.py new file mode 100644 index 00000000..dec07747 --- /dev/null +++ b/import_xls/utils.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +import urllib2 +import time, xlrd +from django.conf import settings +from django.utils import translation +from hvad.utils import get_translation_aware_manager +from bitfield import BitHandler +from place_exposition.models import PlaceExposition +from exposition.models import Exposition +from country.models import Country +from city.models import City +from theme.models import Theme, Tag +from functions.files import get_alternative_filename +from exposition.models import BIT_AUDIENCE + + +def to_int(val): + """ + Reverse function to get_int + return None if value isnt integer + """ + try: + return int(val) + except ValueError: + return None + +def to_date(value): + if not value: + return None + + if isinstance(value, unicode) or isinstance(value, str): + + t = time.strptime(value, "%d.%m.%Y") + if isinstance(value, float): + t = xlrd.xldate_as_tuple(value, 0)+(0,0,0) + return time.strftime("%Y-%m-%d", t) + +def to_country(value): + try: + query = get_translation_aware_manager(Country) + country = query.filter(name=value)[0] + return country + except IndexError: + return None + +def to_city(value, lang, country): + try: + # get city by name + #objects = get_translation_aware_manager(City) + # except IndexError if no found + city = City.objects.filter(translations__name=value, country=country)[0] + # print(city) + return city + except IndexError: + # print('---------------------') + # print(value.encode('utf8')) + # print('---------------------') + return None + + +def to_theme(obj, value): + + if isinstance(value, float) or isinstance(value, int): + if (value - int(value) > 0): + value = str(value) + else: + value = str(int(value)) + theme_ids = value.split('.') + else: + theme_ids = value.split(',') + + obj.theme.add(*Theme.objects.filter(id__in=theme_ids)) + +def to_tag(obj,value): + if value == [""]: + return None + names = value.split(',') + translation.activate('en') + if names: + obj.tag.add(*Tag.objects.filter(translations__name__in=names, theme__in=obj.theme.all())) + else: + return + + + +def to_place(value): + try: + place = PlaceExposition.objects.get(url=value) + return place + except PlaceExposition.DoesNotExist: + return None + + +def to_periodic(value): + periodic = {'': 0, u'Ежегодно': 1.0, u'2 раза в год': 2.0, u'3 раза в год': 3.0, + u'4 раза в год': 4.0, u'5 раз в год': 5.0, u'Раз в 2 года': 0.5, + u'Раз в 3 года': 0.33, u'Раз в 4 года': 0.25} + + return periodic.get(value, 0) + + +def to_audience(value, model=Exposition): + if value: + l = value.split(', ') + if l: + new_list = [] + for value in l: + for item1, item2 in BIT_AUDIENCE: + if value == item2: + new_list.append(item1) + if new_list: + return reduce(lambda x,y: x|y, (getattr(model.audience, item) for item in new_list)) + return 0 + +def get_audience(value): + if isinstance(value, BitHandler): + l = [k for k, v in value.iteritems() if v] + if l: + new_list = [] + for value in l: + for item1, item2 in BIT_AUDIENCE: + if value == item1: + new_list.append(item2) + + return ', '.join(new_list) + return + + +def save_logo(obj, path): + if not path: + return None + file_name = path.split('/')[-1] + logo_path = obj.logo.field.upload_to + full_path = settings.MEDIA_ROOT + logo_path + + + alt_name = get_alternative_filename(full_path, file_name) + + download_to = full_path+alt_name + + if path.startswith('http://') or path.startswith('https://'): + + url = path + else: + url = 'http://expomap.ru' + path + + try: + response = urllib2.urlopen(url, timeout=10) + except: + return None + + with open(download_to,'wb') as f: + try: + f.write(response.read()) + f.close() + except: + # can be timeout + return None + + obj.logo = logo_path + alt_name + obj.save() + + +def check_quality_label(obj, value, label): + bit = obj.quality_label + try: + value = int(value) + except: + return bit + if value: + setattr(bit, label, True) + return bit \ No newline at end of file diff --git a/media/imgs/149.jpeg b/media/imgs/149.jpeg deleted file mode 100644 index 3fac9105..00000000 Binary files a/media/imgs/149.jpeg and /dev/null differ diff --git a/media/imgs/27159.gif b/media/imgs/27159.gif deleted file mode 100644 index 51b6920c..00000000 Binary files a/media/imgs/27159.gif and /dev/null differ diff --git a/media/imgs/ALUMINIUM 2012.jpg b/media/imgs/ALUMINIUM 2012.jpg deleted file mode 100644 index 8cfa8622..00000000 Binary files a/media/imgs/ALUMINIUM 2012.jpg and /dev/null differ diff --git a/media/imgs/AMB 2012.jpg b/media/imgs/AMB 2012.jpg deleted file mode 100644 index 72d11b9f..00000000 Binary files a/media/imgs/AMB 2012.jpg and /dev/null differ diff --git a/media/imgs/Automechanika 2012.jpeg b/media/imgs/Automechanika 2012.jpeg deleted file mode 100644 index 5404aaf3..00000000 Binary files a/media/imgs/Automechanika 2012.jpeg and /dev/null differ diff --git a/media/imgs/Bio-Europe.jpg b/media/imgs/Bio-Europe.jpg deleted file mode 100644 index 3ac5e624..00000000 Binary files a/media/imgs/Bio-Europe.jpg and /dev/null differ diff --git a/media/imgs/BioNord 2014.jpg b/media/imgs/BioNord 2014.jpg deleted file mode 100644 index 1b9e0dfe..00000000 Binary files a/media/imgs/BioNord 2014.jpg and /dev/null differ diff --git a/media/imgs/BioSud 2014.jpg b/media/imgs/BioSud 2014.jpg deleted file mode 100644 index bf675aa9..00000000 Binary files a/media/imgs/BioSud 2014.jpg and /dev/null differ diff --git a/media/imgs/CBB-China Brew & China Beverage.jpg b/media/imgs/CBB-China Brew & China Beverage.jpg deleted file mode 100644 index 13ae6ce8..00000000 Binary files a/media/imgs/CBB-China Brew & China Beverage.jpg and /dev/null differ diff --git a/media/imgs/CEF Summer Chengdu 2014.jpeg b/media/imgs/CEF Summer Chengdu 2014.jpeg deleted file mode 100644 index 4f435ac0..00000000 Binary files a/media/imgs/CEF Summer Chengdu 2014.jpeg and /dev/null differ diff --git a/media/imgs/CIMES china.jpg b/media/imgs/CIMES china.jpg deleted file mode 100644 index 905cc9e4..00000000 Binary files a/media/imgs/CIMES china.jpg and /dev/null differ diff --git a/media/imgs/CWIEME Berlin.jpeg b/media/imgs/CWIEME Berlin.jpeg deleted file mode 100644 index 56929a3b..00000000 Binary files a/media/imgs/CWIEME Berlin.jpeg and /dev/null differ diff --git a/media/imgs/CWMEE China expo.jpg b/media/imgs/CWMEE China expo.jpg deleted file mode 100644 index 00b68402..00000000 Binary files a/media/imgs/CWMEE China expo.jpg and /dev/null differ diff --git a/media/imgs/Cersaie_logo.jpg b/media/imgs/Cersaie_logo.jpg deleted file mode 100644 index 0f3dfb26..00000000 Binary files a/media/imgs/Cersaie_logo.jpg and /dev/null differ diff --git a/media/imgs/Cosmetic_Business.jpg b/media/imgs/Cosmetic_Business.jpg deleted file mode 100644 index f04c7bc1..00000000 Binary files a/media/imgs/Cosmetic_Business.jpg and /dev/null differ diff --git a/media/imgs/Eltec Nurnberg 2013 .gif b/media/imgs/Eltec Nurnberg 2013 .gif deleted file mode 100644 index 1d68c941..00000000 Binary files a/media/imgs/Eltec Nurnberg 2013 .gif and /dev/null differ diff --git a/media/imgs/EuroBLECH 2012.jpg b/media/imgs/EuroBLECH 2012.jpg deleted file mode 100644 index 29e82d6e..00000000 Binary files a/media/imgs/EuroBLECH 2012.jpg and /dev/null differ diff --git a/media/imgs/FILExpo China.gif b/media/imgs/FILExpo China.gif deleted file mode 100644 index 9bf97bf6..00000000 Binary files a/media/imgs/FILExpo China.gif and /dev/null differ diff --git a/media/imgs/Fachdental Leipzig 2012.jpg b/media/imgs/Fachdental Leipzig 2012.jpg deleted file mode 100644 index 647aba2f..00000000 Binary files a/media/imgs/Fachdental Leipzig 2012.jpg and /dev/null differ diff --git a/media/imgs/Filtration and Separation Asia 2014.jpg b/media/imgs/Filtration and Separation Asia 2014.jpg deleted file mode 100644 index 00f0d66a..00000000 Binary files a/media/imgs/Filtration and Separation Asia 2014.jpg and /dev/null differ diff --git a/media/imgs/Flomart.jpg b/media/imgs/Flomart.jpg deleted file mode 100644 index 0e898ea6..00000000 Binary files a/media/imgs/Flomart.jpg and /dev/null differ diff --git a/media/imgs/FlowEx China 2014.jpeg b/media/imgs/FlowEx China 2014.jpeg deleted file mode 100644 index 4752963d..00000000 Binary files a/media/imgs/FlowEx China 2014.jpeg and /dev/null differ diff --git a/media/imgs/Furniture China.jpg b/media/imgs/Furniture China.jpg deleted file mode 100644 index 6eec850e..00000000 Binary files a/media/imgs/Furniture China.jpg and /dev/null differ diff --git a/media/imgs/Guangzhou International Lighting Exhibition 2012.jpeg b/media/imgs/Guangzhou International Lighting Exhibition 2012.jpeg deleted file mode 100644 index 40c9055b..00000000 Binary files a/media/imgs/Guangzhou International Lighting Exhibition 2012.jpeg and /dev/null differ diff --git a/media/imgs/HOGA.png b/media/imgs/HOGA.png deleted file mode 100644 index 192a5f1d..00000000 Binary files a/media/imgs/HOGA.png and /dev/null differ diff --git a/media/imgs/Logo_Aquatech_China_NEG.jpg b/media/imgs/Logo_Aquatech_China_NEG.jpg deleted file mode 100644 index edd7a55d..00000000 Binary files a/media/imgs/Logo_Aquatech_China_NEG.jpg and /dev/null differ diff --git a/media/imgs/about-j-13151-1.gif b/media/imgs/about-j-13151-1.gif deleted file mode 100644 index 01a74eaf..00000000 Binary files a/media/imgs/about-j-13151-1.gif and /dev/null differ diff --git a/media/imgs/agrar-unternehmertage-9863-1.jpg b/media/imgs/agrar-unternehmertage-9863-1.jpg deleted file mode 100644 index da808bce..00000000 Binary files a/media/imgs/agrar-unternehmertage-9863-1.jpg and /dev/null differ diff --git a/media/imgs/agrochemex-13588-1.gif b/media/imgs/agrochemex-13588-1.gif deleted file mode 100644 index 1f28123f..00000000 Binary files a/media/imgs/agrochemex-13588-1.gif and /dev/null differ diff --git a/media/imgs/airtec-10923-1.jpg b/media/imgs/airtec-10923-1.jpg deleted file mode 100644 index 153adb93..00000000 Binary files a/media/imgs/airtec-10923-1.jpg and /dev/null differ diff --git a/media/imgs/all-china-shoetch-9997-1.gif b/media/imgs/all-china-shoetch-9997-1.gif deleted file mode 100644 index 3edbb797..00000000 Binary files a/media/imgs/all-china-shoetch-9997-1.gif and /dev/null differ diff --git a/media/imgs/all_in_print_logo_842.jpg b/media/imgs/all_in_print_logo_842.jpg deleted file mode 100644 index 71eba4b8..00000000 Binary files a/media/imgs/all_in_print_logo_842.jpg and /dev/null differ diff --git a/media/imgs/aluminium-china-6392-1.gif b/media/imgs/aluminium-china-6392-1.gif deleted file mode 100644 index 15a7f8a8..00000000 Binary files a/media/imgs/aluminium-china-6392-1.gif and /dev/null differ diff --git a/media/imgs/ambiente-lavoro-2913-1.gif b/media/imgs/ambiente-lavoro-2913-1.gif deleted file mode 100644 index 0629efa6..00000000 Binary files a/media/imgs/ambiente-lavoro-2913-1.gif and /dev/null differ diff --git a/media/imgs/analytica-china-3387-1.gif b/media/imgs/analytica-china-3387-1.gif deleted file mode 100644 index dba756f1..00000000 Binary files a/media/imgs/analytica-china-3387-1.gif and /dev/null differ diff --git a/media/imgs/animal-105-1.gif b/media/imgs/animal-105-1.gif deleted file mode 100644 index bdb659d8..00000000 Binary files a/media/imgs/animal-105-1.gif and /dev/null differ diff --git a/media/imgs/arbeitsschutz-aktuell-9855-1.gif b/media/imgs/arbeitsschutz-aktuell-9855-1.gif deleted file mode 100644 index 5c7c52f3..00000000 Binary files a/media/imgs/arbeitsschutz-aktuell-9855-1.gif and /dev/null differ diff --git a/media/imgs/asia-bike-16381-1.gif b/media/imgs/asia-bike-16381-1.gif deleted file mode 100644 index 20cb0a31..00000000 Binary files a/media/imgs/asia-bike-16381-1.gif and /dev/null differ diff --git a/media/imgs/asia-outdoor-trade-fair-14181-1.gif b/media/imgs/asia-outdoor-trade-fair-14181-1.gif deleted file mode 100644 index 53c29b3a..00000000 Binary files a/media/imgs/asia-outdoor-trade-fair-14181-1.gif and /dev/null differ diff --git a/media/imgs/asiamold-15926-1.gif b/media/imgs/asiamold-15926-1.gif deleted file mode 100644 index 4a02b0ac..00000000 Binary files a/media/imgs/asiamold-15926-1.gif and /dev/null differ diff --git a/media/imgs/asme-turbo-expo-155-1.gif b/media/imgs/asme-turbo-expo-155-1.gif deleted file mode 100644 index 81e41639..00000000 Binary files a/media/imgs/asme-turbo-expo-155-1.gif and /dev/null differ diff --git a/media/imgs/auto-chongqing-14014-1.gif b/media/imgs/auto-chongqing-14014-1.gif deleted file mode 100644 index b6df35fd..00000000 Binary files a/media/imgs/auto-chongqing-14014-1.gif and /dev/null differ diff --git a/media/imgs/auto-e-moto-d-epoca-11715-1.gif b/media/imgs/auto-e-moto-d-epoca-11715-1.gif deleted file mode 100644 index 60642414..00000000 Binary files a/media/imgs/auto-e-moto-d-epoca-11715-1.gif and /dev/null differ diff --git a/media/imgs/auto-guangzhou-7498-1.gif b/media/imgs/auto-guangzhou-7498-1.gif deleted file mode 100644 index a7de68cc..00000000 Binary files a/media/imgs/auto-guangzhou-7498-1.gif and /dev/null differ diff --git a/media/imgs/autochtona-9960-1.gif b/media/imgs/autochtona-9960-1.gif deleted file mode 100644 index 22106816..00000000 Binary files a/media/imgs/autochtona-9960-1.gif and /dev/null differ diff --git a/media/imgs/automechanika-shanghai-4160-1.gif b/media/imgs/automechanika-shanghai-4160-1.gif deleted file mode 100644 index da2f3a01..00000000 Binary files a/media/imgs/automechanika-shanghai-4160-1.gif and /dev/null differ diff --git a/media/imgs/automotive-testing-expo-china-7978-1.gif b/media/imgs/automotive-testing-expo-china-7978-1.gif deleted file mode 100644 index 3f552988..00000000 Binary files a/media/imgs/automotive-testing-expo-china-7978-1.gif and /dev/null differ diff --git a/media/imgs/automotive-testing-expo-europe-2681-1.gif b/media/imgs/automotive-testing-expo-europe-2681-1.gif deleted file mode 100644 index 89a3352e..00000000 Binary files a/media/imgs/automotive-testing-expo-europe-2681-1.gif and /dev/null differ diff --git a/media/imgs/azubi-studientage-chemnitz-9872-1.1.gif b/media/imgs/azubi-studientage-chemnitz-9872-1.1.gif deleted file mode 100644 index 941aa9ae..00000000 Binary files a/media/imgs/azubi-studientage-chemnitz-9872-1.1.gif and /dev/null differ diff --git a/media/imgs/azubi-studientage-chemnitz-9872-1.2.gif b/media/imgs/azubi-studientage-chemnitz-9872-1.2.gif deleted file mode 100644 index 941aa9ae..00000000 Binary files a/media/imgs/azubi-studientage-chemnitz-9872-1.2.gif and /dev/null differ diff --git a/media/imgs/azubi-studientage-chemnitz-9872-1.3.gif b/media/imgs/azubi-studientage-chemnitz-9872-1.3.gif deleted file mode 100644 index 941aa9ae..00000000 Binary files a/media/imgs/azubi-studientage-chemnitz-9872-1.3.gif and /dev/null differ diff --git a/media/imgs/azubi-studientage-chemnitz-9872-1.gif b/media/imgs/azubi-studientage-chemnitz-9872-1.gif deleted file mode 100644 index 941aa9ae..00000000 Binary files a/media/imgs/azubi-studientage-chemnitz-9872-1.gif and /dev/null differ diff --git a/media/imgs/azubi-studientage-leipzig-12049-1.gif b/media/imgs/azubi-studientage-leipzig-12049-1.gif deleted file mode 100644 index 941aa9ae..00000000 Binary files a/media/imgs/azubi-studientage-leipzig-12049-1.gif and /dev/null differ diff --git a/media/imgs/babywelt-munchen-10255-1.gif b/media/imgs/babywelt-munchen-10255-1.gif deleted file mode 100644 index af5affd7..00000000 Binary files a/media/imgs/babywelt-munchen-10255-1.gif and /dev/null differ diff --git a/media/imgs/babywelt-stuttgart-10256-1.1.gif b/media/imgs/babywelt-stuttgart-10256-1.1.gif deleted file mode 100644 index a138d9b7..00000000 Binary files a/media/imgs/babywelt-stuttgart-10256-1.1.gif and /dev/null differ diff --git a/media/imgs/babywelt-stuttgart-10256-1.gif b/media/imgs/babywelt-stuttgart-10256-1.gif deleted file mode 100644 index a138d9b7..00000000 Binary files a/media/imgs/babywelt-stuttgart-10256-1.gif and /dev/null differ diff --git a/media/imgs/bau-253-1.gif b/media/imgs/bau-253-1.gif deleted file mode 100644 index 883434a7..00000000 Binary files a/media/imgs/bau-253-1.gif and /dev/null differ diff --git a/media/imgs/bauma-258-1.gif b/media/imgs/bauma-258-1.gif deleted file mode 100644 index 27bf702c..00000000 Binary files a/media/imgs/bauma-258-1.gif and /dev/null differ diff --git a/media/imgs/bauma-china-5156-1.gif b/media/imgs/bauma-china-5156-1.gif deleted file mode 100644 index 8e19c3dd..00000000 Binary files a/media/imgs/bauma-china-5156-1.gif and /dev/null differ diff --git a/media/imgs/baumesse-bad-kreuznach-17085-1.gif b/media/imgs/baumesse-bad-kreuznach-17085-1.gif deleted file mode 100644 index 35c17f49..00000000 Binary files a/media/imgs/baumesse-bad-kreuznach-17085-1.gif and /dev/null differ diff --git a/media/imgs/baumesse-hofheim-17086-1.gif b/media/imgs/baumesse-hofheim-17086-1.gif deleted file mode 100644 index 35c17f49..00000000 Binary files a/media/imgs/baumesse-hofheim-17086-1.gif and /dev/null differ diff --git a/media/imgs/beijing-essen-welding-cutting-9186-1.gif b/media/imgs/beijing-essen-welding-cutting-9186-1.gif deleted file mode 100644 index 46a72689..00000000 Binary files a/media/imgs/beijing-essen-welding-cutting-9186-1.gif and /dev/null differ diff --git a/media/imgs/bi-mu-277-1.gif b/media/imgs/bi-mu-277-1.gif deleted file mode 100644 index 78b05c51..00000000 Binary files a/media/imgs/bi-mu-277-1.gif and /dev/null differ diff --git a/media/imgs/biennale-di-venezia-archittetura-5634-1.jpg b/media/imgs/biennale-di-venezia-archittetura-5634-1.jpg deleted file mode 100644 index 6d967654..00000000 Binary files a/media/imgs/biennale-di-venezia-archittetura-5634-1.jpg and /dev/null differ diff --git a/media/imgs/brau-beviale-325-1.jpg b/media/imgs/brau-beviale-325-1.jpg deleted file mode 100644 index fc826b1a..00000000 Binary files a/media/imgs/brau-beviale-325-1.jpg and /dev/null differ diff --git a/media/imgs/bread-butter-berlin-13444-1.gif b/media/imgs/bread-butter-berlin-13444-1.gif deleted file mode 100644 index eed18333..00000000 Binary files a/media/imgs/bread-butter-berlin-13444-1.gif and /dev/null differ diff --git a/media/imgs/brille-co-dortmund-8538-1.gif b/media/imgs/brille-co-dortmund-8538-1.gif deleted file mode 100644 index 5fe08d1c..00000000 Binary files a/media/imgs/brille-co-dortmund-8538-1.gif and /dev/null differ diff --git a/media/imgs/cadeaux-leipzig-362-1.gif b/media/imgs/cadeaux-leipzig-362-1.gif deleted file mode 100644 index 5817ad00..00000000 Binary files a/media/imgs/cadeaux-leipzig-362-1.gif and /dev/null differ diff --git a/media/imgs/caravan-bremen-375-1.gif b/media/imgs/caravan-bremen-375-1.gif deleted file mode 100644 index 7ccc59a6..00000000 Binary files a/media/imgs/caravan-bremen-375-1.gif and /dev/null differ diff --git a/media/imgs/caravan_salon_duesseldorf 2012.jpg b/media/imgs/caravan_salon_duesseldorf 2012.jpg deleted file mode 100644 index ccb07a99..00000000 Binary files a/media/imgs/caravan_salon_duesseldorf 2012.jpg and /dev/null differ diff --git a/media/imgs/casa-su-misura-2014.jpg b/media/imgs/casa-su-misura-2014.jpg deleted file mode 100644 index a31115e6..00000000 Binary files a/media/imgs/casa-su-misura-2014.jpg and /dev/null differ diff --git a/media/imgs/cbd-4105-1.gif b/media/imgs/cbd-4105-1.gif deleted file mode 100644 index 31dc54de..00000000 Binary files a/media/imgs/cbd-4105-1.gif and /dev/null differ diff --git a/media/imgs/cebit-389-1.gif b/media/imgs/cebit-389-1.gif deleted file mode 100644 index af180f81..00000000 Binary files a/media/imgs/cebit-389-1.gif and /dev/null differ diff --git a/media/imgs/cef-china-electronic-fair-shanghai-11311-1.gif b/media/imgs/cef-china-electronic-fair-shanghai-11311-1.gif deleted file mode 100644 index 1c29e88b..00000000 Binary files a/media/imgs/cef-china-electronic-fair-shanghai-11311-1.gif and /dev/null differ diff --git a/media/imgs/cemat-asia-398-1.gif b/media/imgs/cemat-asia-398-1.gif deleted file mode 100644 index ed0b0911..00000000 Binary files a/media/imgs/cemat-asia-398-1.gif and /dev/null differ diff --git a/media/imgs/chfe-china-international-housing-and-furnishing-exposition-10986-1.gif b/media/imgs/chfe-china-international-housing-and-furnishing-exposition-10986-1.gif deleted file mode 100644 index 4d55b070..00000000 Binary files a/media/imgs/chfe-china-international-housing-and-furnishing-exposition-10986-1.gif and /dev/null differ diff --git a/media/imgs/china-(shenzhen)-international-gifts-handicrafts-watches-houseware-fair-14179-1.gif b/media/imgs/china-(shenzhen)-international-gifts-handicrafts-watches-houseware-fair-14179-1.gif deleted file mode 100644 index 72241536..00000000 Binary files a/media/imgs/china-(shenzhen)-international-gifts-handicrafts-watches-houseware-fair-14179-1.gif and /dev/null differ diff --git a/media/imgs/china-bevtek-16845-1.gif b/media/imgs/china-bevtek-16845-1.gif deleted file mode 100644 index 5455877e..00000000 Binary files a/media/imgs/china-bevtek-16845-1.gif and /dev/null differ diff --git a/media/imgs/china-education-expo-beijing-6992-1.gif b/media/imgs/china-education-expo-beijing-6992-1.gif deleted file mode 100644 index 68118b1f..00000000 Binary files a/media/imgs/china-education-expo-beijing-6992-1.gif and /dev/null differ diff --git a/media/imgs/china-education-expo-chengdu-11953-1.1.gif b/media/imgs/china-education-expo-chengdu-11953-1.1.gif deleted file mode 100644 index 68118b1f..00000000 Binary files a/media/imgs/china-education-expo-chengdu-11953-1.1.gif and /dev/null differ diff --git a/media/imgs/china-education-expo-chengdu-11953-1.gif b/media/imgs/china-education-expo-chengdu-11953-1.gif deleted file mode 100644 index 68118b1f..00000000 Binary files a/media/imgs/china-education-expo-chengdu-11953-1.gif and /dev/null differ diff --git a/media/imgs/china-education-expo-shanghai-6993-1.gif b/media/imgs/china-education-expo-shanghai-6993-1.gif deleted file mode 100644 index 68118b1f..00000000 Binary files a/media/imgs/china-education-expo-shanghai-6993-1.gif and /dev/null differ diff --git a/media/imgs/china-furniture-woodworks-9158-1.gif b/media/imgs/china-furniture-woodworks-9158-1.gif deleted file mode 100644 index 2e10ce07..00000000 Binary files a/media/imgs/china-furniture-woodworks-9158-1.gif and /dev/null differ diff --git a/media/imgs/china-international-footwear-fair-5069-1.gif b/media/imgs/china-international-footwear-fair-5069-1.gif deleted file mode 100644 index 269eac2e..00000000 Binary files a/media/imgs/china-international-footwear-fair-5069-1.gif and /dev/null differ diff --git a/media/imgs/china-international-hardware-show_logo.jpg b/media/imgs/china-international-hardware-show_logo.jpg deleted file mode 100644 index 151f3189..00000000 Binary files a/media/imgs/china-international-hardware-show_logo.jpg and /dev/null differ diff --git a/media/imgs/china-kids-expo-2926-1.gif b/media/imgs/china-kids-expo-2926-1.gif deleted file mode 100644 index 5b8c6717..00000000 Binary files a/media/imgs/china-kids-expo-2926-1.gif and /dev/null differ diff --git a/media/imgs/china-leather-6869-1.gif b/media/imgs/china-leather-6869-1.gif deleted file mode 100644 index 91d9d488..00000000 Binary files a/media/imgs/china-leather-6869-1.gif and /dev/null differ diff --git a/media/imgs/china-wedding-expo-16645-1.gif b/media/imgs/china-wedding-expo-16645-1.gif deleted file mode 100644 index 9b4b1094..00000000 Binary files a/media/imgs/china-wedding-expo-16645-1.gif and /dev/null differ diff --git a/media/imgs/ciaar-11192-1.gif b/media/imgs/ciaar-11192-1.gif deleted file mode 100644 index db3093a2..00000000 Binary files a/media/imgs/ciaar-11192-1.gif and /dev/null differ diff --git a/media/imgs/cieme-17824-1.gif b/media/imgs/cieme-17824-1.gif deleted file mode 100644 index c96735cf..00000000 Binary files a/media/imgs/cieme-17824-1.gif and /dev/null differ diff --git a/media/imgs/cifit.gif b/media/imgs/cifit.gif deleted file mode 100644 index 4989181c..00000000 Binary files a/media/imgs/cifit.gif and /dev/null differ diff --git a/media/imgs/cimamotor-14015-1.gif b/media/imgs/cimamotor-14015-1.gif deleted file mode 100644 index f05ba271..00000000 Binary files a/media/imgs/cimamotor-14015-1.gif and /dev/null differ diff --git a/media/imgs/cinte-techtextil-china-442-1.gif b/media/imgs/cinte-techtextil-china-442-1.gif deleted file mode 100644 index 2e879679..00000000 Binary files a/media/imgs/cinte-techtextil-china-442-1.gif and /dev/null differ diff --git a/media/imgs/ciof-china-international-optics-fair-7355-1.gif b/media/imgs/ciof-china-international-optics-fair-7355-1.gif deleted file mode 100644 index 3acc49ff..00000000 Binary files a/media/imgs/ciof-china-international-optics-fair-7355-1.gif and /dev/null differ diff --git a/media/imgs/citexpo-7985-1.gif b/media/imgs/citexpo-7985-1.gif deleted file mode 100644 index 6e3aba41..00000000 Binary files a/media/imgs/citexpo-7985-1.gif and /dev/null differ diff --git a/media/imgs/cnibf-16217-1.gif b/media/imgs/cnibf-16217-1.gif deleted file mode 100644 index d4a81b81..00000000 Binary files a/media/imgs/cnibf-16217-1.gif and /dev/null differ diff --git a/media/imgs/coilex-16120-1.gif b/media/imgs/coilex-16120-1.gif deleted file mode 100644 index b621429e..00000000 Binary files a/media/imgs/coilex-16120-1.gif and /dev/null differ diff --git a/media/imgs/coiltech-16870-1.gif b/media/imgs/coiltech-16870-1.gif deleted file mode 100644 index 65534dc3..00000000 Binary files a/media/imgs/coiltech-16870-1.gif and /dev/null differ diff --git a/media/imgs/comfortex-480-1.jpg b/media/imgs/comfortex-480-1.jpg deleted file mode 100644 index 19293dab..00000000 Binary files a/media/imgs/comfortex-480-1.jpg and /dev/null differ diff --git a/media/imgs/compamed-4236-1.gif b/media/imgs/compamed-4236-1.gif deleted file mode 100644 index 68f2e4c5..00000000 Binary files a/media/imgs/compamed-4236-1.gif and /dev/null differ diff --git a/media/imgs/composites-europe-10253-1.gif b/media/imgs/composites-europe-10253-1.gif deleted file mode 100644 index 26fb315b..00000000 Binary files a/media/imgs/composites-europe-10253-1.gif and /dev/null differ diff --git a/media/imgs/control-china-3027-1.gif b/media/imgs/control-china-3027-1.gif deleted file mode 100644 index d37f82ec..00000000 Binary files a/media/imgs/control-china-3027-1.gif and /dev/null differ diff --git a/media/imgs/cosh_logo_858.jpg b/media/imgs/cosh_logo_858.jpg deleted file mode 100644 index 8b13e9ff..00000000 Binary files a/media/imgs/cosh_logo_858.jpg and /dev/null differ diff --git a/media/imgs/cosmetica-berlin-13466-1.gif b/media/imgs/cosmetica-berlin-13466-1.gif deleted file mode 100644 index f67741be..00000000 Binary files a/media/imgs/cosmetica-berlin-13466-1.gif and /dev/null differ diff --git a/media/imgs/coteca-13915-1.gif b/media/imgs/coteca-13915-1.gif deleted file mode 100644 index 1cdb2128..00000000 Binary files a/media/imgs/coteca-13915-1.gif and /dev/null differ diff --git a/media/imgs/cphi-china-535-1.gif b/media/imgs/cphi-china-535-1.gif deleted file mode 100644 index 013bc19c..00000000 Binary files a/media/imgs/cphi-china-535-1.gif and /dev/null differ diff --git a/media/imgs/cremona-mondomusica_logo.jpg b/media/imgs/cremona-mondomusica_logo.jpg deleted file mode 100644 index 114d0ba8..00000000 Binary files a/media/imgs/cremona-mondomusica_logo.jpg and /dev/null differ diff --git a/media/imgs/cxiaf-9478-1.gif b/media/imgs/cxiaf-9478-1.gif deleted file mode 100644 index a867ce55..00000000 Binary files a/media/imgs/cxiaf-9478-1.gif and /dev/null differ diff --git a/media/imgs/denkmal-563-1.gif b/media/imgs/denkmal-563-1.gif deleted file mode 100644 index 4a46e97f..00000000 Binary files a/media/imgs/denkmal-563-1.gif and /dev/null differ diff --git a/media/imgs/deutsche-junggeflugelschau-572-1.1.gif b/media/imgs/deutsche-junggeflugelschau-572-1.1.gif deleted file mode 100644 index 520de0e8..00000000 Binary files a/media/imgs/deutsche-junggeflugelschau-572-1.1.gif and /dev/null differ diff --git a/media/imgs/deutsche-junggeflugelschau-572-1.2.gif b/media/imgs/deutsche-junggeflugelschau-572-1.2.gif deleted file mode 100644 index 520de0e8..00000000 Binary files a/media/imgs/deutsche-junggeflugelschau-572-1.2.gif and /dev/null differ diff --git a/media/imgs/deutsche-junggeflugelschau-572-1.gif b/media/imgs/deutsche-junggeflugelschau-572-1.gif deleted file mode 100644 index 520de0e8..00000000 Binary files a/media/imgs/deutsche-junggeflugelschau-572-1.gif and /dev/null differ diff --git a/media/imgs/die-mould-china-4780-1.gif b/media/imgs/die-mould-china-4780-1.gif deleted file mode 100644 index 00133baf..00000000 Binary files a/media/imgs/die-mould-china-4780-1.gif and /dev/null differ diff --git a/media/imgs/dkm-konzepte-losungen-5420-1.jpg b/media/imgs/dkm-konzepte-losungen-5420-1.jpg deleted file mode 100644 index 0b1cd239..00000000 Binary files a/media/imgs/dkm-konzepte-losungen-5420-1.jpg and /dev/null differ diff --git a/media/imgs/dmp-china-(dongguan)-international-plastics-packaging-rubber-exhibition-15121-1.gif b/media/imgs/dmp-china-(dongguan)-international-plastics-packaging-rubber-exhibition-15121-1.gif deleted file mode 100644 index fbc75cc2..00000000 Binary files a/media/imgs/dmp-china-(dongguan)-international-plastics-packaging-rubber-exhibition-15121-1.gif and /dev/null differ diff --git a/media/imgs/dms_expo_logo_neu_2_190.jpg b/media/imgs/dms_expo_logo_neu_2_190.jpg deleted file mode 100644 index 7534463f..00000000 Binary files a/media/imgs/dms_expo_logo_neu_2_190.jpg and /dev/null differ diff --git a/media/imgs/druck-form-599-1.gif b/media/imgs/druck-form-599-1.gif deleted file mode 100644 index 26fba8eb..00000000 Binary files a/media/imgs/druck-form-599-1.gif and /dev/null differ diff --git a/media/imgs/du-und-deine-welt-6250-1.gif b/media/imgs/du-und-deine-welt-6250-1.gif deleted file mode 100644 index 40b09953..00000000 Binary files a/media/imgs/du-und-deine-welt-6250-1.gif and /dev/null differ diff --git a/media/imgs/e-dpc-expo-16741-1.gif b/media/imgs/e-dpc-expo-16741-1.gif deleted file mode 100644 index daeef838..00000000 Binary files a/media/imgs/e-dpc-expo-16741-1.gif and /dev/null differ diff --git a/media/imgs/ebce 2012.jpg b/media/imgs/ebce 2012.jpg deleted file mode 100644 index fe6b2f8a..00000000 Binary files a/media/imgs/ebce 2012.jpg and /dev/null differ diff --git a/media/imgs/ecomobil-13873-1.gif b/media/imgs/ecomobil-13873-1.gif deleted file mode 100644 index 1dbe17a6..00000000 Binary files a/media/imgs/ecomobil-13873-1.gif and /dev/null differ diff --git a/media/imgs/ecomondo-3183-1.gif b/media/imgs/ecomondo-3183-1.gif deleted file mode 100644 index 51fe1e2b..00000000 Binary files a/media/imgs/ecomondo-3183-1.gif and /dev/null differ diff --git a/media/imgs/eicma-esposizione-internazionale-del-ciclo-e-motociclo-755-1.gif b/media/imgs/eicma-esposizione-internazionale-del-ciclo-e-motociclo-755-1.gif deleted file mode 100644 index 3a6c34d4..00000000 Binary files a/media/imgs/eicma-esposizione-internazionale-del-ciclo-e-motociclo-755-1.gif and /dev/null differ diff --git a/media/imgs/eima-international-652-1.gif b/media/imgs/eima-international-652-1.gif deleted file mode 100644 index 4f357cba..00000000 Binary files a/media/imgs/eima-international-652-1.gif and /dev/null differ diff --git a/media/imgs/electronica-669-1.gif b/media/imgs/electronica-669-1.gif deleted file mode 100644 index 23c4e40a..00000000 Binary files a/media/imgs/electronica-669-1.gif and /dev/null differ diff --git a/media/imgs/enertec-711-1.gif b/media/imgs/enertec-711-1.gif deleted file mode 100644 index 82d55e0c..00000000 Binary files a/media/imgs/enertec-711-1.gif and /dev/null differ diff --git a/media/imgs/engine-expo-713-1.gif b/media/imgs/engine-expo-713-1.gif deleted file mode 100644 index c0b90911..00000000 Binary files a/media/imgs/engine-expo-713-1.gif and /dev/null differ diff --git a/media/imgs/ep_china_logo.jpg b/media/imgs/ep_china_logo.jpg deleted file mode 100644 index f64396c3..00000000 Binary files a/media/imgs/ep_china_logo.jpg and /dev/null differ diff --git a/media/imgs/equitana-745-1.gif b/media/imgs/equitana-745-1.gif deleted file mode 100644 index 97199db1..00000000 Binary files a/media/imgs/equitana-745-1.gif and /dev/null differ diff --git a/media/imgs/esbuild-14189-1.gif b/media/imgs/esbuild-14189-1.gif deleted file mode 100644 index 1198488c..00000000 Binary files a/media/imgs/esbuild-14189-1.gif and /dev/null differ diff --git a/media/imgs/euro-id-17466-1.gif b/media/imgs/euro-id-17466-1.gif deleted file mode 100644 index 61bd035f..00000000 Binary files a/media/imgs/euro-id-17466-1.gif and /dev/null differ diff --git a/media/imgs/eurobike-4997-1.1.gif b/media/imgs/eurobike-4997-1.1.gif deleted file mode 100644 index 95460528..00000000 Binary files a/media/imgs/eurobike-4997-1.1.gif and /dev/null differ diff --git a/media/imgs/eurobike-4997-1.gif b/media/imgs/eurobike-4997-1.gif deleted file mode 100644 index 95460528..00000000 Binary files a/media/imgs/eurobike-4997-1.gif and /dev/null differ diff --git a/media/imgs/eurocheval-13459-1.1.gif b/media/imgs/eurocheval-13459-1.1.gif deleted file mode 100644 index 54de9dad..00000000 Binary files a/media/imgs/eurocheval-13459-1.1.gif and /dev/null differ diff --git a/media/imgs/eurocheval-13459-1.gif b/media/imgs/eurocheval-13459-1.gif deleted file mode 100644 index 54de9dad..00000000 Binary files a/media/imgs/eurocheval-13459-1.gif and /dev/null differ diff --git a/media/imgs/euromineralexpo-5763-1.gif b/media/imgs/euromineralexpo-5763-1.gif deleted file mode 100644 index a0734806..00000000 Binary files a/media/imgs/euromineralexpo-5763-1.gif and /dev/null differ diff --git a/media/imgs/eurotier-hannover-803-1.gif b/media/imgs/eurotier-hannover-803-1.gif deleted file mode 100644 index e6f2912e..00000000 Binary files a/media/imgs/eurotier-hannover-803-1.gif and /dev/null differ diff --git a/media/imgs/expo-casearia-16420-1.gif b/media/imgs/expo-casearia-16420-1.gif deleted file mode 100644 index 2e1465f7..00000000 Binary files a/media/imgs/expo-casearia-16420-1.gif and /dev/null differ diff --git a/media/imgs/expo-real-844-1.gif b/media/imgs/expo-real-844-1.gif deleted file mode 100644 index 29328942..00000000 Binary files a/media/imgs/expo-real-844-1.gif and /dev/null differ diff --git a/media/imgs/expobici_logo.jpg b/media/imgs/expobici_logo.jpg deleted file mode 100644 index df95e9db..00000000 Binary files a/media/imgs/expobici_logo.jpg and /dev/null differ diff --git a/media/imgs/expocentre_logo_rus.jpg b/media/imgs/expocentre_logo_rus.jpg deleted file mode 100644 index 924f9753..00000000 Binary files a/media/imgs/expocentre_logo_rus.jpg and /dev/null differ diff --git a/media/imgs/expopharm-909-1.gif b/media/imgs/expopharm-909-1.gif deleted file mode 100644 index 71163d61..00000000 Binary files a/media/imgs/expopharm-909-1.gif and /dev/null differ diff --git a/media/imgs/f-cell-14512-1.gif b/media/imgs/f-cell-14512-1.gif deleted file mode 100644 index dc84f3f5..00000000 Binary files a/media/imgs/f-cell-14512-1.gif and /dev/null differ diff --git a/media/imgs/fachdental sudwest.jpg b/media/imgs/fachdental sudwest.jpg deleted file mode 100644 index dcb4de59..00000000 Binary files a/media/imgs/fachdental sudwest.jpg and /dev/null differ diff --git a/media/imgs/fachpack-940-1.gif b/media/imgs/fachpack-940-1.gif deleted file mode 100644 index bf903c93..00000000 Binary files a/media/imgs/fachpack-940-1.gif and /dev/null differ diff --git a/media/imgs/fakuma-945-1.gif b/media/imgs/fakuma-945-1.gif deleted file mode 100644 index 5a3145db..00000000 Binary files a/media/imgs/fakuma-945-1.gif and /dev/null differ diff --git a/media/imgs/family-home-5201-1.gif b/media/imgs/family-home-5201-1.gif deleted file mode 100644 index 11718dab..00000000 Binary files a/media/imgs/family-home-5201-1.gif and /dev/null differ diff --git a/media/imgs/famous-furniture-dongguan.jpg b/media/imgs/famous-furniture-dongguan.jpg deleted file mode 100644 index 68c3e88e..00000000 Binary files a/media/imgs/famous-furniture-dongguan.jpg and /dev/null differ diff --git a/media/imgs/fastener-fair-stuttgart-16115-1.gif b/media/imgs/fastener-fair-stuttgart-16115-1.gif deleted file mode 100644 index 6385176a..00000000 Binary files a/media/imgs/fastener-fair-stuttgart-16115-1.gif and /dev/null differ diff --git a/media/imgs/fastener-trade-show-13375-1.1.gif b/media/imgs/fastener-trade-show-13375-1.1.gif deleted file mode 100644 index e62a729d..00000000 Binary files a/media/imgs/fastener-trade-show-13375-1.1.gif and /dev/null differ diff --git a/media/imgs/fastener-trade-show-13375-1.gif b/media/imgs/fastener-trade-show-13375-1.gif deleted file mode 100644 index e62a729d..00000000 Binary files a/media/imgs/fastener-trade-show-13375-1.gif and /dev/null differ diff --git a/media/imgs/faszination-modellbau-friedrichshafen-1913-1.gif b/media/imgs/faszination-modellbau-friedrichshafen-1913-1.gif deleted file mode 100644 index 9cd51b45..00000000 Binary files a/media/imgs/faszination-modellbau-friedrichshafen-1913-1.gif and /dev/null differ diff --git a/media/imgs/fhc-food-drink-1050-1.gif b/media/imgs/fhc-food-drink-1050-1.gif deleted file mode 100644 index 8dbd074a..00000000 Binary files a/media/imgs/fhc-food-drink-1050-1.gif and /dev/null differ diff --git a/media/imgs/fi-asia-china-1057-1.gif b/media/imgs/fi-asia-china-1057-1.gif deleted file mode 100644 index d187bd29..00000000 Binary files a/media/imgs/fi-asia-china-1057-1.gif and /dev/null differ diff --git a/media/imgs/fiera-natale-16436-1.gif b/media/imgs/fiera-natale-16436-1.gif deleted file mode 100644 index 65960bae..00000000 Binary files a/media/imgs/fiera-natale-16436-1.gif and /dev/null differ diff --git a/media/imgs/fieracavalli-988-1.gif b/media/imgs/fieracavalli-988-1.gif deleted file mode 100644 index 034b77e6..00000000 Binary files a/media/imgs/fieracavalli-988-1.gif and /dev/null differ diff --git a/media/imgs/food-ing-international-16137-1.gif b/media/imgs/food-ing-international-16137-1.gif deleted file mode 100644 index 37a924bc..00000000 Binary files a/media/imgs/food-ing-international-16137-1.gif and /dev/null differ diff --git a/media/imgs/forum-vini-5159-1.gif b/media/imgs/forum-vini-5159-1.gif deleted file mode 100644 index 2d882ba4..00000000 Binary files a/media/imgs/forum-vini-5159-1.gif and /dev/null differ diff --git a/media/imgs/fragranze-9993-1.jpg b/media/imgs/fragranze-9993-1.jpg deleted file mode 100644 index 1a8b1e39..00000000 Binary files a/media/imgs/fragranze-9993-1.jpg and /dev/null differ diff --git a/media/imgs/galabau-1110-1.gif b/media/imgs/galabau-1110-1.gif deleted file mode 100644 index 4f947c74..00000000 Binary files a/media/imgs/galabau-1110-1.gif and /dev/null differ diff --git a/media/imgs/gaste-1120-1.gif b/media/imgs/gaste-1120-1.gif deleted file mode 100644 index f4028362..00000000 Binary files a/media/imgs/gaste-1120-1.gif and /dev/null differ diff --git a/media/imgs/gastro-rostock-16668-1.gif b/media/imgs/gastro-rostock-16668-1.gif deleted file mode 100644 index 410f2e09..00000000 Binary files a/media/imgs/gastro-rostock-16668-1.gif and /dev/null differ diff --git a/media/imgs/gds.1.jpg b/media/imgs/gds.1.jpg deleted file mode 100644 index a418a7f3..00000000 Binary files a/media/imgs/gds.1.jpg and /dev/null differ diff --git a/media/imgs/gds.jpg b/media/imgs/gds.jpg deleted file mode 100644 index a418a7f3..00000000 Binary files a/media/imgs/gds.jpg and /dev/null differ diff --git a/media/imgs/gio-sun-3174-1.gif b/media/imgs/gio-sun-3174-1.gif deleted file mode 100644 index d0575662..00000000 Binary files a/media/imgs/gio-sun-3174-1.gif and /dev/null differ diff --git a/media/imgs/glasstec-1153-1.gif b/media/imgs/glasstec-1153-1.gif deleted file mode 100644 index befb56ca..00000000 Binary files a/media/imgs/glasstec-1153-1.gif and /dev/null differ diff --git a/media/imgs/golf-europe-1164-1.gif b/media/imgs/golf-europe-1164-1.gif deleted file mode 100644 index d4e15587..00000000 Binary files a/media/imgs/golf-europe-1164-1.gif and /dev/null differ diff --git a/media/imgs/gpec-4229-1.gif b/media/imgs/gpec-4229-1.gif deleted file mode 100644 index 4375fd3a..00000000 Binary files a/media/imgs/gpec-4229-1.gif and /dev/null differ diff --git a/media/imgs/hanseboot-6249-1.gif b/media/imgs/hanseboot-6249-1.gif deleted file mode 100644 index 55e98bae..00000000 Binary files a/media/imgs/hanseboot-6249-1.gif and /dev/null differ diff --git a/media/imgs/haus-hof-7877-1.gif b/media/imgs/haus-hof-7877-1.gif deleted file mode 100644 index e7d74b91..00000000 Binary files a/media/imgs/haus-hof-7877-1.gif and /dev/null differ diff --git a/media/imgs/hm13_logo_en_col.png b/media/imgs/hm13_logo_en_col.png deleted file mode 100644 index ec23489b..00000000 Binary files a/media/imgs/hm13_logo_en_col.png and /dev/null differ diff --git a/media/imgs/hobby-elektronik-1215-1.gif b/media/imgs/hobby-elektronik-1215-1.gif deleted file mode 100644 index 5ab11a91..00000000 Binary files a/media/imgs/hobby-elektronik-1215-1.gif and /dev/null differ diff --git a/media/imgs/homi_eng.jpg b/media/imgs/homi_eng.jpg deleted file mode 100644 index f9b16ef5..00000000 Binary files a/media/imgs/homi_eng.jpg and /dev/null differ diff --git a/media/imgs/logo-china-paper.jpg b/media/imgs/logo-china-paper.jpg deleted file mode 100644 index 56e55500..00000000 Binary files a/media/imgs/logo-china-paper.jpg and /dev/null differ diff --git a/media/imgs/logo_chillventa.jpg b/media/imgs/logo_chillventa.jpg deleted file mode 100644 index 8a9ea868..00000000 Binary files a/media/imgs/logo_chillventa.jpg and /dev/null differ diff --git a/media/imgs/logo_hoobyshow_network_inde.gif b/media/imgs/logo_hoobyshow_network_inde.gif deleted file mode 100644 index 87ff411c..00000000 Binary files a/media/imgs/logo_hoobyshow_network_inde.gif and /dev/null differ diff --git a/media/imgs/manifestazione_logo_2223.jpg b/media/imgs/manifestazione_logo_2223.jpg deleted file mode 100644 index 83aa79b7..00000000 Binary files a/media/imgs/manifestazione_logo_2223.jpg and /dev/null differ diff --git a/media/imgs/map_print.pdf b/media/imgs/map_print.pdf deleted file mode 100644 index da567f37..00000000 Binary files a/media/imgs/map_print.pdf and /dev/null differ diff --git a/media/imgs/messen_hbe_rot12_h.gif b/media/imgs/messen_hbe_rot12_h.gif deleted file mode 100644 index cf43fe52..00000000 Binary files a/media/imgs/messen_hbe_rot12_h.gif and /dev/null differ diff --git a/meta/__init__.py b/meta/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/meta/models.py b/meta/models.py new file mode 100644 index 00000000..39f32f06 --- /dev/null +++ b/meta/models.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +from django.db import models +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager +from django.utils import translation +from pymorphy.django_conf import default_morph as morph + +class MetaSetting(TranslatableModel): + name = models.CharField(max_length=100, unique=True) + translations = TranslatedFields( + title = models.CharField(max_length=255, blank=True), + description = models.CharField(max_length=255, blank=True), + keywords = models.CharField(max_length=255, blank=True), + h1 = models.CharField(max_length=255, blank=True), + ) + + params = {'EXPONAME':{'name': 'name'}, + 'EXPONAME_YA':{'name': 'name', 'inflect': True}, + 'EXPOCOUNTRY':{'name': 'country'}, + 'EXPOCOUNTRY_YA':{'name': 'country', 'inflect': True}, + 'EXPOPLACE':{'name': 'place'}, + 'EXPOCITY':{'name': 'city'}, + 'EXPOTHEME':{'name': 'theme'}, + 'EXPOTAG':{'name': 'tag'}, + 'EXPOMONTH':{'name': 'month'}, + 'EXPOYEAR':{'name': 'year'}, + } + + def __unicode__(self): + return self.name + + def generate_meta(self, obj): + """ + obj must be in current language + """ + lang = translation.get_language() + params = {'EXPONAME': getattr(obj, 'name', '')} + tr = self.translations.get(language_code=lang) + title = tr.title.format(**params) + description = tr.description.format(**params) + keywords = []#tr.keywords.format(**params) + h1 = tr.h1.format(**params) + return {'title': title, 'description': description, 'keywords': keywords, 'h1': h1} + + def get_param(self, obj, field): + param = self.params.get(field) + if not param: + return '' + s = getattr(obj, param['name'], '') + if param.get('inflected'): + s = morph.inflect_ru(s, u'пр') + return s + + + + + diff --git a/meta/settings.py b/meta/settings.py new file mode 100644 index 00000000..14b40ed4 --- /dev/null +++ b/meta/settings.py @@ -0,0 +1,13 @@ +from django.conf import settings + +SITE_PROTOCOL = getattr(settings, 'META_SITE_PROTOCOL', None) +SITE_DOMAIN = getattr(settings, 'META_SITE_DOMAIN', None) +SITE_TYPE = getattr(settings, 'META_SITE_TYPE', None) +SITE_NAME = getattr(settings, 'META_SITE_NAME', None) +INCLUDE_KEYWORDS = getattr(settings, 'META_INCLUDE_KEYWORDS', []) +DEFAULT_KEYWORDS = getattr(settings, 'META_DEFAULT_KEYWORDS', []) +IMAGE_URL = getattr(settings, 'META_IMAGE_URL', settings.STATIC_URL) +USE_OG_PROPERTIES = getattr(settings, 'META_USE_OG_PROPERTIES', False) +USE_TWITTER_PROPERTIES = getattr(settings, 'META_USE_TWITTER_PROPERTIES', False) +USE_GOOGLEPLUS_PROPERTIES = getattr(settings, 'META_USE_GOOGLEPLUS_PROPERTIES', False) +USE_SITES = getattr(settings, 'META_USE_SITES', False) diff --git a/meta/templatetags/__init__.py b/meta/templatetags/__init__.py new file mode 100644 index 00000000..3ed9fd0f --- /dev/null +++ b/meta/templatetags/__init__.py @@ -0,0 +1 @@ +__author__ = 'root' diff --git a/meta/templatetags/meta.py b/meta/templatetags/meta.py new file mode 100644 index 00000000..85f62339 --- /dev/null +++ b/meta/templatetags/meta.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import, unicode_literals + +from django import template + +register = template.Library() + + +@register.simple_tag +def generic_prop(namespace, name, value): + """ + Generic property setter that allows to create custom namespaced meta + """ + return '' % (namespace, name, value) + + +@register.simple_tag +def og_prop(name, value): + return '' % (name, value) + + +@register.simple_tag +def twitter_prop(name, value): + return '' % (name, value) + + +@register.simple_tag +def googleplus_prop(name, value): + return '' % (name, value) + + +@register.simple_tag +def googleplus_html_scope(value): + """ + This is meant to be used as attribute to html / body or other tags to + define schema.org type + """ + return ' itemscope itemtype="http://schema.org/%s" ' % value + + +@register.simple_tag +def meta(name, value): + return '' % (name, value) + + +@register.simple_tag +def meta_list(name, lst): + try: + return '' % (name, ', '.join(lst)) + except: + return '' diff --git a/meta/tests.py b/meta/tests.py new file mode 100644 index 00000000..501deb77 --- /dev/null +++ b/meta/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/meta/views.py b/meta/views.py new file mode 100644 index 00000000..f362194a --- /dev/null +++ b/meta/views.py @@ -0,0 +1,167 @@ +from __future__ import unicode_literals + +from django.core.exceptions import ImproperlyConfigured + +from . import settings + + +class Meta(object): + """ Helper for building context meta object """ + + _keywords = [] + _url = None + _image = None + + def __init__(self, **kwargs): + self.use_sites = kwargs.get('use_sites', settings.USE_SITES) + self.title = kwargs.get('title') + self.description = kwargs.get('description') + self.keywords = kwargs.get('keywords') + self.h1 = kwargs.get('h1') + self.url = kwargs.get('url') + self.image = kwargs.get('image') + self.object_type = kwargs.get('object_type', settings.SITE_TYPE) + self.site_name = kwargs.get('site_name', settings.SITE_NAME) + self.use_og = kwargs.get('use_og', settings.USE_OG_PROPERTIES) + self.use_twitter = kwargs.get('use_twitter', settings.USE_TWITTER_PROPERTIES) + self.use_googleplus = kwargs.get('use_googleplus', settings.USE_GOOGLEPLUS_PROPERTIES) + + def get_domain(self): + if self.use_sites: + from django.contrib.sites.models import Site + return Site.objects.get_current().domain + if not settings.SITE_DOMAIN: + raise ImproperlyConfigured('META_SITE_DOMAIN is not set') + return settings.SITE_DOMAIN + + def get_protocol(self): + if not settings.SITE_PROTOCOL: + raise ImproperlyConfigured('META_SITE_PROTOCOL is not set') + return settings.SITE_PROTOCOL + + def get_full_url(self, url): + if not url: + return None + if url.startswith('http'): + return url + if url.startswith('/'): + return '%s://%s%s' % ( + self.get_protocol(), + self.get_domain(), + url + ) + return '%s://%s/%s' % ( + self.get_protocol(), + self.get_domain(), + url + ) + + + @property + def keywords(self): + return self._keywords + + @keywords.setter + def keywords(self, keywords): + if keywords is None: + kws = settings.DEFAULT_KEYWORDS + else: + if not hasattr(keywords, '__iter__'): + # Not iterable + raise ValueError('Keywords must be an intrable') + kws = [k for k in keywords] + if settings.INCLUDE_KEYWORDS: + kws += settings.INCLUDE_KEYWORDS + seen = set() + seen_add = seen.add + self._keywords = [k for k in kws if k not in seen and not seen_add(k)] + + @property + def url(self): + return self._url + + @url.setter + def url(self, url): + self._url = self.get_full_url(url) + + @property + def image(self): + return self._image + + @image.setter + def image(self, image): + if image is None: + self._image = None + return + if not image.startswith('http') and not image.startswith('/'): + image = '%s%s' % (settings.IMAGE_URL, image) + self._image = self.get_full_url(image) + + #-------------------------------------------------------------------- + + + +class MetadataMixin(object): + """ Django CBV mixin to prepare metadata for the view context """ + + meta_class = Meta + title = None + description = None + keywords = [] + h1 = None + url = None + image = None + object_type = None + site_name = None + use_sites = settings.USE_SITES + use_og = settings.USE_OG_PROPERTIES + + def get_meta_class(self): + return self.meta_class + + def get_protocol(self): + return settings.SITE_PROTOCOL + + def get_domain(self): + return settings.SITE_DOMAIN + + def get_meta_title(self, context={}): + return self.title + + def get_meta_description(self, context={}): + return self.description + + def get_meta_h1(self, context={}): + return self.h1 + + def get_meta_keywords(self, context={}): + return self.keywords + + def get_meta_url(self, context={}): + return self.url + + def get_meta_image(self, context={}): + return self.image + + def get_meta_object_type(self, context={}): + return self.object_type or settings.SITE_TYPE + + def get_meta_site_name(self, context={}): + return self.site_name or settings.SITE_NAME + + def get_context_data(self, **kwargs): + context = super(MetadataMixin, self).get_context_data(**kwargs) + context['meta'] = self.get_meta_class()( + use_og=self.use_og, + use_sites=self.use_sites, + title=self.get_meta_title(context=context), + h1=self.get_meta_h1(context=context), + + description=self.get_meta_description(context=context), + keywords=self.get_meta_keywords(context=context), + image=self.get_meta_image(context=context), + url=self.get_meta_url(context=context), + object_type=self.get_meta_object_type(context=context), + site_name=self.get_meta_site_name(context=context), + ) + return context diff --git a/note/__init__.py b/note/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/note/models.py b/note/models.py new file mode 100644 index 00000000..53341335 --- /dev/null +++ b/note/models.py @@ -0,0 +1,13 @@ +from django.db import models +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes import generic + + +class Note(models.Model): + content_type = models.ForeignKey(ContentType, null=True) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk") + user = models.ForeignKey('accounts.User') + text = models.TextField(verbose_name='Note') + + diff --git a/note/tests.py b/note/tests.py new file mode 100644 index 00000000..501deb77 --- /dev/null +++ b/note/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/note/views.py b/note/views.py new file mode 100644 index 00000000..60f00ef0 --- /dev/null +++ b/note/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/organiser/admin.py b/organiser/admin.py index 221e3afe..723342f2 100644 --- a/organiser/admin.py +++ b/organiser/admin.py @@ -1,125 +1,64 @@ # -*- coding: utf-8 -*- -from django.shortcuts import render_to_response -from django.http import HttpResponseRedirect, HttpResponse -from django.core.context_processors import csrf from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.contrib.auth.decorators import login_required -#models and forms +from photologue.forms import PhotoForm from models import Organiser -from accounts.models import User -from city.models import City from theme.models import Tag from forms import OrganiserForm, OrganiserFilterForm -from file.models import FileModel, TmpFile -from file.forms import FileModelForm -#python -import random -#custom functions -from functions.custom_views import objects_list, add_object_with_file -from functions.admin_views import AdminListView - - -def organiser_all(request): - """ - Return list of all organisers with pagination - """ - return objects_list(request, Organiser, 'organiser_all.html') - - - - -def organiser_add(request): - """ - Return form of organiser and post it on the server. - If form is posted redirect on the page of all organiser. - """ - return add_object_with_file(request, OrganiserForm, 'organiser_add.html', '/admin/organiser/all/', - choices={'city': City, 'tag': Tag}) - - -def organiser_change(request, url): - """ - Return form and fill it with existing Organiser object data. - - If form is posted redirect on the page of all organisers. - """ - organiser = Organiser.objects.safe_get(url=url) - # try get user by id if doesnt work by url - if organiser is None: - organiser = Organiser.objects.safe_get(id=url) - #redirect to list of all organisers if cannot find organiser - if organiser is None: - return HttpResponseRedirect('/admin/organiser/all/') - #get id - organiser_id = getattr(organiser, 'id') - #init FileModelForm - file_form = FileModelForm(initial={'model': 'organiser.Organiser'}) - - if request.POST: - form = OrganiserForm(request.POST) - #set choices filled by ajax - form.fields['tag'].choices = [(item.id, item.name) for item in Tag.objects.all()] - form.fields['city'].choices = [(item.id, item.name) for item in City.objects.filter(country=request.POST['country'])] - - if form.is_valid(): - form.save(id=organiser_id) - return HttpResponseRedirect('/admin/organiser/all/') - else: - #fill form with data from database - data = {'staff_number':organiser.staff_number, 'address': organiser.address, - 'events_number':organiser.events_number, 'phone':organiser.phone, - 'fax':organiser.fax, 'web_page':organiser.web_page, 'url':organiser.url, - 'email':organiser.email, 'social':organiser.social, 'foundation': organiser.foundation} - - data['user'] = User.objects.safe_get(organiser=organiser) - - if organiser.country: - data['country'] = organiser.country.id - - if organiser.city: - data['city'] = organiser.city.id - - data['theme'] = [item.id for item in organiser.theme.all()] - data['tag'] = [item.id for item in organiser.tag.all()] - data['place_exposition'] = [item.id for item in organiser.place_exposition.all()] - data['place_conference'] = [item.id for item in organiser.place_conference.all()] - #data from translated fields - - for code, name in settings.LANGUAGES: - obj = Organiser._meta.translations_model.objects.get(language_code = code,master__id=organiser_id) #access to translated fields - data['name_%s' % code] = obj.name - data['description_%s' % code] = obj.description - data['specialization_%s' % code] = obj.specialization - data['address_inf_%s' % code] = obj.address_inf - data['representation_%s' % code] = obj.representation - data['title_%s' % code] = obj.title - data['keywords_%s' % code] = obj.keywords - data['descriptions_%s' % code] = obj.descriptions - - #fill form - form = OrganiserForm(initial=data) - form.fields['city'].widget.attrs['data-init-text'] = organiser.city.name - #set choices filled by ajax - #form.fields['city'].choices = [(item.id, item.name) for item in City.objects.filter(country=data.get('country'))] - form.fields['tag'].choices = [(item.id, item.name) for item in Tag.objects.filter(theme__in=data.get('theme'))] - - args = {} - args.update(csrf(request)) - - args['form'] = form - args['languages'] = settings.LANGUAGES - args['file_form'] = file_form - - #get list of files which connected with specific model object - args['files'] = FileModel.objects.filter(content_type=ContentType.objects.get_for_model(organiser), - object_id=getattr(organiser, 'id')) - args['obj_id'] = organiser_id - - return render_to_response('organiser_add.html', args) +from functions.admin_views import AdminListView, AdminView class OrganiserListView(AdminListView): template_name = 'admin/organiser/organiser_list.html' form_class = OrganiserFilterForm - model = Organiser \ No newline at end of file + model = Organiser + +class OrganiserView(AdminView): + form_class = OrganiserForm + model = Organiser + success_url = '/admin/organiser/all/' + template_name = 'admin/organiser/organiser.html' + + def get_form(self, form_class): + if self.request.POST: + return super(OrganiserView, self).get_form(form_class) + obj = self.set_obj() + if obj: + data = {'staff_number':obj.staff_number, 'address': obj.address, + 'events_number':obj.events_number, 'phone':obj.phone, + 'fax':obj.fax, 'web_page':obj.web_page, 'url':obj.url, + 'email':obj.email, 'foundation': obj.foundation} + + if obj.country: + data['country'] = obj.country_id + + if obj.city: + data['city'] = obj.city_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['place_exposition'] = [item.id for item in obj.place_exposition.all()] + data['place_conference'] = [item.id for item in obj.place_conference.all()] + + + 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] = trans_obj.name + data['description_%s' % code] = trans_obj.description + data['specialization_%s' % code] = trans_obj.specialization + data['address_inf_%s' % code] = trans_obj.address_inf + data['representation_%s' % code] = trans_obj.representation + 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.filter(theme__in=data['theme'])] + return form + else: + return form_class() + + def get_context_data(self, **kwargs): + context = super(OrganiserView, self).get_context_data(**kwargs) + context['photo_form'] = PhotoForm() + return context \ No newline at end of file diff --git a/organiser/admin_urls.py b/organiser/admin_urls.py index e15df584..04262aa9 100644 --- a/organiser/admin_urls.py +++ b/organiser/admin_urls.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url -from admin import OrganiserListView +from admin import OrganiserListView, OrganiserView urlpatterns = patterns('organiser.admin', - url(r'^add.*/$', 'organiser_add'), - url(r'^change/(?P.*).*/$', 'organiser_change'), - #url(r'^all/$', 'organiser_all'), url(r'^all/$', OrganiserListView.as_view()), + url(r'^$', OrganiserView.as_view()), + url(r'^(?P.*)/$', OrganiserView.as_view()), ) diff --git a/organiser/forms.py b/organiser/forms.py index 63d18448..48db9dec 100644 --- a/organiser/forms.py +++ b/organiser/forms.py @@ -8,8 +8,7 @@ from django.core.validators import URLValidator from models import Organiser from country.models import Country from city.models import City -from theme.models import Theme -from accounts.models import User +from theme.models import Theme, Tag from place_exposition.models import PlaceExposition from place_conference.models import PlaceConference #functions @@ -30,14 +29,17 @@ class OrganiserForm(forms.Form): """ url = forms.CharField(label='url', required=False) - user = forms.ModelChoiceField(label='Пользователь', queryset=User.objects.all(), empty_label='',required=False) - country = forms.ModelChoiceField(label='Страна', queryset=Country.objects.all(), empty_label=None) - theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=Theme.objects.all()) - place_exposition = forms.ModelMultipleChoiceField(label='Места проведения выставок', queryset=PlaceExposition.objects.all(), required=False) - place_conference = forms.ModelMultipleChoiceField(label='Места проведения конференций', queryset=PlaceConference.objects.all(), required=False) + #user = forms.ModelChoiceField(label='Пользователь', queryset=User.objects.all(), empty_label='',required=False) + country = forms.ChoiceField(label='Страна', choices=[(item.id, item.name) for item in Country.objects.all()]) + city = forms.CharField(label='Город', widget=forms.HiddenInput()) + theme = forms.MultipleChoiceField(label='Тематики', choices=[(item.id, item.name) for item in Theme.objects.language().all()]) + place_exposition = forms.MultipleChoiceField(label='Места проведения выставок', required=False, + choices=[(item.id, item.name) for item in PlaceExposition.objects.language().all()]) + place_conference = forms.MultipleChoiceField(label='Места проведения конференций', required=False, + choices=[(item.id, item.name) for item in PlaceConference.objects.all()]) #creates select input with empty choices cause it will be filled with ajax - city = forms.ChoiceField(label='Город', choices=[('','')]) - tag = forms.MultipleChoiceField(label='Теги', required=False) + tag = forms.CharField(label='Теги', widget=forms.HiddenInput(), required=False) + logo = forms.ImageField(label='Logo', required=False) staff_number = forms.CharField(label='Количество сотрудников', required=False, widget=forms.TextInput(attrs={'placeholder': 'Количество сотрудников'})) @@ -50,16 +52,12 @@ class OrganiserForm(forms.Form): widget=forms.TextInput(attrs={'placeholder': 'Введите факс'})) web_page = forms.CharField(label='Веб-сайт', required=False, widget=forms.TextInput(attrs={'placeholder': 'Введите адрес сайта'})) - email = forms.CharField(label='Email', required=False, + email = forms.EmailField(label='Email', required=False, widget=forms.TextInput(attrs={'placeholder': 'Введите email'})) - social = forms.CharField(label='Социальные страници', required=False) events_number = forms.CharField(label='Количество мероприятий в год', required=False, widget=forms.TextInput(attrs={'placeholder': 'Количество'})) foundation = forms.CharField(label='Год основания', required=False, widget=forms.TextInput(attrs={'placeholder': 'Год основания'})) - #field for comparing tmp files - key = forms.CharField(required=False, widget=forms.HiddenInput()) - def __init__(self, *args, **kwargs): """ @@ -74,13 +72,13 @@ class OrganiserForm(forms.Form): # first iteration is a default lang so it required fields required = True if lid == 0 else False self.fields['name_%s' % code] = forms.CharField(label='Название', required=required) - self.fields['description_%s' % code] = forms.CharField(label='Описание', - required=False, widget=CKEditorWidget) self.fields['specialization_%s' % code] = forms.CharField(label='Специализация', required=False) - self.fields['address_inf_%s' % code] = forms.CharField(label='Доп инф по адресу', + self.fields['description_%s' % code] = forms.CharField(label='Описание', required=False, widget=CKEditorWidget) self.fields['representation_%s' % code] = forms.CharField(label='Представительства', required=False, max_length=255, widget=forms.TextInput(attrs={'style':'width: 550px'})) + self.fields['address_inf_%s' % code] = forms.CharField(label='Доп инф по адресу', + required=False, widget=CKEditorWidget) #meta data self.fields['title_%s' % code] = forms.CharField(label='Тайтл', required=False, max_length=255, widget=forms.TextInput(attrs={'style':'width: 550px'})) @@ -90,19 +88,19 @@ class OrganiserForm(forms.Form): widget=forms.TextInput(attrs={'style':'width: 550px'})) - def save(self, id=None): + def save(self, obj=None): """ - change organisers object with id = id + change organisers object N/A add new Organiser object usage: form.save(obj) - if change organiser form.save() - if add organiser """ data = self.cleaned_data #create new Organiser object or get exists - if not id: + if not obj: organiser = Organiser() else: - organiser = Organiser.objects.get(id=id) + organiser = obj organiser.theme.clear() organiser.tag.clear() organiser.place_conference.clear() @@ -116,12 +114,11 @@ class OrganiserForm(forms.Form): organiser.web_page = data['web_page'] organiser.email = data['email'] organiser.foundation = data['foundation'] - organiser.social = data['social'] organiser.staff_number = data['staff_number'] organiser.events_number = data['events_number'] if data.get('country'): - organiser.country = Country.objects.get(id=data['country'].id)#.id cause select uses queryset + organiser.country = Country.objects.get(id=data['country'])#.id cause select uses queryset if data.get('city'): organiser.city = City.objects.get(id=data['city']) @@ -129,37 +126,41 @@ class OrganiserForm(forms.Form): fill_with_signal(Organiser, organiser, data) #fill manytomany fields + organiser.theme.add(*Theme.objects.filter(id__in=data['theme'])) + organiser.tag.add(*Tag.objects.filter(id__in=data['tag'])) + organiser.place_exposition.add(*PlaceExposition.objects.filter(id__in=data['place_exposition'])) + organiser.place_conference.add(*PlaceConference.objects.filter(id__in=data['place_conference'])) - for item in data['theme']: - organiser.theme.add(item.id)#.id cause select uses queryset - - for item in data['tag']: - organiser.tag.add(item) - for item in data['place_exposition']: - organiser.place_exposition.add(item.id)#.id cause select uses queryset - - for item in data['place_conference']: - organiser.place_conference.add(item.id)#.id cause select uses queryset - - # uses because in the next loop data will be overwritten organiser.save() - #save files - check_tmp_files(organiser, data['key']) - #bound organiser to user + """ if data.get('user'): user = User.objects.safe_get(id=data['user'].id) if user: user.organiser = organiser user.save() + """ def clean_url(self): cleaned_data = super(OrganiserForm, self).clean() url = cleaned_data.get('url').strip() return url + 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_foundation(self): """ checking foundation diff --git a/organiser/models.py b/organiser/models.py index 10532826..8d09405b 100644 --- a/organiser/models.py +++ b/organiser/models.py @@ -17,6 +17,10 @@ class OrganiserManager(TranslationManager): except: return None +def logo_name(instance, filename): + url = instance.url + return '/'.join(['organiser', url, url+'_logo.jpg']) + class Organiser(TranslatableModel): """ @@ -38,6 +42,8 @@ class Organiser(TranslatableModel): tag = models.ManyToManyField('theme.Tag', verbose_name='Теги', blank=True, null=True) #address. uses LocationField. saves data in json format address = LocationField(verbose_name='Адресс', blank=True, null=True) + logo = models.ImageField(verbose_name='Logo', upload_to=logo_name, blank=True) + rating = models.IntegerField(default=0) phone = models.BigIntegerField(verbose_name='Телефон', blank=True, null=True) fax = models.BigIntegerField(verbose_name='Факс', blank=True, null=True) diff --git a/password_reset/forms.py b/password_reset/forms.py index 662a101b..fbfd0049 100644 --- a/password_reset/forms.py +++ b/password_reset/forms.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django import forms from django.core.validators import validate_email from django.db.models import Q @@ -29,7 +30,7 @@ class PasswordRecoveryForm(forms.Form): labels = { 'username': _('Username'), 'email': _('Email'), - 'both': _('Username or Email'), + 'both': _(u'Для восстановления пароля укажите email, который Вы регистрировали'), } User = get_user_model() # noqa if getattr(User, 'USERNAME_FIELD', 'username') == 'email': diff --git a/password_reset/templates/password_reset/recovery_form.html b/password_reset/templates/password_reset/recovery_form.html index bb84ac27..5046a9d6 100644 --- a/password_reset/templates/password_reset/recovery_form.html +++ b/password_reset/templates/password_reset/recovery_form.html @@ -5,8 +5,7 @@ {% block title %}{% trans "Password recovery" %}{% endblock %} {% block content %} -
- {% csrf_token %} + {% csrf_token %} {{ form.as_p }}

diff --git a/photologue/forms.py b/photologue/forms.py index 630fbec7..788b94a8 100644 --- a/photologue/forms.py +++ b/photologue/forms.py @@ -48,8 +48,8 @@ class PhotoForm(forms.Form): # uses enumerate for detect iteration number # first iteration is a default lang so it required fields required = True if lid == 0 else False - self.fields['title_%s' % code] = forms.CharField(label='Описание', required=required) - self.fields['caption_%s' % code] = forms.CharField(label='Заголовок', required=False) + self.fields['title_%s' % code] = forms.CharField(label='Описание', required=False) + self.fields['caption_%s' % code] = forms.CharField(label='Заголовок', required=required) def save(self, obj=None): data = self.cleaned_data diff --git a/photologue/models.py b/photologue/models.py index cd7a1904..eddf4de7 100644 --- a/photologue/models.py +++ b/photologue/models.py @@ -189,7 +189,10 @@ class Gallery(TranslatableModel): related_name='galleries', verbose_name=_('photos'), null=True, - blank=True) + blank=True, + #sorted=True, + #sort_value_field_name='sort_value' + ) tags = TagField(help_text=tagfield_help_text, verbose_name=_('tags')) sites = models.ManyToManyField(Site, verbose_name=_(u'sites'), blank=True, null=True) diff --git a/place_conference/models.py b/place_conference/models.py index b4cbdcab..170a8806 100644 --- a/place_conference/models.py +++ b/place_conference/models.py @@ -105,10 +105,10 @@ class PlaceConference(TranslatableModel, ExpoMixin): return PlaceConference.objects.filter(city=self.city).exclude(id=self.id)[:6] def get_catalog_url(self): - return '/places/' + return self.catalog def get_permanent_url(self): - url = '%splace-%s'%(self.get_catalog_url(), self.url) + url = '%s%s/'%(self.get_catalog_url(), self.url) return url diff --git a/place_exposition/admin.py b/place_exposition/admin.py index 9a09397b..ce467f5a 100644 --- a/place_exposition/admin.py +++ b/place_exposition/admin.py @@ -4,18 +4,16 @@ from django.shortcuts import render_to_response, get_object_or_404, HttpRespons from django.http import HttpResponseRedirect from django.core.context_processors import csrf from django.conf import settings -from django.forms.formsets import formset_factory -from django.forms.models import modelformset_factory from django.contrib.contenttypes.models import ContentType from django.contrib.auth.decorators import login_required -from django.forms.formsets import BaseFormSet, formset_factory +from django.forms.formsets import formset_factory from django.forms.models import modelformset_factory #models and forms from forms import ExpositionForm, PlaceExpositionFormDelete, HallForm, PlaceExpositionFilter from models import PlaceExposition, Hall 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 @@ -194,14 +192,13 @@ def exposition_change(request, url): return render_to_response('place_exposition_add.html', args) -#test---------------------- - +#new---------------------- class PlaceExpositionView(AdminView): form_class = ExpositionForm model = PlaceExposition - success_url = 'admin/place_exposition/all/' + success_url = '/admin/place_exposition/all/' template_name = 'admin/place_exposition/place_exposition.html' def get_form(self, form_class): @@ -245,9 +242,15 @@ class PlaceExpositionView(AdminView): def get_context_data(self, **kwargs): context = super(PlaceExpositionView, self).get_context_data(**kwargs) obj = self.set_obj() + if obj: + context['file_form'] = FileForm(initial={'model': 'place_exposition.PlaceExposition'}) + files = FileModel.objects.filter(content_type=ContentType.objects.get_for_model(obj),object_id=getattr(obj, 'id')) + context['files'] = files + context['halls'] = list(Hall.objects.language().filter(place_exposition=self.obj)) context['hall_form'] = HallForm() context['photo_form'] = PhotoForm() + return context def add_hall(request, place_id): diff --git a/place_exposition/admin_urls.py b/place_exposition/admin_urls.py index 53d1782b..47ab735d 100644 --- a/place_exposition/admin_urls.py +++ b/place_exposition/admin_urls.py @@ -9,13 +9,13 @@ urlpatterns = patterns('place_exposition.admin', url(r'^upload-photo/(?P.*)/$', 'upload_place_photo'), url(r'^all/$', PlaceExpositionListView.as_view()), - url(r'^add.*/$', 'exposition_add'), - url(r'^delete/(?P.*)/$', 'exposition_delete'), - url(r'^change/(?P.*)/$', 'exposition_change'), - url(r'^copy/(?P.*)/$', 'place_exposition_copy'), - - url(r'^$', PlaceExpositionView.as_view()), + #url(r'^add.*/$', 'exposition_add'), + #url(r'^delete/(?P.*)/$', 'exposition_delete'), + #url(r'^change/(?P.*)/$', 'exposition_change'), + #url(r'^copy/(?P.*)/$', 'place_exposition_copy'), url(r'^(?P.*)/$', PlaceExpositionView.as_view()), + url(r'^$', PlaceExpositionView.as_view()), + diff --git a/place_exposition/forms.py b/place_exposition/forms.py index 669ccbf6..d8a4a818 100644 --- a/place_exposition/forms.py +++ b/place_exposition/forms.py @@ -29,7 +29,7 @@ class ExpositionForm(forms.Form): type = forms.ChoiceField(required=False, choices=types) logo = forms.ImageField(label='Logo', required=False) - country = forms.ModelChoiceField(label='Страна', queryset=Country.objects.all(), empty_label=None) + country = forms.ChoiceField(label='Страна', choices=[(c.id, c.name) for c in Country.objects.all()]) # creates select input with empty choices cause it will be filled with ajax city = forms.CharField(label='Город', widget=forms.HiddenInput()) @@ -69,10 +69,6 @@ class ExpositionForm(forms.Form): parking = forms.BooleanField(label='Парковка', required=False) press_centre = forms.BooleanField(label='Пресс-центр', required=False) mobile_application = forms.BooleanField(label='Мобильное приложение', required=False) - # - key = forms.CharField(required=False, widget=forms.HiddenInput()) - # - place_exposition_id = forms.CharField(required=False, widget=forms.HiddenInput()) def __init__(self, *args, **kwargs): """ @@ -121,8 +117,8 @@ class ExpositionForm(forms.Form): place_exposition.url = translit_with_separator(data['name_en'].strip()).lower() else: place_exposition.url = translit_with_separator(data['name_ru'].strip()).lower() - - place_exposition.logo = data['logo'] + if data.get('logo'): + place_exposition.logo = data['logo'] place_exposition.type = data['type'] place_exposition.address = data['address'] place_exposition.phone = data['phone'] @@ -149,29 +145,14 @@ class ExpositionForm(forms.Form): place_exposition.mobile_application = data['mobile_application'] if data.get('country'): - place_exposition.country = Country.objects.get(id=data['country'].id)#.id cause select uses queryset + place_exposition.country = Country.objects.get(id=data['country']) if data.get('city'): place_exposition.city = City.objects.get(id=data['city']) fill_with_signal(PlaceExposition, place_exposition, data) place_exposition.save() - #save files - check_tmp_files(place_exposition, data['key']) - - return PlaceExposition.objects.get(id=place_exposition.id) - - def clean(self): - id = self.cleaned_data.get('place_exposition_id') - name_ru = self.cleaned_data.get('name_ru') - - place_exposition = PlaceExposition.objects.filter(url=translit_with_separator(name_ru)) - if place_exposition and str(place_exposition[0].id) != id: - msg = 'Место проведения с таким названием уже существует' - self._errors['name_ru'] = ErrorList([msg]) - del self.cleaned_data['name_ru'] - - return self.cleaned_data + return place_exposition def clean_phone(self): diff --git a/place_exposition/management/commands/place_exposition_load.py b/place_exposition/management/commands/place_exposition_load.py index c880b5c6..b47945e8 100644 --- a/place_exposition/management/commands/place_exposition_load.py +++ b/place_exposition/management/commands/place_exposition_load.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- from django.core.management.base import BaseCommand, CommandError -from city.models import City -from country.models import Country from place_exposition.models import PlaceExposition import xlrd, xlwt from import_xls.excel_settings import import_settings, place_exp_sett diff --git a/place_exposition/models.py b/place_exposition/models.py index 361cf358..5d90d64b 100644 --- a/place_exposition/models.py +++ b/place_exposition/models.py @@ -26,6 +26,11 @@ EXPOSITION_TYPE = (('Exposition complex', u'Выставочный компле dist=lambda s,d: (s[0]-d[0])**2+(s[1]-d[1])**2 +def logo_name(instance, filename): + url = instance.url + return '/'.join(['place_exposition', url, url+'_logo.jpg']) + + class PlaceExposition(TranslatableModel, ExpoMixin): """ Create PlaceConference model @@ -33,9 +38,11 @@ class PlaceExposition(TranslatableModel, ExpoMixin): Uses hvad.TranslatableModel which is child of django.db.models class """ + catalog = '/places/' + catalog_name = _(u'Места:') + search_name = None #set manager of this model objects = ExpoManager() - catalog = '/places/' url = models.SlugField(unique=True, max_length=255) country = models.ForeignKey('country.Country', on_delete=models.PROTECT) @@ -71,16 +78,12 @@ class PlaceExposition(TranslatableModel, ExpoMixin): press_centre = models.NullBooleanField() mobile_application = models.NullBooleanField() # - logo = models.ImageField(verbose_name='Logo', upload_to='place_exposition/logo/', blank=True) + logo = models.ImageField(verbose_name='Logo', upload_to=logo_name, blank=True) rating = models.IntegerField(default=0) - # - - #logo - #scheme # delete after profiling files = generic.GenericRelation('file.FileModel', content_type_field='content_type', object_id_field='object_id') - photos = generic.GenericRelation('file.Photo', content_type_field='content_type', object_id_field='object_id') + #photos = generic.GenericRelation('file.Photo', content_type_field='content_type', object_id_field='object_id') #translations is translated fields translations = TranslatedFields( @@ -104,7 +107,7 @@ class PlaceExposition(TranslatableModel, ExpoMixin): views = models.PositiveIntegerField(default=0) is_published = models.BooleanField(default=1) class Meta: - ordering = ['translations__name'] + ordering = ['-rating', 'id'] def get_gallery(self): if self.photogallery: @@ -141,8 +144,10 @@ class PlaceExposition(TranslatableModel, ExpoMixin): def get_index_text(self): - names = [tr.name for tr in self.translations.all()] - return names + translations = self.translations.all() + names = ' '.join([tr.name for tr in translations]) + titles = ' '.join([tr.main_title for tr in translations]) + return names + ' ' + titles def __unicode__(self): @@ -152,10 +157,10 @@ class PlaceExposition(TranslatableModel, ExpoMixin): return _(u'Места') def get_catalog_url(self): - return '/places/' + return self.catalog def get_permanent_url(self): - url = '%splace-%s'%(self.get_catalog_url(), self.url) + url = '%s%s/'%(self.catalog, self.url) return url def get_logo(self): @@ -199,7 +204,7 @@ class PlaceExposition(TranslatableModel, ExpoMixin): return qs_hotels_all.filter(res) def get_type(self): - type = {'Convention centre': _(u'Конгессо-выставочный центр'), 'Exposition centre': _(u'Выставочный центр'), + type = {'Convention centre': _(u'Конгрессно-выставочный центр'), 'Exposition centre': _(u'Выставочный центр'), 'Exposition complex': _(u'Выставочный комплекс')} return type.get(self.type) diff --git a/place_exposition/search_indexes.py b/place_exposition/search_indexes.py index 6dc54a7e..da84b3e4 100644 --- a/place_exposition/search_indexes.py +++ b/place_exposition/search_indexes.py @@ -1,28 +1,35 @@ +# -*- coding: utf-8 -*- from haystack import indexes from models import PlaceExposition +from functions.search_mixin import ExpoSearchMixin -class PlaceExpositionIndex(indexes.SearchIndex, indexes.Indexable): +class PlaceExpositionIndex(indexes.SearchIndex, indexes.Indexable, ExpoSearchMixin): text = indexes.CharField(document=True, use_template=True) + where = indexes.MultiValueField() - country = indexes.CharField(model_attr='country', null=True) - #city = indexes.CharField(model_attr='city', null=True) + content_auto = indexes.EdgeNgramField() + area_id = indexes.IntegerField() + country_id = indexes.IntegerField() + city_id = indexes.IntegerField() + url = indexes.CharField() + form_name = indexes.CharField() + # translated fields + name_en = indexes.CharField() + name_ru = indexes.CharField() + catalog_name_en = indexes.CharField() + catalog_name_ru = indexes.CharField() + - def prepare_country(self, obj): - if obj.country: - return '%s'%obj.country.id - return '' - def prepare_city(self, obj): - if obj.city: - return '%s'%obj.country.city - return '' + def prepare_form_name(self, obj): + return None - def prepare_where(self, obj): - country = [tr.name for tr in obj.country.translations.all()] - city = [tr.name for tr in obj.city.translations.all()] + def prepare_catalog_name_en(self, obj): + return u'Places' - return country + city + def prepare_catalog_name_ru(self, obj): + return u'Места' def get_model(self): return PlaceExposition diff --git a/place_exposition/urls.py b/place_exposition/urls.py new file mode 100644 index 00000000..af64dd62 --- /dev/null +++ b/place_exposition/urls.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from django.conf.urls import patterns, include, url +from views import PlaceSearchView, PlaceDetail, PlaceList, PlaceCityCatalog, PlaceCountryCatalog, PlacePhoto + + + +urlpatterns = patterns('', + # correct + url(r'search/', PlaceSearchView.as_view()), + # correct + #url(r'country/$', PlaceCountryCatalog.as_view()), + url(r'country/(?P.*)/page/(?P\d+)/$', PlaceCountryCatalog.as_view()), + url(r'country/(?P.*)/$', PlaceCountryCatalog.as_view()), + # correct + #url(r'expo/city/$', ExpositionByCity.as_view()), + url(r'city/(?P.*)/page/(?P\d+)/$', PlaceCityCatalog.as_view()), + url(r'city/(?P.*)/$', PlaceCityCatalog.as_view()), + + #!!! + url(r'(?P.*)/photo/page/(?P\d+)/$', PlacePhoto.as_view()), + url(r'(?P.*)/photo/$', PlacePhoto.as_view()), + #url(r'expo/(?P.*)/service/(?P.*)/$', ExpositionServiceView.as_view()), + url(r'(?P.*)/$', PlaceDetail.as_view()), + url(r'page/(?P\d+)/$', PlaceList.as_view()), + url(r'$', PlaceList.as_view()), + ) diff --git a/place_exposition/views.py b/place_exposition/views.py index bce56c6d..aac4d1dc 100644 --- a/place_exposition/views.py +++ b/place_exposition/views.py @@ -1,13 +1,17 @@ # -*- coding: utf-8 -*- from django.shortcuts import render_to_response +from django.views.generic import ListView, DetailView, FormView +from django.utils import translation from django.http import HttpResponseRedirect, HttpResponse from django.template import RequestContext from django.core.context_processors import csrf from django.http import Http404 from django.shortcuts import get_object_or_404 +from django.utils.translation import ugettext as _ #models from place_conference.models import PlaceConference - +from country.models import Country +from city.models import City from models import PlaceExposition def catalog(request): @@ -32,4 +36,145 @@ def place(request, url, photo=None): if photo: args['object'] = place return render_to_response('photoreport.html', args, context_instance=RequestContext(request)) - return render_to_response('place.html', args, context_instance=RequestContext(request)) \ No newline at end of file + return render_to_response('place.html', args, context_instance=RequestContext(request)) + +from functions.custom_views import ExpoSearchView +from functions.search_forms import PlaceSearchForm + +class PlaceSearchView(ExpoSearchView): + #paginate_by = 10 + template_name = 'place/search.html' + search_form = PlaceSearchForm + model = PlaceExposition + +class PlaceDetail(DetailView): + model = PlaceExposition + search_form = PlaceSearchForm + slug_field = 'url' + template_name = 'client/place/place_detail.html' + + def get_object(self, queryset=None): + """ + Returns the object the view is displaying. + By default this requires `self.queryset` and a `pk` or `slug` argument + in the URLconf, but subclasses can override this to return any object. + """ + # Use a custom queryset if provided; this is required for subclasses + # like DateDetailView + if queryset is None: + queryset = self.get_queryset() + # Next, try looking up by primary key. + pk = self.kwargs.get(self.pk_url_kwarg, None) + slug = self.kwargs.get(self.slug_url_kwarg, None) + if pk is not None: + queryset = queryset.filter(pk=pk) + # Next, try looking up by slug. + elif slug is not None: + slug_field = self.get_slug_field() + queryset = queryset.filter(**{slug_field: slug}) + # If none of those are defined, it's an error. + else: + raise AttributeError("Generic detail view %s must be called with " + "either an object pk or a slug." + % self.__class__.__name__) + try: + # Get the single item from the filtered queryset + obj = queryset.get() + except queryset.model.DoesNotExist: + try: + PlaceConference.objects.get(url=slug) + except PlaceConference.DoesNotExist: + + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) + return obj + + def get_context_data(self, **kwargs): + context = super(PlaceDetail, self).get_context_data(**kwargs) + context['search_form'] = self.search_form + return context + +class PlacePhoto(ListView): + template_name = 'client/place/photo.html' + obj = None + search_form = PlaceSearchForm + + def get_queryset(self): + slug = self.kwargs.get('slug') + try: + place = PlaceExposition.objects.get(url=slug) + except PlaceExposition.DoesNotExist: + try: + place = PlaceConference.objects.get(url=slug) + except PlaceConference.DoesNotExist: + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': PlaceExposition._meta.verbose_name}) + self.obj = place + return place.photogallery.photos.all() + + def get_context_data(self, **kwargs): + context = super(PlacePhoto, self).get_context_data(**kwargs) + context['object'] = self.obj + context['search_form'] = self.search_form + return context + + +class PlaceList(ListView): + model = PlaceExposition + paginate_by = 10 + template_name = 'client/place/place_list.html' + search_form = PlaceSearchForm + + + def get_queryset(self): + lang = translation.get_language() + qs = super(PlaceList, self).get_queryset().filter(language_code=lang) + conf_qs = PlaceConference.objects.language().all() + return list(qs)+list(conf_qs) + + def get_context_data(self, **kwargs): + context = super(PlaceList, self).get_context_data(**kwargs) + context['search_form'] = self.search_form + return context + + +class PlaceCatalog(ListView): + model = PlaceExposition + paginate_by = 10 + template_name = 'place/catalog.html' + search_form = PlaceSearchForm + filter_object = None + + + def get_context_data(self, **kwargs): + context = super(PlaceCatalog, self).get_context_data(**kwargs) + context['search_form'] = self.search_form + context['filter_object'] = self.filter_object + + context['catalog_url'] = self.catalog_url + return context + + +class PlaceCountryCatalog(PlaceCatalog): + catalog_url = '/places/' + + def get_queryset(self): + slug = self.kwargs.get('slug') + country = get_object_or_404(Country, url=slug) + self.filter_object = country + qs = self.model.objects.language().filter(country=country).order_by('-rating') + conf_qs = PlaceConference.objects.language().filter(country=country) + + return list(qs) + list(conf_qs) + + +class PlaceCityCatalog(PlaceCatalog): + catalog_url = '/places/' + + def get_queryset(self): + slug = self.kwargs.get('slug') + city = get_object_or_404(City, url=slug) + self.filter_object = city + qs = self.model.objects.language().filter(city=city).order_by('-rating') + conf_qs = PlaceConference.objects.language().filter(city=city) + return list(qs) + list(conf_qs) \ No newline at end of file diff --git a/proj/admin.py b/proj/admin.py index 3dfed6e6..1d2114b2 100644 --- a/proj/admin.py +++ b/proj/admin.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- from django.shortcuts import render_to_response -from django.http import HttpResponseRedirect, HttpResponse +from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.contrib.contenttypes.models import ContentType from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.admin.views.decorators import staff_member_required from file.models import TmpFile, FileModel -from file.forms import FileModelForm +from file.forms import FileModelForm, FileForm from city.models import City from theme.models import Tag from django.db.models.loading import get_model @@ -39,7 +39,7 @@ def ajax_tag(request): else: #request empty - send empty array return HttpResponse('[]', content_type='application/json') - +''' def ajax_post_file(request, obj_id=None): """ Takes file and file data and save it @@ -71,6 +71,42 @@ def ajax_post_file(request, obj_id=None): args['languages'] = settings.LANGUAGES args['file_form'] = file_form return render_to_response('ajax_error_form.html', args) +''' +from django.core.context_processors import csrf +def ajax_post_file(request, obj_id=None): + """ + Takes file and file data and save it + + Returns 'file_list.html' template with existing files if id != None. + + """ + if request.POST: + if not obj_id: + raise Http404 + file_form = FileForm(request.POST, request.FILES) + if file_form.is_valid(): + #takes data from hidden input "model" and initial Model + Model = get_model(request.POST['model'].split('.')[0], request.POST['model'].split('.')[1]) + #initial model object + obj = Model.objects.get(id=obj_id) + file = file_form.save(request.FILES, obj) + + files = FileModel.objects.filter(content_type=ContentType.objects.get_for_model(obj),object_id=getattr(obj, 'id')) + + + return render_to_response('file_list.html', {'files' : files}) + else: + args = {} + + args['languages'] = settings.LANGUAGES + args['file_form'] = file_form + args['obj_id'] = obj_id + args.update(csrf(request)) + return render_to_response('ajax_error_form.html', args) + else: + raise Http404 + + from file.forms import PhotoForm from file.models import Photo diff --git a/proj/settings.py b/proj/settings.py index a120e2d0..f9d61c5c 100644 --- a/proj/settings.py +++ b/proj/settings.py @@ -161,6 +161,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( MIDDLEWARE_CLASSES = ( # 'django.middleware.cache.UpdateCacheMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', @@ -171,8 +172,8 @@ MIDDLEWARE_CLASSES = ( # 'django.middleware.cache.FetchFromCacheMiddleware', # Uncomment the next line for simple clickjacking protection: 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware',#должно быть последним полем + #'debug_toolbar.middleware.DebugToolbarMiddleware', ) @@ -227,7 +228,7 @@ EMAIL_HOST = 'smtp.gmail.com' #EMAIL_HOST = 'localhost' #EMAIL_HOST_USER = 'kotzilla' EMAIL_HOST_USER = 'kotzillla@gmail.com' -EMAIL_HOST_PASSWORD = 'fitter2006' +EMAIL_HOST_PASSWORD = 'fitteR2006!' EMAIL_PORT = 587 @@ -315,6 +316,7 @@ INSTALLED_APPS = ( 'exposition', 'file', 'news', + 'note', 'organiser', 'place_conference', 'place_exposition', @@ -327,6 +329,7 @@ INSTALLED_APPS = ( 'theme', 'translator', 'webinar', + 'meta', #django modules 'sorl.thumbnail', 'photologue', @@ -338,15 +341,22 @@ INSTALLED_APPS = ( 'bitfield', 'djutils', 'pytils', + 'pymorphy', 'password_reset', #'social_auth', 'social.apps.django_app.default', # 'south', -# 'debug_toolbar', + #'debug_toolbar', ) -#INTERNAL_IPS = ('176.121.5.82',) -#DEBUG_TOOLBAR_PATCH_SETTINGS = False - +INTERNAL_IPS = ('176.121.5.82',) +DEBUG_TOOLBAR_PATCH_SETTINGS = False +#DEBUG_TOOLBAR_PANELS = ( + + # 'debug_toolbar.panels.profiling.ProfilingPanel', +#) +PYMORPHY_DICTS = { + 'ru': { 'dir': '/home/www/proj/settings/russian_dicts' }, +} # search backend HAYSTACK_CONNECTIONS = { @@ -401,4 +411,5 @@ except ImportError, e: from production import * """ ADMIN_PAGINATION = 20 +CLIENT_PAGINATION = 15 TEMPLATE_DEBUG = DEBUG \ No newline at end of file diff --git a/proj/urls.py b/proj/urls.py index cd85dcda..4ab41738 100644 --- a/proj/urls.py +++ b/proj/urls.py @@ -1,18 +1,21 @@ # -*- coding: utf-8 -*- from django.conf import settings from django.conf.urls import patterns, include, url -from core.views import PlaceListView, PlacePhotoView, PlaceSearchView, EventSearchView +from core.views import PlaceListView, PlacePhotoView, EventSearchView from core.simple_index_view import AdvertisingView, AboutView -from views import MainPageView +from views import MainPageView, MainPageViewTest +from place_exposition.views import PlaceSearchView from django.http import HttpResponse -def check_forward(request, *args, **kwargs): - return HttpResponse(request.META.get('HTTP_X_FORWARDED_FOR', None)) + urlpatterns = patterns('', - url(r'^bla/$', check_forward), + url(r'^admin/', include('proj.admin_urls')), url(r'^$', MainPageView.as_view()), + url(r'^main_page/$', MainPageViewTest.as_view()), url(r'^theme/', include('theme.urls')), + url(r'^places/', include('place_exposition.urls')), + url(r'^', include('accounts.urls')), url(r'^', include('exposition.urls')), url(r'^', include('conference.urls')), @@ -35,14 +38,14 @@ urlpatterns = patterns('', url(r'^about/$', AboutView.as_view()), url(r'^partners/$', AboutView.as_view()), url(r'^contacts/$', AboutView.as_view()), - url(r'^events/search/$', EventSearchView.as_view()), - url(r'^places/search/$', PlaceSearchView.as_view()), - url(r'^places/(?P.*)/photo/(?P\d+)/$', PlacePhotoView.as_view()), - url(r'^places/(?P.*)/photo/$', PlacePhotoView.as_view()), - url(r'^places/(?P.*)/(?P\d+)/$', PlaceListView.as_view()), - url(r'^places/(?P\d+)/$', PlaceListView.as_view()), - url(r'^places/(?P.*)/$', PlaceListView.as_view()), - url(r'^places/$', PlaceListView.as_view()), + #url(r'^events/search/$', EventSearchView.as_view()), + #url(r'^places/search/$', PlaceSearchView.as_view()), + #url(r'^places/(?P.*)/photo/(?P\d+)/$', PlacePhotoView.as_view()), + #url(r'^places/(?P.*)/photo/$', PlacePhotoView.as_view()), + #url(r'^places/(?P.*)/(?P\d+)/$', PlaceListView.as_view()), + #url(r'^places/(?P\d+)/$', PlaceListView.as_view()), + #url(r'^places/(?P.*)/$', PlaceListView.as_view()), + #url(r'^places/$', PlaceListView.as_view()), url(r'^social/', include('social.apps.django_app.urls', namespace='social')), url(r'^login/', 'registration.backends.default.views.LoginView'), url(r'^logout/', 'registration.backends.default.views.LogoutView'), @@ -59,7 +62,7 @@ urlpatterns = patterns('', # admin part url(r'^search/', include('haystack.urls')), - url(r'^admin/', include('proj.admin_urls')), + url(r'^', include('service.urls')), ) @@ -69,15 +72,22 @@ urlpatterns += patterns('', url(r'^register/', 'registration.backends.default.views.RegisterAjaxView'), url(r'^register-complete/', 'registration.backends.default.views.complete_registration'), url(r'^callback/', 'core.simple_index_view.callback'), - url(r'^search-form/', 'settings.views.sub_category'), + url(r'^search-form/get-parent/$', 'settings.views.search_parent'), + url(r'^search-form/autocomplete/$', 'settings.views.search_autocomplete'), + url(r'^search-form/$', 'settings.views.sub_category'), + url(r'^search-form/autosearch/exposition/$', 'settings.views.expo_autosearch'), + url(r'^search-form/autosearch/place/$', 'settings.views.place_autosearch'), + url(r'^search-form/autosearch/company/$', 'settings.views.company_autosearch'), + + # url(r'^profile/change-password/', 'accounts.views.change_password'), ) - +""" if settings.DEBUG: import debug_toolbar urlpatterns += patterns('', url(r'^__debug__/', include(debug_toolbar.urls)), ) - +""" \ No newline at end of file diff --git a/proj/views.py b/proj/views.py index 87dca9d8..bbc0adc0 100644 --- a/proj/views.py +++ b/proj/views.py @@ -10,12 +10,13 @@ from news.models import News from article.models import Article from functions.forms import ThemeSearch, PlaceSearch -from functions.search_forms import EventSearchForm + +from functions.search_forms import EventSearchForm, ExpositionSearchForm from functions.custom_views import ExpoListView from accounts.forms import RegistrationCompleteForm def expo_context(request): - cont = {'theme_search_form': ThemeSearch(), 'place_search_form': PlaceSearch(), 'expo_catalog': Exposition.catalog, + cont = {'theme_search_form': ThemeSearch(), 'search_form': ExpositionSearchForm(), 'expo_catalog': Exposition.catalog, 'book_aid': settings.BOOKING_AID} user = request.user @@ -48,27 +49,27 @@ class MainPageView(TemplateView): return context +class MainPageViewTest(TemplateView): + template_name = 'client/main_page.html' -def home(request): - #reg_form = RegistrationFormUniqueEmail() - #login_form = LoginForm() - #args = {'reg_form': reg_form, 'login_form': login_form} - events = Exposition.objects.all().order_by('-main_page')[:5] - exposition_themes = Theme.objects.order_by('-main_page').filter(types=Theme.types.exposition)[:6] - conference_themes = Theme.objects.order_by('-main_page').filter(types=Theme.types.conference)[:6] - seminar_themes = Theme.objects.order_by('-main_page').filter(types=Theme.types.seminar)[:6] - news_list = News.objects.order_by('-main_page').all()[:3] - articles = Article.objects.order_by('-main_page').all()[:2] - args = {'events': events, 'exposition_themes': exposition_themes, + def get_context_data(self, **kwargs): + context = super(MainPageViewTest, self).get_context_data(**kwargs) + events = Exposition.objects.all().order_by('-main_page')[:5] + exposition_themes = Theme.objects.order_by('-main_page').filter(types=Theme.types.exposition)[:6] + conference_themes = Theme.objects.order_by('-main_page').filter(types=Theme.types.conference)[:6] + seminar_themes = Theme.objects.order_by('-main_page').filter(types=Theme.types.seminar)[:6] + news_list = News.objects.order_by('-main_page').all()[:3] + articles = Article.objects.order_by('-main_page').all()[:2] + + args = {'events': events, 'exposition_themes': exposition_themes, 'conference_themes': conference_themes, 'seminar_themes': seminar_themes, 'news_list': news_list, 'articles': articles} - args.update(csrf(request)) - return render_to_response('index.html', args, context_instance=RequestContext(request)) + context.update(args) + + return context -class Test(ExpoListView): - model = Exposition diff --git a/registration/backends/default/views.py b/registration/backends/default/views.py index 416f5df5..06e3ac06 100644 --- a/registration/backends/default/views.py +++ b/registration/backends/default/views.py @@ -157,7 +157,7 @@ from django.views.decorators.cache import never_cache @sensitive_post_parameters('password1', 'password2') @never_cache def RegisterAjaxView(request): - if request.is_ajax(): + #if request.is_ajax(): data = {'success': False} if request.POST: form = RegistrationFormUniqueEmail(request.POST) @@ -183,9 +183,9 @@ def RegisterAjaxView(request): else: # 404 return HttpResponse('not post') - else: + #else: # 404 - return HttpResponse('not ajax') + # return HttpResponse('not ajax') from django.contrib.auth.forms import AuthenticationForm from registration.forms import LoginForm @@ -205,7 +205,9 @@ def LoginView(request): if form.is_valid(): login(request, form.get_user()) - return HttpResponseRedirect(request.META.get('HTTP_REFERER','/')) + response= {'success':True} + return HttpResponse(json.dumps(response), content_type='application/json') + #return HttpResponseRedirect(request.META.get('HTTP_REFERER','/')) else: response={'success':False, 'errors': form.errors} diff --git a/seminar/models.py b/seminar/models.py index c5e9eaa0..f25ac9d5 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -24,6 +24,9 @@ class Seminar(TranslatableModel, EventMixin, ExpoMixin): Uses hvad.TranslatableModel which is child of django.db.models class """ + catalog = '/seminar/' + # type of event + event_type = 'seminar' #set manager of this model objects = ExpoManager() services = BitField(flags=flags) diff --git a/service/models.py b/service/models.py index ae3d2d19..377f4578 100644 --- a/service/models.py +++ b/service/models.py @@ -1,13 +1,18 @@ # -*- coding: utf-8 -*- from django.db import models -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager from functions.custom_fields import EnumField CURENCIES = ('USD', 'RUB', 'EUR') + + + + class Service(TranslatableModel): + url = models.SlugField(unique=True) currency = EnumField(values=CURENCIES, blank=True, default='USD') price = models.PositiveIntegerField(blank=True, null=True) @@ -24,9 +29,10 @@ class Service(TranslatableModel): descriptions = models.CharField(max_length=255, blank=True), keywords = models.CharField(max_length=255, blank=True), ) - + sort = models.PositiveIntegerField(default=0, db_index=True) main_page = models.PositiveIntegerField(default=0, db_index=True) + def __unicode__(self): return self.lazy_translation_getter('name', self.pk) @@ -36,6 +42,12 @@ class Service(TranslatableModel): def get_permanent_url(self): return '/service/%s/'%self.url + def get_price(self): + pr = self.price + + + + from django.db.models.signals import post_save from functions.signal_handlers import post_save_handler diff --git a/settings/russian_dicts/endings.sqlite b/settings/russian_dicts/endings.sqlite new file mode 100644 index 00000000..7bd4ae08 Binary files /dev/null and b/settings/russian_dicts/endings.sqlite differ diff --git a/settings/russian_dicts/freq.sqlite b/settings/russian_dicts/freq.sqlite new file mode 100644 index 00000000..2cc494e4 Binary files /dev/null and b/settings/russian_dicts/freq.sqlite differ diff --git a/settings/russian_dicts/lemmas.sqlite b/settings/russian_dicts/lemmas.sqlite new file mode 100644 index 00000000..de52d309 Binary files /dev/null and b/settings/russian_dicts/lemmas.sqlite differ diff --git a/settings/russian_dicts/misc.sqlite b/settings/russian_dicts/misc.sqlite new file mode 100644 index 00000000..f0e01dec Binary files /dev/null and b/settings/russian_dicts/misc.sqlite differ diff --git a/settings/russian_dicts/rules.sqlite b/settings/russian_dicts/rules.sqlite new file mode 100644 index 00000000..c2a1092a Binary files /dev/null and b/settings/russian_dicts/rules.sqlite differ diff --git a/settings/templatetags/tempalte_tags.py b/settings/templatetags/tempalte_tags.py new file mode 100644 index 00000000..7c23ccc5 --- /dev/null +++ b/settings/templatetags/tempalte_tags.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +import datetime +from django import template +from django.utils.translation import ugettext as _ + +register = template.Library() + + + + + +class Date(template.Node): + + def __init__(self): + self.now = datetime.datetime.today() + + + def render(self, context): + monthes = {1: {'url':'jan', 'name': _(u'Январь')}, 2: {'url':'feb', 'name': _(u'Февраль')}, + 3: {'url': 'mar', 'name':_(u'Март')}, 4: {'url':'apr', 'name': _(u'Апрель')}, + 5: {'url':'may', 'name': _(u'Май')}, 6: {'url':'jun', 'name': _(u'Июнь')}, + 7: {'url':'jul', 'name': _(u'Июль')}, 8: {'url':'aug', 'name': _(u'Август')}, + 9: {'url':'sep', 'name': _(u'Сентябрь')}, 10: {'url':'oct', 'name': _(u'Октябрь')}, + 11: {'url':'nov', 'name': _(u'Ноябрь')}, 12: {'url':'dec', 'name': _(u'Декабрь')} + } + date = self.now + month = monthes[date.month] + month_period =[{'month_url': month['url'], 'name':month['name'], 'date':date}] + for i in range(5): + try: + date = date.replace(month=date.month+1) + except ValueError: + if date.month == 12: + date = date.replace(year=date.year+1, month=1) + else: + raise + month = monthes.get(date.month) + month_period.append({'month_url': month['url'], 'name':month['name'], 'date':date}) + + context['monthes_period'] = month_period + return u"" + +def get_date(parser, token): + """ + {% set = %} + """ + + return Date() + + +register.tag('get_date', get_date) + diff --git a/settings/templatetags/template_filters.py b/settings/templatetags/template_filters.py index e87345da..a0e67d61 100644 --- a/settings/templatetags/template_filters.py +++ b/settings/templatetags/template_filters.py @@ -115,11 +115,18 @@ def add_month(date, month=1): @register.filter def in_calendar(event, user): - calendar = user.calendar + if event: + calendar = user.calendar + return calendar.check_in_calendar(event) + else: + return False + """ + types = {'expo':'', 'conf':'', 'seminar':'', 'webinar':''} if event in calendar.get_events(): return True return False + """ @register.filter @@ -193,4 +200,28 @@ def set_var(parser, token): raise template.TemplateSyntaxError("'set' tag must be of the form: {% set = %}") return SetVarNode(parts[1], parts[3]) -register.tag('set', set_var) \ No newline at end of file +register.tag('set', set_var) + +@register.filter +def without_page(value): + l = value.split('/') + if not 'page' in l: + return value + else: + result = '/'.join(l[:l.index('page')])+'/' + return result + +@register.filter +def note_by_user(obj, user): + + return obj.get_note_by_user(user.id) + +@register.filter +def isdigit(value): + return value.isdigit() + +@register.filter +def next_monthes(value): + a = value + return value + diff --git a/settings/views.py b/settings/views.py index c46e1a0a..3b56d8a6 100644 --- a/settings/views.py +++ b/settings/views.py @@ -1,18 +1,26 @@ +# -*- coding: utf-8 -*- import json from django.shortcuts import HttpResponse, get_object_or_404 from django.http import Http404 -from country.models import Country, Area +from django.utils import translation +from haystack.query import SearchQuerySet +from exposition.models import Exposition +from country.models import Country, Area, City from theme.models import Tag, Theme +from place_exposition.models import PlaceExposition +from place_conference.models import PlaceConference +from company.models import Company # every this model must have method get_subcategories categories = {'area':{'sub':True, 'model':Area, 'sub_categorie_name':'co'}, - 'co':{'sub':False, 'model':Country, 'sub_categorie_name':'co'}, - 'th':{'sub':False, 'model':Theme, 'sub_categorie_name':'tg'}} + 'co':{'sub':False, 'model':Country, 'sub_categorie_name':'ci'}, + 'th':{'sub':False, 'model':Theme, 'sub_categorie_name':'tg'}, + 'ci':{'sub':False, 'model':City, 'sub_categorie_name':None}, + 'tg':{'sub':False, 'model':Tag, 'sub_categorie_name':None}} def sub_category(request): if request.GET: - name = request.GET['name'] categorie = categories.get(name) if not categorie: @@ -27,5 +35,90 @@ def sub_category(request): else: raise Http404 +search_forms = {'theme':'', 'place':''} +# сделать с помощью haystack!!! +def search_autocomplete(request): + if request.GET: + lang = translation.get_language() + term = request.GET['term'] + form = request.GET['form'] + if form == 'place': + areas = [{'text':item.name, 'id':item.id, 'name':'area'} for item in Area.objects.filter(translations__name__contains=term)] + countries = [{'text':item.name, 'id':item.id, 'name':'co'} for item in Country.objects.select_related('exposition_country')\ + .filter(exposition_country__country__isnull=False, translations__language_code=lang, translations__name__contains=term).distinct()] + cities = [{'text':item.name, 'id':item.id, 'name':'ci'} for item in City.objects.select_related('exposition_city')\ + .filter(exposition_city__city__isnull=False, translations__language_code=lang, translations__name__contains=term).distinct()] + + objects = areas + countries + cities + return HttpResponse(json.dumps(objects), content_type='application/json') + if form == 'subj': + objects = [{'text': get_by_lang(item, 'name', lang), 'id':item.pk, 'name': item.form_name} for item in SearchQuerySet().models(Theme, Tag).autocomplete(content_auto=term)] + #themes = [{'text':item.name, 'id':item.id, 'name':'th'} for item in Theme.objects.filter(translations__name__contains=term)] + #tags = [{'text':item.name, 'id':item.id, 'name':'tg'} for item in Tag.objects.filter(translations__name__contains=term)] + #objects = themes + tags + return HttpResponse(json.dumps(objects), content_type='application/json') + else: + return HttpResponse("Don't implemented yet") + + else: + raise Http404 + +def search_parent(request): + if request.GET: + name = request.GET['name'] + categorie = categories.get(name) + if not categorie: + raise Http404 + categorie_id = request.GET['id'] + model = categorie['model'] + obj = get_object_or_404(model, id=long(categorie_id)) + result = obj.get_parent() + return HttpResponse(json.dumps(result), content_type='application/json') + else: + raise Http404 + + + +def get_by_lang(item, field, lang='en'): + """ + + :param item: searchresult object + field: translated field + :return: + """ + return getattr(item, field+'_'+lang) + +def expo_autosearch(request): + if request.GET: + lang = translation.get_language() + term = request.GET['term'] + qs = SearchQuerySet().models(Exposition, Theme, Tag).autocomplete(content_auto=term).order_by('text') + result = [{'cat': get_by_lang(item, 'catalog_name', lang), 'text': get_by_lang(item, 'name', lang), 'url':item.url, + 'id':item.pk, 'name': item.form_name} for item in qs] + result = sorted(result, key=lambda x:x['cat'], reverse=True) + return HttpResponse(json.dumps(result), content_type='application/json') + else: + raise Http404 + +def place_autosearch(request): + if request.GET: + lang = translation.get_language() + qs = SearchQuerySet().models(PlaceExposition, PlaceConference).autocomplete(content_auto=request.GET['term']) + result = [{'cat': get_by_lang(item, 'catalog_name', lang), 'text': get_by_lang(item, 'name', lang), 'url':item.url, + 'id':item.pk, 'name': item.form_name} for item in qs].sort(key=lambda x:x['cat']) + result = sorted(result, key=lambda x:x['cat']) + return HttpResponse(json.dumps(result), content_type='application/json') + else: + raise Http404 + +def company_autosearch(request): + if request.GET: + qs = SearchQuerySet().models(Company, Theme, Tag).autocomplete(content_auto=request.GET['term']) + result = [{'cat': item.object.catalog_name, 'text': item.object.name, 'url':item.url, + 'id':item.object.id, 'name': item.object.search_name} for item in qs].sort(key=lambda x:x['cat']) + result = sorted(result, key=lambda x:x['cat']) + return HttpResponse(json.dumps(result), content_type='application/json') + else: + raise Http404 \ No newline at end of file diff --git a/static/custom_js/main.js b/static/custom_js/main.js index 256dade8..b9989d43 100644 --- a/static/custom_js/main.js +++ b/static/custom_js/main.js @@ -36,7 +36,7 @@ function deletePhoto(data){ } function postSuccess(data, textStatus, jqXHR){ - //console.log(data); + console.log(data); //$('#close').click(); if (data.indexOf("
{{ form.user_id }} + {{ form.errors }} {# email #}
- +
- {{ form.email }} Сбросить пароль + {{ object.email }} {{ form.email.errors }}
diff --git a/templates/admin/ajax_error_form.html b/templates/admin/ajax_error_form.html index 53653fa0..bdfc0172 100644 --- a/templates/admin/ajax_error_form.html +++ b/templates/admin/ajax_error_form.html @@ -1,9 +1,8 @@ -