# -*- coding: utf-8 -*- """Forms for emencia.django.newsletter""" from datetime import date, datetime, timedelta from itertools import groupby import xlrd from django import forms from django.core.exceptions import ValidationError from django.db.models import Sum, Q from django.http import Http404 from django.utils import translation from django.utils.translation import ugettext_lazy as _ from django.utils.translation import get_language from django.utils.encoding import smart_text, force_text from django.conf import settings from conference.models import Conference from exposition.models import Exposition from functions.forms import ML_ModelMultipleChoiceField from haystack.query import SearchQuerySet from emencia.django.newsletter.models import ( Contact, ContactMailingStatus, ContactSettings, MailingList, Newsletter, PopupCount ) from city.models import City from country.models import Area, Country from theme.models import Theme, Tag from functions.form_check import translit_with_separator as tr from functions.search_forms import get_by_lang from functions.model_utils import EnumChoices class AutomaticEmailTestForm(forms.Form): date = forms.DateField( label=_(u'Дата'), input_formats=['%Y-%m-%d', '%d.%m.%Y'], widget=forms.DateInput( attrs={'class': 'date', 'id': 'dateFrom', 'placeholder': _(u'дд.мм.гггг')})) email = forms.EmailField(label=_(u'Email')) def clean_email(self): try: Contact.objects.get(email=self.cleaned_data['email']) except (Contact.DoesNotExist, ) as e: raise ValidationError(_(u'Контакта с таким емейлом не существует')) return self.cleaned_data['email'] class MailingListSubscriptionForm(forms.ModelForm): """Form for subscribing to a mailing list""" # Notes : This form will not check the uniquess of # the 'email' field, by defining it explictly and setting6 # it the Meta.exclude list, for allowing registration # to a mailing list even if the contact already exists. # Then the contact is always added to the subscribers field # of the mailing list because it will be cleaned with no # double. email = forms.EmailField(label=_('Email'), max_length=75) def save(self, mailing_list): data = self.cleaned_data contact, created = Contact.objects.get_or_create( email=data['email'], defaults={'first_name': data['first_name'], 'last_name': data['last_name']}) mailing_list.subscribers.add(contact) mailing_list.unsubscribers.remove(contact) class Meta: model = Contact fields = ('first_name', 'last_name') exclude = ('email',) class MailingSettingsForm(forms.ModelForm): email = forms.EmailField( error_messages={ 'required': _(u'Поле e-mail обязательно для заполнения') }, widget=forms.TextInput(attrs={'placeholder': _(u'Ваш e-mail')}) ) r_cities = ML_ModelMultipleChoiceField( label=_(u'Города России'), required=False, queryset=City.objects.all()) co = ML_ModelMultipleChoiceField( label=_(u'Зарубежные страны'), required=False, queryset=Country.objects.all()) tg = ML_ModelMultipleChoiceField( label=_(u'Теги'), required=False, queryset=Tag.objects.all()) th = ML_ModelMultipleChoiceField( label=_(u'Тематики'), required=False, queryset=Theme.objects.all()) area = forms.ModelMultipleChoiceField( label=_(u'Регион'), required=False, queryset=Area.objects.all()) class Meta: model = Contact fields = [ 'email', 'first_name', 'moscow', 'russia', 'r_cities', 'foreign', 'content_news', 'content_overview', 'content_articles', ] widgets = { 'first_name': forms.TextInput(attrs={'placeholder': _(u'Ваше имя')}), 'moscow': forms.CheckboxInput(), 'foreign': forms.CheckboxInput(), 'content_news': forms.CheckboxInput(), 'content_overview': forms.CheckboxInput(), 'content_articles': forms.CheckboxInput(), } def __init__(self, *args, **kwargs): super(MailingSettingsForm, self).__init__(*args, **kwargs) for field in ['co', 'r_cities', 'tg', 'th', 'area']: self.fields[field].widget.attrs.update({'style': 'display: none;'}) if self.instance and self.instance.pk: # если пользовать авторизован, у нас есть инстанс # и там не нужно выводить для редактирования его личные данные del self.fields['email'] del self.fields['first_name'] # area self.initial['area'] = set(self.instance.area.values_list('pk', flat=True)) area_q = Area.objects.language().all().order_by('name') where = " `{db_table}_translation`.`language_code` = '{lang}' "\ .format( db_table=Area._meta.db_table, lang=get_language()) area_q = area_q.extra(where=[where]) self.fields['area'].queryset = area_q # foreign countries self.initial['co'] = set(self.instance.f_countries.values_list('pk', flat=True)) co_query = Country.objects.language().all().order_by('name') where = " `{db_table}_translation`.`language_code` = '{lang}' "\ .format( db_table=Country._meta.db_table, lang=get_language()) co_query = co_query.extra(where=[where]) self.fields['co'].c_queryset = co_query self.fields['co'].widget.choices = self.fields['co'].choices # russia cities self.fields['r_cities'].c_queryset = self.instance.r_cities.distinct() self.fields['r_cities'].widget.choices = self.fields['r_cities'].choices # tag self.initial['tg'] = set(self.instance.tags.values_list('pk', flat=True)) self.fields['tg'].c_queryset = self.instance.tags.distinct() self.fields['tg'].widget.choices = self.fields['tg'].choices # theme self.initial['th'] = set(self.instance.themes.values_list('pk', flat=True)) self.fields['th'].c_queryset = self.instance.themes.distinct() self.fields['th'].widget.choices = self.fields['th'].choices def save(self): obj = super(MailingSettingsForm, self).save(commit=False) if not obj.pk: obj.save() f_countries_cleaned = list(self.cleaned_data.get('co')\ .order_by('area').values_list('pk', 'area_id')) areas = set(self.cleaned_data.get('area')\ .order_by('pk').values_list('pk', flat=True)) for area, group in groupby(f_countries_cleaned, lambda x: x[1]): if area not in areas: areas.add(area) obj.f_countries = self.cleaned_data.get('co') obj.area = areas obj.r_cities = self.cleaned_data.get('r_cities') or [] r_cities = obj.r_cities.values_list('pk', flat=True) if obj.moscow and not settings.MOSCOW_PK in r_cities: obj.r_cities.add(settings.MOSCOW_PK) if not obj.moscow and settings.MOSCOW_PK in r_cities: obj.moscow = True obj.tags = self.cleaned_data.get('tg') or [] obj.themes = self.cleaned_data.get('th') or [] obj.from_users = False obj.subscriber = True obj.save() return obj def clean(self): cleaned_data = super(MailingSettingsForm, self).clean() if not cleaned_data.get('tg') and not cleaned_data.get('th'): raise forms.ValidationError(_(u'Для успешной подписки необходимо ' u'выбрать тематики событий, которые ' u'вам интересны. На основе этих ' u'настроек мы включим в ваше письмо ' u'релевантные события!')) if not cleaned_data.get('moscow') and not cleaned_data.get('russia') and not cleaned_data.get('foreign') and not cleaned_data.get('area') and not cleaned_data.get('r_cities') and not cleaned_data.get('co'): raise forms.ValidationError(_(u'Необходимо выбрать минимум ' u'1 вариант в гео-фильтрах')) return cleaned_data class AllMailingListSubscriptionForm(MailingListSubscriptionForm): """Form for subscribing to all mailing list""" mailing_lists = forms.ModelMultipleChoiceField( queryset=MailingList.objects.all(), initial=[obj.id for obj in MailingList.objects.all()], label=_('Mailing lists'), widget=forms.CheckboxSelectMultiple()) def save(self, mailing_list): data = self.cleaned_data contact, created = Contact.objects.get_or_create( email=data['email'], defaults={'first_name': data['first_name'], 'last_name': data['last_name']}) for mailing_list in data['mailing_lists']: mailing_list.subscribers.add(contact) mailing_list.unsubscribers.remove(contact) class ContactForm(forms.ModelForm): email = forms.CharField(required=True, widget=forms.TextInput(attrs={'placeholder': _(u'Ваш e-mail')})) first_name = forms.CharField(required=True, widget=forms.TextInput(attrs={'placeholder': _(u'Ваше имя')})) class Meta: model = Contact fields = ('email', 'first_name', ) def clean_first_name(self): name = self.cleaned_data['first_name'] return name.capitalize() def clean_email(self): email = self.cleaned_data['email'] try: Contact.objects.get(email__iexact=email) raise forms.ValidationError(_(u'Указанный e-mail адрес уже ' u'подписан на рассылку')) except Contact.DoesNotExist: pass return email types_choice = EnumChoices( ALL=(1, _(u'Все')), USERS=(2, _(u'Пользователи')), SUBSCRIBERS=(3, _(u'Подписчики')), ) class ContactFilterForm(forms.Form): email = forms.EmailField( label="Email", max_length=255, required=False, widget=forms.TextInput(attrs={'class':'input-xlarge search-query','placeholder': 'Email'}) ) theme = forms.MultipleChoiceField( label="Тематика", choices=[(t.id, t.name) for t in Theme.objects.language()], required=False ) country = forms.MultipleChoiceField( label="Страна", choices=[(c.id, c.name) for c in list(set(Country.objects.language('ru').all()))], required=False ) city = forms.MultipleChoiceField( label="Город", choices=[(c.id, c.name) for c in list(set(City.objects.language('ru').filter(contactsettings__isnull=False)))], required=False, widget= forms.SelectMultiple(attrs={'id':'cities'}) ) area = forms.MultipleChoiceField( label="Area", choices=[(c.id, c.name) for c in list(set(Area.objects.language()))], required=False ) exposition = ML_ModelMultipleChoiceField(label=_(u'Новости по выставкам'), queryset=Exposition.objects.all(), required=False) conference = ML_ModelMultipleChoiceField(label=_(u'Новости по конференциям'), queryset=Conference.objects.all(), required=False) event_news = forms.BooleanField(label=_(u'Подписка на новости хотя бы по 1 событию'), required=False) mailinglist = forms.ModelChoiceField( queryset=MailingList.objects.all(), label="Список рассылки", required=False ) created_from = forms.CharField(max_length=255, label=_(u"Создан с"), required=False) created_to = forms.CharField(max_length=255, label=_(u"Создан по"), required=False) not_active = forms.BooleanField(label=_(u"Не подтверждена подписка"), required=False) not_valid = forms.BooleanField(label=_(u"Неалидный Email"), required=False) not_subscriber = forms.BooleanField(label=_(u"Отписался"), required=False) types_choice = forms.TypedChoiceField(label=_(u'Тип записи'), required=False, choices=types_choice, coerce=int) def filter(self): title = u'contact list ' qs = Contact.objects.all().select_related('contactsettings') if self.cleaned_data.get('mailinglist'): qs = qs.filter(mailinglist_subscriber__id=self.cleaned_data['mailinglist'].pk) title += u" mailinglist: %s" % self.cleaned_data['mailinglist'].name if self.cleaned_data.get('country'): qs = qs.filter(contactsettings__country__id__in=self.cleaned_data['country']) title += u" countries: %s" % ','.join([obj.url for obj in Country.objects.language().filter(id__in=self.cleaned_data['country'])]) if self.cleaned_data.get('email'): qs = qs.filter(email__icontains=self.cleaned_data['email']) if self.cleaned_data.get('theme'): qs = qs.filter(contactsettings__theme__id__in=self.cleaned_data['theme']) title += u" themes: %s" % ','.join([obj.url for obj in Theme.objects.language().filter(id__in=self.cleaned_data['theme'])]) if self.cleaned_data.get('city'): qs = qs.filter(contactsettings__city__id__in=self.cleaned_data['city']) title += u" cities: %s" % ','.join([obj.url for obj in Country.objects.language().filter(id__in=self.cleaned_data['country'])]) if self.cleaned_data.get('area'): qs = qs.filter(contactsettings__area__id__in=self.cleaned_data['area']) title += u" geo area: %s" % ','.join([tr(obj.name) for obj in Area.objects.language('en').filter(id__in=self.cleaned_data['area'])]) if self.cleaned_data.get('not_active'): title = u' not active ' + title qs = qs.filter(activated=False) else: qs = qs.filter(activated=True) if self.cleaned_data.get('not_valid'): title = u'not valid e-mail ' + title qs = qs.filter(valid=False) else: qs = qs.filter(valid=True) if self.cleaned_data.get("created_from"): qs = qs.filter(creation_date__gte=datetime.strptime(self.cleaned_data['created_from'], "%d.%m.%Y")) if self.cleaned_data.get("created_to"): qs = qs.filter(creation_date__lt=datetime.strptime(self.cleaned_data['created_to'], "%d.%m.%Y")) if self.cleaned_data.get('not_subscriber'): qs = qs.filter(subscriber=False) else: qs = qs.filter(subscriber=True) if self.cleaned_data.get('event_news'): qs = qs.filter(Q(conferences__isnull=False) | Q(expositions__isnull=False)) else: q = Q() if self.cleaned_data.get('conference'): q = Q(conferences__in=self.cleaned_data.get('conference')) if self.cleaned_data.get('expositions'): q |= Q(conferences__in=self.cleaned_data.get('expositions')) qs = qs.filter(q) types = self.cleaned_data.get('types_choice') if types == types_choice.USERS: qs = qs.filter(from_users=True) elif types == types_choice.SUBSCRIBERS: qs = qs.filter(from_users=False) qs = qs.distinct() return qs, title.replace('"', '\"').replace('\n', '') COUNTRY_CHOICES = [(c.id, c.name) for c in list(set(Country.objects.language('ru').all()))] COUNTRY_CHOICES.insert(0, ('', _(u'Страна'))) class ContactImportForm(forms.Form): excel_file = forms.FileField(label=_(u'Выберите файл')) activated = forms.BooleanField(label=_(u"Активные"), required=False) is_tester = forms.BooleanField(label=_(u"Тестовые"), required=False) country = forms.ChoiceField(label=_(u"Страна"), choices=COUNTRY_CHOICES, required=False) def save(self): data = self.cleaned_data country_id = self.cleaned_data.get('country') country = None if country_id: country = Country.objects.get(id=country_id) activated = data['activated'] is_tester = data['is_tester'] f = data['excel_file'] 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(1, sheet.nrows)] for row in row_list: c = Contact(email = row[0], first_name=row[1], last_name="", tester=is_tester, activated=activated, valid=True, subscriber=True) try: c.save() except: continue cs = ContactSettings() cs.contact = c cs.save() if country: cs.country.add(country) cs.save() class AbstractSubscribeForm(forms.ModelForm): email = forms.EmailField(widget=forms.TextInput(attrs={'placeholder': 'Email'})) class Meta: model = ContactSettings fields = ('periodic', 'exponent_practicum', 'organiser_practicum', 'theme', 'country', 'city') def clean_email(self): email = self.cleaned_data['email'] try: Contact.objects.get(email=email) except Contact.DoesNotExist: return email raise ValidationError(_(u'Такой email уже есть в базе даных')) def clean_city(self): cities = self.cleaned_data.get('city') if cities: res = [] for id in cities.split(','): try: res.append(int(id)) except ValueError: continue return City.objects.filter(id__in=res) else: return City.objects.none() def clean_country(self): countries = self.cleaned_data['country'] return Country.objects.filter(id__in=countries) def clean_theme(self): themes = self.cleaned_data['theme'] return Theme.objects.filter(id__in=themes) class SubscribeSettingsForm(AbstractSubscribeForm): NA_ID = 12 ASIA_ID = 9 EUROPE_ID = 4 city = forms.CharField( widget=forms.HiddenInput(attrs={'id': 'id_sub_set_city', 'placeholder': _(u'Город')}), required=False) get_announce = forms.BooleanField(required=False, label=_(u'Получать анонсы')) na_expo = forms.BooleanField(required=False, label=_(u'Выставки Северной Америки')) asia_expo = forms.BooleanField(required=False, label=_(u'Выставки Азии')) europe_expo = forms.BooleanField(required=False, label=_(u'Выставки Европы')) def __init__(self, *args, **kwargs): self.contact = kwargs.pop('contact', None) self.activation_send = False lang = translation.get_language() self.cities_choices = dict(City.objects.language(lang).all().values_list('pk', 'name')) super(SubscribeSettingsForm, self).__init__(*args, **kwargs) self.fields['theme'] = forms.MultipleChoiceField( choices=[(item.pk, get_by_lang(item, 'name', lang)) for item in SearchQuerySet().models(Theme).all()], required=False, widget=forms.SelectMultiple(attrs={'placeholder': _(u'Тематики'), 'id': 'id_sub_set_theme'})) self.fields['country'] = forms.MultipleChoiceField( choices=[(item.pk, get_by_lang(item, 'name', lang)) for item in SearchQuerySet().models(Country).all()], required=False, widget=forms.SelectMultiple(attrs={'placeholder': _(u'Страны'), 'id': 'id_sub_set_country'})) if self.instance.pk: self.initial['get_announce'] = self.instance.exponent_practicum or self.instance.organiser_practicum or self.instance.theme.exists() self.initial['city'] = ','.join(['%s:%s'%(pk, self.cities_choices.get(pk)) for pk in set(self.instance.city.all().values_list('pk', flat=True))]) def make_pretty_city_val(self): self.data = self.data.copy() self.data['city'] = ','.join(['%s:%s'%(pk, self.cities_choices.get(pk)) for pk in set(self.instance.city.all().values_list('pk', flat=True))]) def save_additional_fields(self, settings): get_announce = self.cleaned_data.get('get_announce') na_expo = self.cleaned_data.get('na_expo') asia_expo = self.cleaned_data.get('asia_expo') europe_expo = self.cleaned_data.get('europe_expo') if not get_announce: settings.organiser_practicum = False settings.exponent_practicum = False settings.save() settings.theme.clear() if na_expo: settings.area.add(Area.objects.get(id=self.NA_ID)) else: settings.area.remove(Area.objects.get(id=self.NA_ID)) if asia_expo: settings.area.add(Area.objects.get(id=self.ASIA_ID)) else: settings.area.remove(Area.objects.get(id=self.ASIA_ID)) if europe_expo: settings.area.add(Area.objects.get(id=self.EUROPE_ID)) else: settings.area.remove(Area.objects.get(id=self.EUROPE_ID)) return settings def save(self, commit=True): contactsettings = super(SubscribeSettingsForm, self).save(commit=False) # Prepare a 'save_m2m' method for the form, old_save_m2m = self.save_m2m def save_m2m(): old_save_m2m() contactsettings.theme.clear() for theme in self.cleaned_data['theme']: contactsettings.theme.add(theme) contactsettings.country.clear() for country in self.cleaned_data['country']: contactsettings.country.add(country) contactsettings.city.clear() for city in self.cleaned_data['city']: contactsettings.city.add(city) self.save_m2m = save_m2m if commit: contactsettings.save() self.save_m2m() return contactsettings def clean_email(self): if not self.contact: return super(SubscribeSettingsForm, self).clean_email() email = self.cleaned_data['email'] if self.contact.email != email: self.contact.activated = False self.contact.send_activation() self.save() self.activation_send = True return self.cleaned_data['email'] class PopupCountFilter(forms.Form): fr = forms.DateField(required=False) to = forms.DateField(required=False) def filter(self): fr = self.cleaned_data.get('fr') to = self.cleaned_data.get('to') if not fr and not to: fr = date.today() qs = PopupCount.objects.filter(date__gte=fr) contacts = Contact.objects.filter(creation_date__gte=fr) if to: contacts = contacts.filter(creation_date__lte=to+timedelta(days=1)) qs = qs.filter(date__lte=to) subscribed = contacts.count() activated = contacts.filter(activated=True).count() popups = qs.aggregate(count=Sum('cnt'))['count'] return {'subscribed': subscribed, 'activated': activated, 'popups': popups} class MailingStatusFilter(forms.Form): status = forms.ChoiceField(choices=[('', _(u'Не выбрано'))] + [(item[0], item[1]) for item in ContactMailingStatus.STATUS_CHOICES], required=False) email = forms.CharField(required=False, widget=forms.TextInput(attrs={'placeholder': 'Email'})) ab = forms.TypedChoiceField(required=False, choices=((0, _(u'Любое')), ) + Newsletter.AB_FINAL_CHOICE, coerce=int, empty_value=0) def filter(self, newsletter): status = self.cleaned_data.get('status') email = self.cleaned_data.get('email') ab = self.cleaned_data.get('ab') qs = ContactMailingStatus.objects.select_related().filter(newsletter=newsletter) if status: qs = qs.filter(status=status) if email: qs = qs.filter(contact__email=email) if ab: qs = qs.filter(ab=ab) return qs class SendtoFriendForm(forms.Form): email = forms.EmailField(label=_(u"E-mail Вашего друга"), required=False) first_name = forms.CharField(label=_(u"Имя Вашего друга"), required=False) def clean_first_name(self): if self.cleaned_data.get('email') and not self.cleaned_data.get('first_name'): raise forms.ValidationError(_(u'Введите имя вашего друга')) return self.cleaned_data.get('first_name')