Archive

Archives pour la catégorie ‘Python’

Django : Envoyer des emails HTML avec images inline (intégrées)

17/02/2010 admin Comments off

Voyons comment envoyer des emails multiparties (texte et HTML) avec des images inline (intégrées dans le mail lui-même, et non en pièce jointe), et ceci en utilisant des templates afin de profiter (par exemple) de l’i18n avec gettext, des filtres et tags, de l’utilisation du contexte…

Ce code est un mélange de deux méthodes complémentaires, une sur les mails HTML par Ross Poulton, et l’autre venant d’un djangosnippet par sleytr.

Pour en faciliter l’utilisation et la maintenance, j’ai mis ce petit module django-nice-emails sur bitbucket.

Plutôt que de copier le code ici, je vais plutôt en décrire les grandes étapes:

  1. Utiliser le setting DEFAULT_FROM_EMAIL si l’expéditeur n’est pas fourni
  2. Créer un django.template.Context à partir du dictionnaire fourni (permet de remplacer les {{ var }} dans les templates)
  3. Utiliser le contexte créé pour initialiser le contenu texte, HTML ainsi que le sujet
  4. Transformer le destinataire fourni en liste (si ce n’est pas déjà une liste de destinataires)
  5. Créer un django.core.mail.EmailMultiAlternatives qui est la base de notre email (basé sur le contenu texte)
  6. Rajouter la partie HTML
  7. Rajouter les images en inline si nécéssaire
  8. Envoyer le mail

Rien de compliqué donc dans ce code qui fait moins de 20 lignes « utiles ».

Voyons maintenant un exemple d’utilisation avec de la traduction et de l’héritage de templates:

Les templates

On utilise ici la méthode de Ross Poulton qui consiste à ne fournir en paramètre template_name que la base du nom de fichier, sans l’extension. On fournit ensuite au django.template.loader ce template_name avec l’extension .txt et .html, ces templates doivent donc exister tous les deux.

templates/test_email.txt

{% load i18n %}
{% trans "Bonjour" %} {{ nom }},

{% blocktrans %}Ceci est un exemple de "nice-email" que je vous fait parvenir,
à titre d'exemple, et bien que vous vous en fichiez{% endblocktrans %}.

{% trans "Cordialement" %}

Mathieu Agopian

templates/test_email.html

{% extends "base_email.html" %}
{% load i18n %}
{% block email_content %}
<p>{% trans "Bonjour" %} {{ nom }},</p>

<p>
{% blocktrans %}Ceci est un exemple de "nice-email" que je vous fait parvenir,
à titre d'exemple, et bien que vous vous en fichiez{% endblocktrans %}.
</p>

<p>{% trans "Cordialement" %}</p>

<p><em>Mathieu Agopian</em></p>
<img src="cid:signature" />
{% endblock email_content %}

templates/base_email.html

<table width="600">
<tr><td><img src="cid:logo" /></td></tr>
<tr><td>
    {% block email_content %}{% endblock email_content %}
</td></tr>
</table>

Le contexte

Un simple dictionnaire python pour chaque tag utilisé dans les templates:

context = {'nom': 'Johnny Biboul'}

Les images

Elles doivent être passées en paramètres dans un tuple de tuples, sous la forme ((‘/chemin/vers/image.png’, ‘tagimage’), ‘/chemin/vers/image2.png’, ‘tagimage2′), …). Si les images sont dans le répertoire images du MEDIA_ROOT:

images = (
    (path.join(settings.MEDIA_ROOT, 'images', 'signature.png'), 'signature'),
    (path.join(settings.MEDIA_ROOT, 'image', 'logo.png'), 'logo'))

Dans les templates, on utilisera les images sous la forme <img src=’cid:tagimage’ />.

Le code

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from os import path
from django.conf import settings
from django.utils.translation import ugettext
from utils.nicemails import send_nice_email

context = {'nom': 'Johnny Biboul'}

images = (
    (path.join(settings.MEDIA_ROOT, 'images', 'signature.png'), 'signature'),
    (path.join(settings.MEDIA_ROOT, 'images', 'logo.png'), 'logo'))

subject = ugettext(u"Test de mail pour %(nom)s") % {'nom': '{{ nom }}'}

