From 5c3b40e7384f7df3617ee0deb94a746e0ea01bf7 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 16:20:54 +0200 Subject: [PATCH 1/8] admin: close db connection around fork When forking database connection status is incertain, so we explicitely close it. Django will reopen it. --- portail_citoyen_announces/admin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/portail_citoyen_announces/admin.py b/portail_citoyen_announces/admin.py index ba94b4f..2686cba 100644 --- a/portail_citoyen_announces/admin.py +++ b/portail_citoyen_announces/admin.py @@ -2,6 +2,7 @@ import os from django.contrib import admin from django.contrib.sites.models import get_current_site +from django.db import connection import models import transports @@ -23,7 +24,9 @@ class SendingAction(object): def __call__(self, modeladmin, request, queryset): pid = os.fork() if pid != 0: + connection.close() return + connection.close() transport = transports.get_transport(self.mode) for announce in queryset.select_related(): transport.send(announce) From 1c76a88d6b17aa8748b1c32416162c13740de008 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 16:22:22 +0200 Subject: [PATCH 2/8] transports: when transport is a tuple the second are keyword arguments for the constructor --- portail_citoyen_announces/transports.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/portail_citoyen_announces/transports.py b/portail_citoyen_announces/transports.py index d3830ee..bca6744 100644 --- a/portail_citoyen_announces/transports.py +++ b/portail_citoyen_announces/transports.py @@ -42,10 +42,14 @@ def get_transports(): if __TRANSPORTS is None: transports = [] for class_path in app_settings.transport_modes: + if not isinstance(class_path, basestring): + class_path, kwargs = class_path + else: + kwargs = {} module_path, class_name = class_path.rsplit('.', 1) try: module = import_module(module_path) - transports.append(getattr(module, class_name)()) + transports.append(getattr(module, class_name)(**kwargs)) except (ImportError, AttributeError), e: raise ImportError('Unable to load transport class %s' % class_path, e) __TRANSPORTS = transports From 4a312d1a28799d2d41eb6890407da879342b128b Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 16:23:04 +0200 Subject: [PATCH 3/8] transports: add the transport identifier to the template variable available for finding templates --- portail_citoyen_announces/transports.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/portail_citoyen_announces/transports.py b/portail_citoyen_announces/transports.py index bca6744..f160f1f 100644 --- a/portail_citoyen_announces/transports.py +++ b/portail_citoyen_announces/transports.py @@ -56,14 +56,14 @@ def get_transports(): return __TRANSPORTS -def get_template_list(template_list, category): +def get_template_list(template_list, **kwargs): '''Customize a template list given an announce category''' for template in template_list: - yield template.format(category=category.identifier) + yield template.format(**kwargs) -def get_template(template_list, category): - template_list = get_template_list(template_list, category) +def get_template(template_list, **kwargs): + template_list = get_template_list(template_list, **kwargs) return select_template(template_list) @@ -105,8 +105,10 @@ class EmailTransport(object): def send(self, announce): category = announce.category site = category.site - subject_template = get_template(self.subject_template_list, category) - body_template = get_template(self.body_template_list, category) + subject_template = get_template(self.subject_template_list, + category=category.identifier, identifier=self.identifier) + body_template = get_template(self.body_template_list, + category=category.identifier, identifier=self.identifier) ctx = Context({ 'announce': announce, 'site': site, 'category': category }) subject = subject_template.render(ctx).replace('\r', '').replace('\n', '') body = body_template.render(ctx) From 580af16e72902b4d3d89a00e39aa4b339e21c7f5 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 16:23:26 +0200 Subject: [PATCH 4/8] transports: add SMS transport through passerelle --- portail_citoyen_announces/transports.py | 71 +++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/portail_citoyen_announces/transports.py b/portail_citoyen_announces/transports.py index f160f1f..5d4a4e9 100644 --- a/portail_citoyen_announces/transports.py +++ b/portail_citoyen_announces/transports.py @@ -1,5 +1,11 @@ import logging import smtplib +try: + import simplejson as json +except: + import json + +import requests from django.utils.importlib import import_module @@ -74,6 +80,71 @@ class HomepageTransport(object): return (('homepage', _('homepage')),) +class SMSTransport(object): + body_template_list = [ + 'portail_citoyen_announces/{identifier}/body_{category}.txt', + 'portail_citoyen_announces/{identifier}/body.txt', + 'portail_citoyen_announces/body_{category}.txt', + 'portail_citoyen_announces/body.txt', + ] + + def __init__(self, url, from_mobile, login=None, password=None, identifier='sms'): + self.url = url + self.from_mobile = from_mobile + self.login = login + self.password = password + self.identifier = identifier + + def get_choices(self): + return ((self.identifier, self.identifier),) + + def get_subscriptions(self, category): + return models.Subscription.objects.filter(category=category, + transport=self.identifier) + + def get_sms(self, category): + qs = self.get_subscriptions(category) + return qs.values_list('identifier', flat=True).distinct() + + def send(self, announce): + category = announce.category + site = category.site + body_template = get_template(self.body_template_list, + category=category.identifier, identifier=self.identifier) + ctx = Context({ 'announce': announce, 'site': site, 'category': category }) + body = body_template.render(ctx) + sms = self.get_sms(category) + logger.info(u'sending announce %(announce)s through %(mode)s to %(count)s emails', + dict(announce=announce, mode=self.identifier, count=len(sms))) + try: + payload = { + 'message': body, + 'from': self.from_mobile, + 'to': list(sms), + } + response = requests.post(self.url, data=json.dumps(payload)) + json_response = response.json() + if json_response['err'] != 0: + msg = u'unable to send announce "%s" on site "%s": %s' % (announce, + site, json_response) + logger.error(msg) + else: + logger.info('announce %(announce)s sent succesfully', + dict(announce=announce)) + msg = u'ok' + except smtplib.SMTPException, e: + msg = u'unable to send announce "%s" on site "%s": %s' % (announce, + site, e) + logger.error(msg) + except Exception, e: + msg = u'unable to send announce "%s" on site "%s": %s' % (announce, + site, e) + logger.exception(msg) + models.Sent.objects.create( + announce=announce, + transport=self.identifier, + result=msg) + class EmailTransport(object): identifier = 'email' From 2d5db8a2707366a632e985229de737393c6bcc3b Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 18:06:09 +0200 Subject: [PATCH 5/8] forms: copy mobile number from user model of portail-citoyen --- portail_citoyen_announces/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/portail_citoyen_announces/forms.py b/portail_citoyen_announces/forms.py index 187574d..934d723 100644 --- a/portail_citoyen_announces/forms.py +++ b/portail_citoyen_announces/forms.py @@ -45,5 +45,6 @@ class SubscriptionForm(forms.Form): for category, transport in wanted_subscriptions: models.Subscription.objects.get_or_create( user=self.user, + identifier=self.user.mobile, category=category, transport=transport) From a1e218dd3b07ea77b962aaead12bd80f2c159d08 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 18:06:36 +0200 Subject: [PATCH 6/8] transports: check mobile numbers use the french format --- portail_citoyen_announces/transports.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/portail_citoyen_announces/transports.py b/portail_citoyen_announces/transports.py index 5d4a4e9..c7cba7e 100644 --- a/portail_citoyen_announces/transports.py +++ b/portail_citoyen_announces/transports.py @@ -87,6 +87,7 @@ class SMSTransport(object): 'portail_citoyen_announces/body_{category}.txt', 'portail_citoyen_announces/body.txt', ] + mobile_re = re.compile('^0[67][0-9]{8}$') def __init__(self, url, from_mobile, login=None, password=None, identifier='sms'): self.url = url @@ -104,7 +105,14 @@ class SMSTransport(object): def get_sms(self, category): qs = self.get_subscriptions(category) - return qs.values_list('identifier', flat=True).distinct() + for subscription in qs: + sms = '' + if subscription.identifier: + sms = subscription.identifier + elif subscription.user: + sms = subscription.user.mobile + if self.mobile_re.match(sms): + yield sms def send(self, announce): category = announce.category @@ -113,7 +121,7 @@ class SMSTransport(object): category=category.identifier, identifier=self.identifier) ctx = Context({ 'announce': announce, 'site': site, 'category': category }) body = body_template.render(ctx) - sms = self.get_sms(category) + sms = list(self.get_sms(category)) logger.info(u'sending announce %(announce)s through %(mode)s to %(count)s emails', dict(announce=announce, mode=self.identifier, count=len(sms))) try: From a3e8270bf7f262fbf39b94c84edb3a771db4e155 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 18:06:54 +0200 Subject: [PATCH 7/8] templates: show subscriptions list only if there is one --- .../templates/portail_citoyen_announces/announce_list.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/portail_citoyen_announces/templates/portail_citoyen_announces/announce_list.html b/portail_citoyen_announces/templates/portail_citoyen_announces/announce_list.html index 0db6ec7..2541913 100644 --- a/portail_citoyen_announces/templates/portail_citoyen_announces/announce_list.html +++ b/portail_citoyen_announces/templates/portail_citoyen_announces/announce_list.html @@ -9,4 +9,6 @@ {% endif %} {% endfor %} +{% if subscriptions %}

{% trans "Subscriptions" %}: {{ subscriptions|join:", " }}

+{% endif %} From 203e7fcf0cfd8c269ec621bdf998147d3b6d9fa1 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 18 Jun 2013 18:07:24 +0200 Subject: [PATCH 8/8] transports: add missing import re --- portail_citoyen_announces/transports.py | 1 + 1 file changed, 1 insertion(+) diff --git a/portail_citoyen_announces/transports.py b/portail_citoyen_announces/transports.py index c7cba7e..7fb18c5 100644 --- a/portail_citoyen_announces/transports.py +++ b/portail_citoyen_announces/transports.py @@ -1,5 +1,6 @@ import logging import smtplib +import re try: import simplejson as json except: