# -*- coding: utf-8 -*- import datetime import csv from django.conf import settings from django.core.urlresolvers import reverse_lazy, reverse from django.contrib.sites.models import Site from django.forms.formsets import formset_factory from django.forms.models import modelformset_factory from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.core.files.storage import default_storage from django.utils import translation from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from django.views.generic import ( CreateView, DeleteView, DetailView, FormView, ListView, RedirectView, UpdateView ) from emencia.django.newsletter.admin_forms import ( AttachmentForm, ContactSettingsForm, MailingListForm, NewsletterForm, NewsletterDailyForm, NewsletterRecommendForm, NewsletterFilterForm ) from emencia.django.newsletter.forms import ( MailingStatusFilter, PopupCountFilter ) from emencia.django.newsletter.mailer import Mailer from emencia.django.newsletter.models import ( Attachment, Contact, ContactMailingStatus, ContactSettings, MailingList, Newsletter, NewsletterRecommend ) from functions.admin_views import paginate_results, AdminListView from functions.custom_views import ContextMixin, CreateUpdateView from functions.http import JsonResponse from HTMLParser import HTMLParseError from theme.models import Theme from ..forms import ( ContactFilterForm, ContactImportForm, AutomaticEmailTestForm ) from ..models import PopupCount from ..utils.excel import ExcelResponse from ..utils.tokens import tokenize class AutomaticEmailTest(ContextMixin, FormView): form_class = AutomaticEmailTestForm template_name = 'newsletter/AutomaticEmail_test.html' # success_url = reverse_lazy('automaticemailtest') def get_initial(self): initial = super(AutomaticEmailTest, self).get_initial() if self.request.GET.get('date'): initial['date'] = self.request.GET.get('date') return initial def form_valid(self, form): contact = Contact.objects.get(email=form.cleaned_data.get('email')) uidb36, token = tokenize(contact) try: self.extra_ctx.update({ 'newsletter': Newsletter.objects.get(sending_date=form.cleaned_data.get('date')) }) except (Newsletter.DoesNotExist, ) as e: pass self.extra_ctx.update({ 'contact': contact, 'domain': Site.objects.get_current().domain, # 'newsletter': self.newsletter, # 'tracking_image_format': TRACKING_IMAGE_FORMAT, 'uidb36': uidb36, 'token': token, 'name': contact.first_name or contact.last_name or _(u'Подписчик'), 'no_logo_image': default_storage.open('newsletter/images/no-logo.png'), }) self.extra_ctx.update(contact.get_announce_context_v2(date=form.cleaned_data.get('date'))) return self.form_invalid(form) class ContactList(FormView): paginate_by = settings.ADMIN_PAGINATION model = Contact template_name = 'c_admin/newsletters/contact_list.html' form_class = ContactFilterForm queryset = Contact.objects.all() def get_form(self, form_class): if self.request.GET: return form_class(self.request.GET) else: return form_class(**self.get_form_kwargs()) def get(self, request, *args, **kwargs): if request.GET: form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) else: return super(ContactList, self).get(request, *args, **kwargs) def form_valid(self, form): qs, _ = form.filter() result = paginate_results(qs, page=self.request.GET.get('page')) context = self.get_context_data(form=form) context.update({'object_list': result}) return self.render_to_response(context) def get_context_data(self, **kwargs): context = super(ContactList, 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 class ContactQueryDelete(RedirectView): url = reverse_lazy('newsletters_contact_list') filter_form = ContactFilterForm def get(self, request, *args, **kwargs): form = self.filter_form(request.GET) if form.is_valid(): qs,_ = form.filter() qs.delete() return HttpResponseRedirect(self.url) else: return HttpResponse('400') class DeleteContact(DeleteView): model = Contact success_url = reverse_lazy('newsletters_contact_list') template_name = 'c_admin/newsletters/confirm_delete.html' class UpdateContact(UpdateView): model = ContactSettings form_class = ContactSettingsForm template_name = 'c_admin/newsletters/contact.html' success_url = '/admin/newsletters/contact/all/' def form_valid(self, form): self.object = form.save() self.object.theme = form.cleaned_data['theme'] self.object.country = form.cleaned_data['country'] self.object.area = form.cleaned_data['area'] self.object.city = form.cleaned_data['city'] self.object.save() return HttpResponseRedirect(self.get_success_url()) def get_initial(self): lang = translation.get_language() obj = self.object.contact data = self.initial.copy() city = ','.join(['%s:%s'%(item.id, item.name) for item in self.object.city.filter(translations__language_code=lang)]) data.update({'first_name': obj.first_name, 'subscriber': obj.subscriber, 'valid': obj.valid, 'tester': obj.tester, 'city':city}) return data class ExportContacts(FormView): form_class = ContactFilterForm def get(self, request=None, *args, **kwargs): form = self.form_class(request.GET) if form.is_valid(): qs, title = form.filter() if qs.count(): columns = ('email', 'first_name') return ExcelResponse(request, qs, title, columns,'contacts') return HttpResponseRedirect(self.request.META['HTTP_REFERER']) class ImportContacts(FormView): form_class = ContactImportForm success_url = reverse_lazy("newsletters_contact_list") template_name = 'c_admin/import templates/import_contacts.html' def form_valid(self, form): form.save() return HttpResponseRedirect(self.get_success_url()) class MailingListView(ListView): paginate_by = settings.ADMIN_PAGINATION model = MailingList template_name = 'c_admin/newsletters/mailing_list.html' class DeleteMailingList(DeleteView): model = MailingList template_name = 'c_admin/newsletters/confirm_delete.html' success_url = reverse_lazy('newsletters_mailinglist') class MailingListMixin(object): model = MailingList form_class = MailingListForm template_name = 'c_admin/newsletters/mailing_list_object.html' success_url = '/admin/newsletters/mailinglist/all/' def dispatch(self, request, *args, **kwargs): self.filter = kwargs.get('filter', False) return super(MailingListMixin, self).dispatch(request, *args, **kwargs) def form_valid(self, form): filter_form = ContactFilterForm(self.request.GET) if not self.object: contacts = Contact.objects.none() if self.filter and filter_form.is_valid(): contacts, _ = filter_form.filter() else: contacts = self.object.subscribers.all() obj = form.save() obj.subscribers = form.get_contact_list(contacts) return HttpResponseRedirect(self.success_url) def get_context_data(self, **kwargs): context = super(MailingListMixin, self).get_context_data(**kwargs) form = ContactFilterForm(self.request.GET) context['filter_form'] = form if form.is_valid(): qs, _ = form.filter() context['contact_count'] = qs.count() context['filter_params'] = self.filter return context class CreateMailingList(MailingListMixin, CreateView): pass class UpdateMailingList(MailingListMixin, UpdateView): pass def export_contacts(request, pk): contacts = Contact.objects.filter( mailinglist_subscriber__pk=pk ).values( 'email', 'first_name', 'last_name' ) response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="contacts.csv"' writer = csv.writer(response) for contact in contacts: writer.writerow( [contact['email'], contact['first_name'].encode('utf-8'), contact['last_name'].encode('utf-8')] ) return response class DailyMailSuccesUrlMixin(object): def get_success_url(self): if self.object.dailymail == True: return reverse('newsletters_dailymail_list') return super(DailyMailSuccesUrlMixin, self).get_success_url() class NewsletterCreate(DailyMailSuccesUrlMixin, CreateView): model = Newsletter form_class = NewsletterForm template_name = 'c_admin/newsletters/newsletter_object.html' success_url = '/admin/newsletters/newsletters/all/' def get_formset(self): AttachmentFormSet = formset_factory(AttachmentForm) if self.request.POST: return AttachmentFormSet(self.request.POST, self.request.FILES) else: return AttachmentFormSet() def get_context_data(self, **kwargs): context = super(NewsletterCreate, self).get_context_data(**kwargs) context['attachment_formset'] = self.get_formset() return context def form_valid(self, form): self.object = form.save() self.object.test_contacts = form.cleaned_data['test_contacts'] formset = self.get_formset() if formset.is_valid(): for item in formset.forms: if item.is_valid() and item.has_changed(): instance = item.save(commit=False) instance.newsletter = self.object instance.save() return HttpResponseRedirect(self.success_url) class NewsletterUpdate(DailyMailSuccesUrlMixin, UpdateView): model = Newsletter form_class = NewsletterForm form_class_daily = NewsletterDailyForm template_name = 'c_admin/newsletters/newsletter_object.html' template_name_daily = 'c_admin/newsletters/newsletter_object_daily.html' success_url = '/admin/newsletters/newsletters/all/' def get_template_names(self): if self.kwargs.get('dailymail', False): return [self.template_name_daily] return super(NewsletterUpdate, self).get_template_names() def get_form_class(self): if self.kwargs.get('dailymail', False): return self.form_class_daily return super(NewsletterUpdate, self).get_form_class() def get_formset(self): if self.request.POST: AttachmentFormSet = modelformset_factory(Attachment, form=AttachmentForm, exclude=('newsletter',)) else: AttachmentFormSet = modelformset_factory(Attachment, form=AttachmentForm, exclude=('newsletter',)) if self.request.POST: return AttachmentFormSet(self.request.POST, self.request.FILES, queryset=self.object.attachment_set.all()) else: return AttachmentFormSet(queryset=self.object.attachment_set.all()) def get_context_data(self, **kwargs): context = super(NewsletterUpdate, self).get_context_data(**kwargs) context['attachment_formset'] = self.get_formset() return context def form_valid(self, form): self.object = form.save() self.object.test_contacts = form.cleaned_data['test_contacts'] if not self.kwargs.get('dailymail', False): formset = self.get_formset() if formset.is_valid(): for item in formset.forms: if item.is_valid() and item.has_changed(): instance = item.save(commit=False) instance.newsletter = self.object instance.save() return HttpResponseRedirect(self.get_success_url()) class NewsletterListView(AdminListView): paginate_by = settings.ADMIN_PAGINATION model = Newsletter template_name = 'c_admin/newsletters/newsletter_list.html' template_name_daily = 'c_admin/newsletters/newsletter_list_daily.html' form_class = NewsletterFilterForm def get_template_names(self): if self.kwargs.get('dailymail', False): return [self.template_name_daily] return super(NewsletterListView, self).get_template_names() def get_queryset(self): return self.model.objects.filter( dailymail=self.kwargs.get('dailymail', False) ) def send_test_newsletter(request, pk): newsletter = get_object_or_404(Newsletter, pk=pk) if newsletter.test_contacts.count(): mailer = Mailer(newsletter, test=True) try: mailer.run() except HTMLParseError: return HttpResponse(_('Unable send newsletter, due to errors within HTML.')) else: return HttpResponse(_(u'Нет тестовых контактов')) if newsletter.dailymail == False: redirect = request.META.get('HTTP_REFERER', '/admin/newsletter/newsletter/all/') else: redirect = request.META.get('HTTP_REFERER', reverse('newsletters_dailymail_list')) return HttpResponseRedirect(redirect) class NewsletterStatistics(DetailView): model = Newsletter template_name = 'c_admin/newsletters/newsletter_stat.html' def get_context_data(self, **kwargs): context = super(NewsletterStatistics, self).get_context_data(**kwargs) if self.object.ab_testing: context.update({'stat': self.get_stat_data(params={'ab': Newsletter.A})}) context.update({'statB': self.get_stat_data(params={'ab': Newsletter.B})}) else: context.update({'stat': self.get_stat_data()}) return context def get_stat_data(self, params={}): qs = self.object.contactmailingstatus_set sent = qs.filter(status=ContactMailingStatus.SENT, **params).count() errors = qs.filter(status=ContactMailingStatus.ERROR, **params).count() _opened_qs = qs.filter(status=ContactMailingStatus.OPENED, **params).\ values_list('contact', flat=True) opened = _opened_qs.distinct().count() opened_all = _opened_qs.count() unsubscribed = qs.filter(status=ContactMailingStatus.UNSUBSCRIPTION, **params).\ values_list('contact', flat=True).distinct().count() no_data = qs.filter(status=ContactMailingStatus.ANNOUNCE_NO_DATA, **params).count() links = qs.filter(status=ContactMailingStatus.LINK_OPENED, **params).count() links_unique = qs.filter(status=ContactMailingStatus.LINK_OPENED, **params).\ values_list('contact', flat=True).distinct().count() CMS = ContactMailingStatus opened_percent = 0 if opened or sent == 0 else (float(opened)/sent)*100 unsub_percent = 0 if unsubscribed or sent == 0 else (float(unsubscribed)/sent)*100 ab = params.get('ab', '') stat = { 'sent': {'data': sent, 'filter': ""}, 'errors': {'data': errors, 'filter': CMS.ERROR}, 'opened': {'data': opened, 'data_all': opened_all, 'filter': CMS.OPENED, 'percent': opened_percent}, 'unsub': {'data': unsubscribed, 'filter': CMS.UNSUBSCRIPTION, 'percent': unsub_percent}, 'no_data': {'data': no_data, 'filter': CMS.ANNOUNCE_NO_DATA}, 'links': {'data': links, 'filter': CMS.LINK_OPENED}, 'links_unique': {'data': links_unique, 'filter': CMS.LINK_OPENED}, 'ab': ab, } return stat class DailyMailSuccesUrlMixinUrl(object): def get_url(self, newsletter): if newsletter.dailymail == True: return reverse('newsletters_dailymail_list') return self.url class NewsletterCloneView(RedirectView, DailyMailSuccesUrlMixinUrl): url = reverse_lazy("newsletters_newsletters_list") def get(self, request, *args, **kwargs): newsletter = get_object_or_404(Newsletter, id=kwargs['pk']) newsletter.clone() return HttpResponseRedirect(self.get_url(newsletter)) def post(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) class NewsletterCancelView(NewsletterCloneView, DailyMailSuccesUrlMixinUrl): url = reverse_lazy("newsletters_newsletters_list") def get(self, request, *args, **kwargs): newsletter = get_object_or_404(Newsletter, id=kwargs['pk']) if newsletter.can_cancel: newsletter.status = Newsletter.CANCELED newsletter.save() return HttpResponseRedirect(self.get_url(newsletter)) class NewsletterHistory(ListView): model = ContactMailingStatus template_name = 'c_admin/newsletters/newsletter_history.html' paginate_by = 50 def get_queryset(self): self.newsletter = get_object_or_404(Newsletter, pk=self.kwargs['pk']) if self.request.GET: form = MailingStatusFilter(self.request.GET) if form.is_valid(): qs = form.filter(self.newsletter) else: qs = ContactMailingStatus.objects.select_related().filter(newsletter=self.newsletter) else: qs = ContactMailingStatus.objects.select_related().filter(newsletter=self.newsletter) return qs def get_context_data(self, **kwargs): context = super(NewsletterHistory, self).get_context_data(**kwargs) context['newsletter'] = self.newsletter context['choices'] = ContactMailingStatus.STATUS_CHOICES form = MailingStatusFilter(self.request.GET) context['filter_form'] = form return context class NewsletterDelete(DailyMailSuccesUrlMixin, DeleteView): model = Newsletter template_name = 'c_admin/newsletters/confirm_delete.html' success_url = reverse_lazy('newsletters_newsletters_list') def count_popups(request): if not request.is_ajax(): return HttpResponse("request is not ajax") else: themes = request.GET.getlist('theme') if themes: for theme_id in themes: obj,_ = PopupCount.objects.get_or_create(theme=Theme.objects.get(id=theme_id), date=datetime.date.today()) obj.cnt += 1 obj.save() else: obj,_ = PopupCount.objects.get_or_create(theme=None, date=datetime.date.today()) obj.cnt+=1 obj.save() return HttpResponse(obj.cnt, content_type='application/json') class PopupStatisticsView(FormView): form_class = PopupCountFilter template_name = 'c_admin/newsletters/popup_count.html' def get(self, request, *args, **kwargs): form_class = self.get_form_class() form = form_class(request.GET) if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) def form_valid(self, form): data = form.filter() data['form'] = form return self.render_to_response(data) def theme_for_filter_ajax(request): if request.is_ajax(): empty = [{'pk': '', 'name': '-----'}] try: ml = MailingList.objects.get(pk=request.GET.get('id')) themes = Theme.objects.language().filter(pk__in=[x.pk for x in ml.theme_for_filter.all()]).values('pk', 'name') return JsonResponse({'data': empty + [{'pk': x['pk'], 'name': force_text(x['name'])} for x in themes]}) except (MailingList.DoesNotExist, ValueError, ): return JsonResponse({'data': empty}) return Http404() class RecommendsList(ListView): model = NewsletterRecommend template_name = 'newsletter/recommend/list.html' class RecommendsCreateorEdit(CreateUpdateView): model = NewsletterRecommend form_class = NewsletterRecommendForm template_name = 'newsletter/recommend/create.html' success_url = reverse_lazy('newsletters_recommends_list') class RecommendsDelete(DeleteView): model = NewsletterRecommend template_name = 'newsletters/confirm_delete.html' success_url = reverse_lazy('newsletters_recommends_list')