send_nice_email(
        template_name='test_email',
        email_context=context,
        subject=subject,
        recipients='johnny@biboul.com',
        sender='foo bar ',
        images=images)

Conclusion

Il vous suffit de mettre ce code dans une de vos vues pour pouvoir faire de jolis mails de confirmation d’inscription, des newsletters, ou voir même (bouh! c’est mal!) du mass-mailing. Veillez néanmoins à ne pas forcer la dose sur le html, ou les images inlines!

lancer gunicorn avec runit

10/02/2010 admin Comments off

ATTENTION votre serviteur a fait le test pour vous sur une ubuntu: après avoir installé runit et runit-run, le système ne démarre plus. Pour suivre les étapes de ce billet, il ne faut pas installer runit-run, qui ne doit être installé que lorsque l’on souhaite remplacer totalement le système d’initialisation (et cela demande plus de configuration qu’une simple installation du paquet).

Pour les malheureux qui ont fait les frais de la première version de ce billet (demandant d’installer runit-run), je ne peux que m’excuser platement, et vous fournir la méthode « au secours rescue moi! »: récuperer une installation avec un cd ubuntu. Une fois chrooté sur la partition root, il vous restera à désinstaller le paquet fautif et redémarrer:
        $ aptitude purge runit-run

Edité le 2010/02/10 à 20:58

Pour faire suite au précédent billet gunicorn: un server wsgi ultra simple à utiliser et configurer, voici une recette rapide pour lancer automatiquement (et monitorer) gunicorn avec runit.

Pourquoi runit et pas sysvinit, inittab, upstart, …

Je vous laisse consulter la page benefits sur le site officiel pour vous faire une idée. Pour les personnes ne parlant pas anglais, voici un bref résumé:

  1. Un répertoire par service, contenant un script run
  2. Un environnement d’exécution propre et prédictible pour chaque processus
  3. Service de logging (optionnel) qui sera lancé en même temps que le processus, et en couvrira toute la durée de vie (redémarrages compris!)
  4. Très peu encombrant, efficace… et peut complètement remplacer le système d’initialisation de votre linux

Utiliser runit avec le système d’initialisation actuel

Pour nous faciliter la vie, et ne pas avoir à modifier/importer de nombreux scripts de démarrage pour tous les démons et services déjà installés, nous allons utiliser runit « avec » le système d’initialisation actuel.

Installer runit

$ aptitude install runit

ATTENTION: Si vous installez aussi runit-run, il vous faut absolument configurer votre système (How to replace init), et ce, avant de rebooter (sinon votre système ne démarrera pas, et vous serez contraint à utiliser une méthode de récupération, comme celle présentée en tête de ce billet).

Créer un répertoire pour le service gunicorn et son script de lancement

$ mkdir /etc/sv/gu-monprojet
$ vi /etc/sv/gu-monprojet/run

Et voici le contenu du script run

#!/bin/bash
source /path/to/venv/bin/activate # activer le virtualenv
cd /path/to/django/project
exec gunicorn_django -b localhost:8080 --workers=3

Avec la version de gunicorn utilisée pour l’écriture de cet article, il est nécessaire d’être dans le répertoire du projet django (là ou se situe le fichier settings.py) pour lancer gunicorn_django.

Dans une future version (la modification est dans le trunk à l’heure de l’écriture) il suffira d’indiquer le chemin vers le fichier settings.py comme paramètre à la commande gunicorn_django.

Enfin, ne pas oublier de rajouter les droits d’exécution sur le script qu’on vient de créer:

$ chmod a+x /etc/sv/gu-monprojet

Indiquer à runit qu’il doit lancer le script

Pour celà, un simple lien symbolique, et dans les secondes qui suivent le script sera lancé:

$ ln -s /etc/sv/gu-monprojet /etc/service/

Et c’est tout!

Il suffit maintenant d’en profiter en allant sur http://localhost:8080, en configurant apache pour proxiser les requêtes directement dessus (cf le billet gunicorn: un server wsgi ultra simple à utiliser et configurer), ou encore en utilisant la commande sv pour gérer le service gunicorn:

$ sv status gu-monprojet
$ sv check gu-monprojet
$ sv up gu-monprojet
$ sv down gu-monprojet
$ sv restart gu-monprojet
$ sv hup gu-monprojet
...

