# -*- coding: utf-8 -*- import os import datetime 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, TemplateView, UpdateView ) from emencia.django.newsletter.admin_forms import ( AttachmentForm, ContactSettingsForm, MailingListForm, NewsletterForm, NewsletterDailyForm, NewsletterRecommendForm, ) 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 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 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(ListView): 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' success_url = '/admin/newsletters/newsletters/all/' 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): qs = super(NewsletterListView, self).get_queryset() return qs.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')