You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

581 lines
24 KiB

# -*- 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
from django.forms.models import ModelChoiceIterator
from django.http import Http404
from django.utils import translation
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_text, force_text
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'))
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 CustomModelChoiceIterator(ModelChoiceIterator):
def __init__(self, field):
self.field = field
self.queryset = getattr(field, 'c_queryset', field.queryset.none())
class ML_ModelMultipleChoiceField(forms.ModelMultipleChoiceField):
''' use it with field.queryset = Model.objects.none()
'''
def _get_choices(self):
# If self._choices is set, then somebody must have manually set
# the property self.choices. In this case, just return self._choices.
if hasattr(self, '_choices'):
return self._choices
# Otherwise, execute the QuerySet in self.queryset to determine the
# choices dynamically. Return a fresh ModelChoiceIterator that has not been
# consumed. Note that we're instantiating a new ModelChoiceIterator *each*
# time _get_choices() is called (and, thus, each time self.choices is
# accessed) so that we can ensure the QuerySet has not been consumed. This
# construct might look complicated but it allows for lazy evaluation of
# the queryset.
return CustomModelChoiceIterator(self)
choices = property(_get_choices, forms.ChoiceField._set_choices)
# def clean(self, value):
# if self.required and not value:
# raise ValidationError(self.error_messages['required'])
# elif not self.required and not value:
# return self.queryset.none()
# if not isinstance(value, (list, tuple)):
# raise ValidationError(self.error_messages['list'])
# key = self.to_field_name or 'pk'
# qs = self.queryset.filter(**{'%s__in' % key: value})
# pks = set([force_text(getattr(o, key)) for o in qs])
# for val in value:
# if force_text(val) not in pks:
# raise ValidationError(self.error_messages['invalid_choice'] % val)
# # Since this overrides the inherited ModelChoiceField.clean
# # we run custom validators here
# self.run_validators(value)
# return qs
class MailingSettingsForm(forms.ModelForm):
r_cities = ML_ModelMultipleChoiceField(
label=_(u'Города России'), required=False,
queryset=City.objects.all())
f_countries = 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 = ML_ModelMultipleChoiceField(
label=_(u'Регион'), required=False,
queryset=Area.objects.all())
class Meta:
model = Contact
fields = [
'moscow', 'russia', 'r_cities', 'foreign', 'f_countries',
# 'tags', 'themes',
'periodic', 'periodic_day', 'content_news', 'content_overview',
'content_articles',
]
widgets = {
'moscow': forms.CheckboxInput(),
'foreign': forms.CheckboxInput(),
'periodic': forms.RadioSelect(),
'periodic_day': forms.RadioSelect(),
'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 ['f_countries', 'r_cities', 'tg', 'th']:
self.fields[field].widget.attrs.update({'style': 'display: none;'})
if self.instance and self.instance.pk:
if self.instance.f_countries:
self.fields['f_countries'].c_queryset = self.instance.f_countries.distinct()
self.fields['f_countries'].widget.choices = self.fields['f_countries'].choices
if self.instance.r_cities:
self.fields['r_cities'].c_queryset = self.instance.r_cities.distinct()
self.fields['r_cities'].widget.choices = self.fields['r_cities'].choices
if self.instance.tags:
self.fields['tg'].c_queryset = self.instance.tags.distinct()
self.fields['tg'].widget.choices = self.fields['tg'].choices
if self.instance.themes:
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()
# В форме можеть быть выбрана:
# 1. Area и внутри для уточнения не выбрана ни одна страна
# 2. Area и внутри выбрано одна или несколько стран
# Мы проверяем первый случай и добавляем все страны из данной зоны.
countries_add = set()
areas_cleaned = self.cleaned_data.get('area')
f_countries_cleaned = set(self.cleaned_data.get('f_countries')\
.order_by('pk')\
.values_list('pk', flat=True))
if areas_cleaned.exists():
areas = areas_cleaned\
.prefetch_related('country')\
.order_by('pk')\
.values_list('pk', 'country__pk')
for area, group in groupby(areas, lambda x: x[0]):
c = set(map(lambda x: x[1], group))
if not c.intersection(f_countries_cleaned):
countries_add.update(c)
if countries_add:
obj.f_countries.add(*countries_add)
#
obj.tags = self.cleaned_data.get('tg') or []
obj.themes = self.cleaned_data.get('th') or []
obj.save()
return obj
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()
class ContactSettingsForm(forms.ModelForm):
theme = forms.MultipleChoiceField(choices=[(str(item.id), item.name) for item in list(Theme.objects.language().all())],
widget=forms.CheckboxSelectMultiple(attrs={'class': 'pr-checkbox'}), required=False)
class Meta:
model = ContactSettings
fields = ('exponent_practicum', 'organiser_practicum', 'theme')
def clean_theme(self):
theme = self.cleaned_data.get('theme')
if theme:
return Theme.objects.filter(id__in=theme)
else:
return Theme.objects.none()
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
)
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)
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 SubscribeAssideForm(AbstractSubscribeForm):
city = forms.CharField(
widget=forms.HiddenInput(attrs={'id': 'id_subscription_city', 'placeholder': _(u'Город')}),
required=False)
periodic = forms.ChoiceField(
choices=ContactSettings.PERIODIC_CHOICES, required=False,
widget=forms.Select(attrs={'placeholder': _(u'Периодичность'), 'id': 'id_subscription_periodic'}))
def __init__(self, *args, **kwargs):
super(SubscribeAssideForm, self).__init__(*args, **kwargs)
lang = translation.get_language()
self.fields['theme'] = forms.MultipleChoiceField(
choices=[(item.pk, get_by_lang(item, 'name', lang)) for item in SearchQuerySet().models(Theme).all()],
widget=forms.SelectMultiple(attrs={'placeholder': _(u'Тематики'), 'id': 'id_subscription_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_subscription_country'}))
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