gunicorn: un server wsgi ultra simple à utiliser et configurer

09/02/2010 admin Comments off

Deux billets le même jour, c’est fête!

Voici une recette simple pour installer, configurer et utiliser gunicorn avec apache et django.

Installer gunicorn

Pour installer gunicorn dans son environnement virtuel:

$ pip install -E /path/to/venv install gunicorn

Configurer Apache en proxy

Apache servira les fichiers statiques, et « proxisera » toutes les autres requêtes directement à gunicorn qui sera lancé en local sur le port 8080:

<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com

DocumentRoot /path/to/django/project

<Proxy *>
    Order deny,allow
    Allow from all
</Proxy>

# laisser apache servir les fichiers statiques
ProxyPass /robots.txt !
ProxyPass /favicon.ico !
ProxyPass /static/ !

# proxiser toutes les autres requêtes vers gunicorn
ProxyPass / http://localhost:8080/

# robots.txt et favicon.ico sont dans /path/to/django/project/static/
Alias /robots.txt /path/to/django/project/static/robots.txt
Alias /favicon.ico /path/to/django/project/static/favicon.ico

<Directory /path/to/django/project>
    Order deny,allow
    Allow from all
    Options -Indexes
</Directory>
</VirtualHost>

Pour que le tout fonctionne correctement, il faut activer les modules mod_proxy et mod_proxy_html (et en option mod_cache):

$ a2enmod proxy proxy_http cache

Puis de redémarrer le server Apache:

$ /etc/init.d/apache2 restart

Lancer gunicorn

Il suffit de se placer dans le répertoire du projet django (avec le virtualenv activé), puis de taper:

$ gunicorn_django -b localhost:8080 --workers=2

Un ordre d’idée pour le calcul des workers: un de plus que le nombre de CPUs de la machine.

Conclusion

On peut alors se créer un script (a placer dans /etc/init.d) et l’activer pour qu’il se lance automatiquement au démarrage avec la commande update-rc.d (sous Debian), ou utiliser runit (jamais testé, peut-être un futur billet?).

Encore mieux, remplacer Apache par Nginx! (jamais testé non plus, et sûrement un futur billet ;) ).

On peut difficilement faire plus simple!

Installer PIL (Python Imaging Library) facilement avec pip

09/02/2010 admin 3 commentaires

Le fabuleux utilitaire pip de Ian Bicking est un remplacement à easy_install qui fonctionne très bien avec virtualenv (pas étonnant, c’est du même auteur!).

Je laisse le soin au lecteur de consulter la documentation sur ces deux utilitaires très pratiques et indispensables à tout développeur python.

Voici la commande à utiliser pour installer PIL (Python Imaging Library) dans votre environnement virtuel:

$ pip -E /path/to/venv install http://effbot.org/downloads/Imaging-1.1.7.tar.gz

Vous pouvez consulter la liste des versions disponibles en vous rendant sur la page officielle de Python Imaging Library.

ATTENTION: Il faut avoir les sources de python installées et disponibles afin de pouvoir compiler le paquet. Sur debian, il vous suffit de taper

$ aptitude install python-dev

Si vous avez une erreur pip du genre

ImportError: No module named pkg_resources

il vous faut aussi installer python-pkg-resources:

$ aptitude install python-pkg-resources
Categories: Python Tags: , , , ,

PyCon.fr: venez m'y voir!

23/05/2009 admin Comments off

Toi, oui toi lecteur,

sache qu’il y a une conférence sur Python, entièrement gratuite, qui se tiendra le 30 et 31 mai (autant dire, le week-end prochain) sur Paris, à la Cité des Sciences et de l’Industrie de la Villette.

Tarek Ziadé, le président de l’association AFPY, donne un peu plus de précisions sur cet évènement.

Et enfin un lien vers l’évènement lui même, afin d’y trouver des informations sur le programme: PyCon.fr 2009

J’y donnerai une présentation de 20 minutes sur un billet que j’ai commencé sur ce blog: Les (D)VCS, pourquoi, comment? (le premier article: Le contrôle de version de sources, pourquoi?)

Categories: (D)VCS, Python, Uncategorized Tags: