Merge branch 'newsletter' of git.general-servers.com:expomap/expomap into newsletter

remotes/origin/1203
Nazar Kotjuk 10 years ago
commit b6de05dc0e
  1. 4
      core/utils.py
  2. 21
      core/views.py
  3. 10
      emencia/django/newsletter/admin_urls.py
  4. 10
      emencia/django/newsletter/models.py
  5. 49
      emencia/django/newsletter/views/admin_views.py
  6. 17
      functions/forms.py
  7. BIN
      static/img/announce-letter.png
  8. 58
      templates/admin/newsletters/newsletter_history.html
  9. 4
      templates/admin/newsletters/newsletter_list.html
  10. 62
      templates/admin/newsletters/newsletter_stat.html
  11. 22
      templates/client/accounts/calendar.html
  12. 17
      templates/client/includes/announces.html
  13. BIN
      templates/client/static_client/img/announce-letter.png

@ -83,13 +83,13 @@ def queryset_to_workbook(queryset, columns, report_date = None):
'pattern: pattern solid, fore_color gray_ega;', 'pattern: pattern solid, fore_color gray_ega;',
) )
odd_style = xlwt.Style.easyxf( odd_style = xlwt.Style.easyxf(
'font: name Calibri, height 300, bold False;' 'font: name Calibri, height 240, bold False;'
'borders: left thin, right thin, top thin, bottom thin;' 'borders: left thin, right thin, top thin, bottom thin;'
'alignment: horizontal center, vertical center, wrap True;' 'alignment: horizontal center, vertical center, wrap True;'
'pattern: pattern solid, fore_color white;', 'pattern: pattern solid, fore_color white;',
) )
even_style = xlwt.Style.easyxf( even_style = xlwt.Style.easyxf(
'font: name Calibri, height 300, bold False;' 'font: name Calibri, height 240, bold False;'
'borders: left thin, right thin, top thin, bottom thin;' 'borders: left thin, right thin, top thin, bottom thin;'
'alignment: horizontal center, vertical center, wrap True;' 'alignment: horizontal center, vertical center, wrap True;'
'pattern: pattern solid, fore_color silver_ega;', 'pattern: pattern solid, fore_color silver_ega;',

@ -266,13 +266,18 @@ def download_workbook(request):
lang = get_language() lang = get_language()
data = request.GET data = request.GET
if data: if data:
qs = [] if data.get('filter') == u'future':
for i,obj in enumerate(data): qs = request.user.calendar.get_events()
if data.get('data[%i][name]' % i) == 'expo': qs = [event for event in qs if event.data_begin > datetime.date.today()]
qs.append(Exposition.objects.language(lang).get(id=data['data[%i][value]'%i])) else:
elif data.get('data[%i][name]' % i) == 'conf': qs = []
qs.append(Conference.objects.language(lang).get(id=data['data[%i][value]'%i])) for i,obj in enumerate(data):
if data.get('data[%i][name]' % i) == 'expo':
qs.append(Exposition.objects.language(lang).get(id=data['data[%i][value]'%i]))
elif data.get('data[%i][name]' % i) == 'conf':
qs.append(Conference.objects.language(lang).get(id=data['data[%i][value]'%i]))
if not qs:
return HttpResponseRedirect("/profile/calendar/?message=empty")
earliest_event = qs[0].data_begin earliest_event = qs[0].data_begin
for i, obj in enumerate(qs, start=1): for i, obj in enumerate(qs, start=1):
if obj.data_begin < earliest_event: if obj.data_begin < earliest_event:
@ -305,4 +310,4 @@ def download_workbook(request):
workbook.save(response) workbook.save(response)
return response return response
else: else:
return HttpResponseRedirect(request.META.get('HTTP_REFERER'), "/profile/calendar/") return HttpResponseRedirect("/profile/calendar/")

@ -2,16 +2,18 @@ from django.conf.urls import url
from django.conf.urls import patterns from django.conf.urls import patterns
from emencia.django.newsletter.views.admin_views import ContactList, UpdateContact, MailingListView, UpdateMailingList,\ from emencia.django.newsletter.views.admin_views import ContactList, UpdateContact, MailingListView, UpdateMailingList,\
CreateMailingList, NewsletterListView, NewsletterCreate, ExportContacts, DeleteContact, NewsletterUpdate, \ CreateMailingList, NewsletterListView, NewsletterCreate, ExportContacts, DeleteContact, NewsletterUpdate, \
DeleteMailingList, ImportContacts DeleteMailingList, ImportContacts, NewsletterHistory, NewsletterStatistics
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^newsletters/all/$', NewsletterListView.as_view(), name='newsletters_newsletters_list'), url(r'^newsletters/all/$', NewsletterListView.as_view(), name='newsletters_newsletters_list'),
url(r'^newsletters/(?P<pk>\d+)/edit/', NewsletterUpdate.as_view(), name='newsletters_newsletters_update'), url(r'^newsletters/(?P<pk>\d+)/edit/$', NewsletterUpdate.as_view(), name='newsletters_newsletters_update'),
url(r'^newsletters/(?P<pk>\d+)/test/', url(r'^newsletters/(?P<pk>\d+)/history/', NewsletterHistory.as_view(), name='newsletters_newsletters_history'),
url(r'^newsletters/(?P<pk>\d+)/statistics/', NewsletterStatistics.as_view(), name='newsletters_newsletters_stat'),
url(r'^newsletters/(?P<pk>\d+)/test/$',
'emencia.django.newsletter.views.admin_views.send_test_newsletter', 'emencia.django.newsletter.views.admin_views.send_test_newsletter',
name='newsletters_newsletters_send_test'), name='newsletters_newsletters_send_test'),
url(r'^newsletters/', NewsletterCreate.as_view(), name='newsletters_newsletters_create'), url(r'^newsletters/$', NewsletterCreate.as_view(), name='newsletters_newsletters_create'),
url(r'^mailinglist/all/$', MailingListView.as_view(), name='newsletters_mailinglist'), url(r'^mailinglist/all/$', MailingListView.as_view(), name='newsletters_mailinglist'),

@ -471,6 +471,16 @@ class ContactMailingStatus(models.Model):
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True) creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
def get_verbose_status(self):
verbose = ''
for s in self.STATUS_CHOICES:
if s[0] == self.status:
verbose = unicode(s[1])
break
if self.status == ContactMailingStatus.LINK_OPENED:
verbose +=": <a href='%s'>%s</a>"% (self.link.url, self.link.url)
return verbose
def __unicode__(self): def __unicode__(self):
return '%s : %s : %s' % (self.newsletter.__unicode__(), return '%s : %s : %s' % (self.newsletter.__unicode__(),
self.contact.__unicode__(), self.contact.__unicode__(),

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.views.generic import CreateView, ListView, UpdateView, DeleteView, FormView from django.views.generic import CreateView, ListView, UpdateView, DeleteView, FormView, DetailView
from django.conf import settings from django.conf import settings
from django.utils import translation from django.utils import translation
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -8,7 +8,7 @@ from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from HTMLParser import HTMLParseError from HTMLParser import HTMLParseError
from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter, Attachment from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter, Attachment, ContactMailingStatus
from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm, AttachmentForm from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm, AttachmentForm
from emencia.django.newsletter.mailer import Mailer from emencia.django.newsletter.mailer import Mailer
from ..forms import ContactFilterForm, ContactImportForm from ..forms import ContactFilterForm, ContactImportForm
@ -222,6 +222,51 @@ def send_test_newsletter(request, pk):
return HttpResponseRedirect(redirect) return HttpResponseRedirect(redirect)
class NewsletterHistory(ListView):
model = ContactMailingStatus
template_name = 'admin/newsletters/newsletter_history.html'
paginate_by = 50
def get_queryset(self):
self.newsletter = get_object_or_404(Newsletter, pk=self.kwargs['pk'])
qs = self.newsletter.contactmailingstatus_set.select_related('contact', 'link').all()
if self.request.GET.get('filter'):
qs = qs.filter(status=self.request.GET['filter'])
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
return context
class NewsletterStatistics(DetailView):
model = Newsletter
template_name = 'admin/newsletters/newsletter_stat.html'
def get_context_data(self, **kwargs):
context = super(NewsletterStatistics, self).get_context_data(**kwargs)
sent = self.object.contactmailingstatus_set.filter(status=ContactMailingStatus.SENT).count()
errors = self.object.contactmailingstatus_set.filter(status=ContactMailingStatus.ERROR).count()
opened = self.object.contactmailingstatus_set.filter(status=ContactMailingStatus.OPENED).count()
unsubscribed = self.object.contactmailingstatus_set.filter(status=ContactMailingStatus.UNSUBSCRIPTION).count()
no_data = self.object.contactmailingstatus_set.filter(status=ContactMailingStatus.ANNOUNCE_NO_DATA).count()
links = self.object.contactmailingstatus_set.filter(status=ContactMailingStatus.LINK_OPENED).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
stat = {
'sent':{'data':sent, 'filter':""},
'errors':{ 'data':errors, 'filter':CMS.ERROR},
'opened':{'data': opened, '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}}
context.update({'stat': stat})
return context
class ExportContacts(FormView): class ExportContacts(FormView):
form_class = ContactFilterForm form_class = ContactFilterForm

@ -97,10 +97,10 @@ class TranslatableModelForm(forms.ModelForm):
if field.name not in veto_fields} if field.name not in veto_fields}
for code, name in settings.LANGUAGES: for code, name in settings.LANGUAGES:
# todo: fix requirements - where field is required default language must be required to)
for tr_field in self.translatable_fields: for tr_field in self.translatable_fields:
field = fkwargs[tr_field].formfield() field = fkwargs[tr_field].formfield()
field.required = False if code != get_language():
field.required = False
self.fields[tr_field+"_"+code] = field self.fields[tr_field+"_"+code] = field
def clean(self): def clean(self):
@ -126,14 +126,13 @@ class TranslatableModelForm(forms.ModelForm):
translations = {obj.language_code: obj for obj in list(inst.translations.all())} translations = {obj.language_code: obj for obj in list(inst.translations.all())}
for lang in self.data_by_lang: for lang in self.data_by_lang:
if lang is not init_lang: if lang is not init_lang:
try: translation = translations.get(lang)
tr = translations[lang] # there are no available translation in db
except KeyError: if not translation:
# there are no available translation in db translation = inst.translate(lang)
tr = inst.translate(lang)
for key, value in self.data_by_lang[lang].iteritems(): for key, value in self.data_by_lang[lang].iteritems():
setattr(tr, key, value) setattr(translation, key, value)
tr.save() translation.save()
self.instance = inst.lazy_translation_getter(init_lang) self.instance = inst.lazy_translation_getter(init_lang)
return self.instance return self.instance

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

@ -0,0 +1,58 @@
{% extends 'admin/base.html' %}
{% load staticfiles %}
{% block body %}
<div class="box span10">
<div class="box-header well">
<h2><i class="icon-arrow-down"></i>История для {{ newsletter.title }}</h2>
</div>
<div class="box-content">
<form id="id_form" class="form-inline">
<div class="form-group">
<select class="form-control" name="filter" id="filter">
<option>Фильтровать</option>
<option value="">Все</option>
{% for id, name in choices%}
<option class="opt" value="{{ id }}">{{ name }}</option>
{% endfor %}
</select>
</div>
</form>
<table class="table table-hover table table-bordered table-striped">
<colgroup>
<col width="33%">
<col width="33%">
<col width="33%">
</colgroup>
<thead>
<tr>
<th>Дата</th>
<th>Контакт</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
{% for item in object_list %}
<tr>
<td>{{ item.creation_date|date:"Y-m-d H:i:s" }}</td>
<td>{{ item.contact.email }}</td>
<td>{{ item.get_verbose_status|safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{# pagination #}
{% include 'admin/includes/admin_pagination.html' with page_obj=object_list %}
</div>
<script>
$(document).ready(function(){
$("#filter").on('change', function(event){
console.log("click");
$("#id_form").submit();
})
})
</script>
{% endblock %}

@ -31,8 +31,8 @@
<td>{{ item.sending_date|date:"Y-m-d H:i" }}</td> <td>{{ item.sending_date|date:"Y-m-d H:i" }}</td>
<td><a href="{% url 'newsletters_newsletters_send_test' item.id %}">тест</a> </td> <td><a href="{% url 'newsletters_newsletters_send_test' item.id %}">тест</a> </td>
<td><a href="{% url 'newsletters_newsletters_update' item.id %}">Изменить</a> </td> <td><a href="{% url 'newsletters_newsletters_update' item.id %}">Изменить</a> </td>
<td><a href="#">История</a> </td> <td><a href="{% url 'newsletters_newsletters_history' item.id %}">История</a> </td>
<td>{% if item.status == item.SENT or item.status == item.CANCELED %}<a href="#">Статистика</a>{% endif %}</td> <td>{% if item.status == item.SENT or item.status == item.CANCELED %}<a href="{% url 'newsletters_newsletters_stat' item.id %}">Статистика</a>{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

@ -0,0 +1,62 @@
{% extends 'admin/base.html' %}
{% load staticfiles %}
{% block body %}
<div class="box span10">
<div class="box-header well">
<h2><i class="icon-arrow-down"></i>Статистика для {{ newsletter.title }}</h2>
</div>
<div class="box-content">
<table class="table table-hover table table-bordered table-striped">
<colgroup>
<col width="45%">
<col width="45%">
<col width="10%">
</colgroup>
<thead>
<tr>
<th>Название</th>
<th>Количество писем</th>
<th>&nbsp</th>
</tr>
</thead>
<tbody>
<tr>
<td>Отправлено</td>
<td>{{ stat.sent.data }}</td>
<td><a href="{% url 'newsletters_newsletters_history' object.id %}?filter={{ stat.sent.filter }}">Подробно</a></td>
</tr>
<tr>
<td>Ошибок</td>
<td>{{ stat.errors.data }}</td>
<td><a href="{% url 'newsletters_newsletters_history' object.id %}?filter={{ stat.errors.filter }}">Подробно</a></td>
</tr>
<tr>
<td>Открыто</td>
<td>{{ stat.opened.data }} {% if stat.opened.percent %} ({{ stat.opened.percent|floatformat:"0" }}%) {% endif %}</td>
<td><a href="{% url 'newsletters_newsletters_history' object.id %}?filter={{ stat.opened.filter }}">Подробно</a></td>
</tr>
<tr>
<td>Отписалось</td>
<td>{{ stat.unsub.data }} {% if stat.unsub.percent %} ({{ stat.unsub.percent|floatformat:"0" }}%) {% endif %}</td>
<td><a href="{% url 'newsletters_newsletters_history' object.id %}?filter={{ stat.unsub.filter }}">Подробно</a></td>
</tr>
<tr>
<td>Нет данных</td>
<td>{{ stat.no_data.data }}</td>
<td><a href="{% url 'newsletters_newsletters_history' object.id %}?filter={{ stat.no_data.filter }}">Подробно</a></td>
</tr>
<tr>
<td>Переходов по ссылкам</td>
<td>{{ stat.links.data }}</td>
<td><a href="{% url 'newsletters_newsletters_history' object.id %}?filter={{ stat.links.filter }}">Подробно</a></td>
</tr>
</tbody>
</table>
</div>
</div>
{% endblock %}

@ -20,14 +20,18 @@
{% block content_list %} {% block content_list %}
<div class="m-article p-calendar"> <div class="m-article p-calendar">
{% with days=days events=events current_day=current_day %} {% with days=days events=events current_day=current_day %}
{% include 'includes/accounts/calendar_table.html' %} {% include 'client/includes/accounts/calendar_table.html' %}
{% endwith %} {% endwith %}
</div> </div>
<div class="m-article cal-lists"> <div class="m-article cal-lists">
{% if events|length > 0 %} {% if events|length > 0 %}
<div class="cl-sect"> <div class="cl-sect">
<div class="cls-title">{{ days.15|date:"F"}}’{{ days.15|date:"y"}}</div> <div class="cls-title">{{ days.15|date:"F"}}’{{ days.15|date:"y"}}
<div style="float:right;margin-right: 5px;" class="check-wrap">
<label class="check"><input type="checkbox" id="selectall"/></label>
</div>
</div>
{% include 'client/includes/accounts/calendar_list.html' with events=events %} {% include 'client/includes/accounts/calendar_list.html' with events=events %}
</div> </div>
@ -35,8 +39,8 @@
<div class="cla-title">{% trans 'Все / выделенные:' %}</div> <div class="cla-title">{% trans 'Все / выделенные:' %}</div>
<div class="cla-btns"> <div class="cla-btns">
<a class="button" id="btn_delete" href="#">{% trans 'удалить из расписания' %}</a> <a class="button" id="btn_delete" href="#">{% trans 'удалить из расписания' %}</a>
<a id ='export' class="button icon-save" href="#">{% trans 'выбраные в xls' %}</a>
<a id ='export' class="button icon-save" href="#">{% trans 'сохранить в xls' %}</a> <a id ='future' class="button icon-save" href="#">{% trans 'все предстоящие в xls' %}</a>
<!--<a class="button icon-calendar-o" href="#">{% trans 'ЭКСПОРТИРОВАТЬ В' %} <span class="lc">iCal</span></a> <!--<a class="button icon-calendar-o" href="#">{% trans 'ЭКСПОРТИРОВАТЬ В' %} <span class="lc">iCal</span></a>
@ -54,6 +58,16 @@
</div> </div>
<script> <script>
$(function(){ $(function(){
if(window.location.search === "?message=empty"){
alert("{% trans 'У вас нету событий в будущем!' %}");
window.location.search = ""
}
$('#selectall').click(function(event) { //on click
$(".qwer").trigger("click");
});
$("#future").click(function(event) {
window.location.href = "/profile/calendar/export/?" + "filter=future";
});
$("#export").click(function(event){ $("#export").click(function(event){
event.preventDefault(); event.preventDefault();
var list = $('.qwer'); var list = $('.qwer');

@ -1,12 +1,13 @@
{% load i18n %} {% load i18n %}
{% load staticfiles %}
<div id="subscribe-sm" class="subscribe-sm"> <div id="subscribe-sm" class="subscribe-sm">
<header><a href="#" style="text-decoration: none">{% trans 'получать анонсЫ' %}</a></header> <header>
{% comment %} <a href="#" style="text-decoration: none"> {% trans 'получать анонсЫ' %}
<ul> <span>
<li><a class="icon-big-email" href="#">{% trans 'по e-mail' %}</a></li> <img style="margin-right: -2px; margin-bottom: 4px; width: 12%;" src="{% static 'img/announce-letter.png' %}">
</ul> </span>
{% endcomment %} </a>
</header>
</div> </div>
<hr> <hr/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Loading…
Cancel
Save