Cyberdelia

Mon nom est Timothée Peignier. Je travaille avec un tas de gens biens avec lesquels je fais des choses comme Croisé dans le métro et plein d’autres encore

Je suis un ingénieur un peu fou et aussi un peu entrepreneur.

Je fais aussi dans la manufacture d'oeuvres électriques de luxe avec deux autres orfèvres pour La Panthère.

Django and Heroku, the fast track

Want to start using Heroku with Django ? This is the fastest way I’ve found as soon as django 1.4 is out the door.

First, install foreman and heroku gems

$ gem install foreman heroku

Create your Django project

$ django-admin.py startproject --template https://github.com/cyberdelia/django-heroku-template/zipball/master --extension py,md heroku-app

Make it a git repository

$ cd heroku-app
$ git init

Create the app at heroku

$ heroku apps:create -s cedar heroku-app

Add redis addons to your heroku app

$ heroku addons:add redistogo:nano   

Because settings use Redis as a default cache backend.

Create and activate you virtualenv

$ mkvirtualenv heroku-app
$ workon heroku-app 

Install dependencies

$ pip install -r requirements.txt

Edit configuration

Settings are read from the .env file by foreman. settings.py loads them from os.environ and uses default heroku environment variables to setup databases and cache.

Start your application

$ foreman start 

or

$ foreman run heroku-app/manage.py runserver

Run a command

$ foreman run heroku-app/manage.py shell

Deploy it to heroku

$ git push heroku master

Have fun! And if you want even more from your heroku app, you can use your own python buildpack.

+
Man is the best computer we can put aboard a spacecraft, and the only one that can be mass produced with unskilled labor.
Werner von Braun
+

Moteur d’inférence

Très souvent si vous devez mettre au point un système de tarification, il y a des règles spécifiques au domaine de votre application et un tas de données à lier pour obtenir votre prix final.

Vous vous retrouvez alors devant un problème, comment calculer un prix à partir de ces règles et de ces données ? Et surtout comment avoir du code maintenable, sûr (parce que c’est une partie importante de votre application, celle qui paye votre salaire) et surtout évolutif (parce que vous et moi savons que c’est le genre de chose qui n’est jamais gravé dans le marbre).

Une bonne solution semble être d’utiliser un moteur d’inférence, plutôt qu’un tas de spaghetti code :

Un moteur d’inférence est un système expert qui permet de conduire des raisonnements logiques et de dériver des conclusions à partir d’une base de faits et d’une base de règles.

Si on en revient à notre problème de tarification, nous avons :

  • une base de règles : nos règles de tarification.
  • une base de faits : nos données.
  • des conclusions : notre prix.

Il existe quelques moteurs d’inférence, mais on va choisir d’utiliser Pyke, parce que c’est en Python et que ça tombe plutôt bien.

Pour illustrer l’utilisation de Pyke, nous allons utiliser un cas très simple qui s’inspire de son utilisation chez e-loue.

Des locataires louent des objets un certain temps à un prix fixé par chaque propriétaire. Chaque propriétaire peut définir un prix par jour et/ou un prix par semaine, selon la durée de la location et les prix existants vous devez trouver quel prix s’applique.

Nous allons commencer par installer Pyke :

$ pip install pyke

La première étape, ainsi que le plus difficile consiste à créer notre base de fait. Pyke s’attend à trouver vos règles dans un fichier dont l’extension est .krb. Nous allons appeler notre base “pricing.krb”.

Le prix par semaine s’applique quand la durée dépasse une semaine et qu’un prix hebdomadaire existe” s’exprime ainsi :

week_package
    use packages(weekly, $delta)
    when
        packages.package('weekly')
        check $delta.days >= 7

J’utilise ici le chaînage arrière de Pyke pour exprimer cette règle.

On doit d’abord déclarer le nom de la règle, ici : week_package.
Le reste peut-être lu de cette façon : “Quand un prix hebdomadaire existe et que la durée dépasse 7 jours” correspond a la partie “when”.
Quand ces faits sont vrais alors on utilise le forfait hebdomadaire, ce qui correspond à la partie “use”.

Pour ceux qui veulent comprendre plus en profondeur ou plus rapidement la syntaxe des règles de Pyke, dirigez-vous vers la documentation adéquate, n’oubliez pas de faire un détour vers la partie Pattern Matching qui sera essentielle à votre compréhension.

Vous devriez pouvoir facilement construire vous même la deuxième règle : “Le prix par jour s’applique quand la durée dépasse un jour et est inférieure à une semaine s’il existe un prix journalier” :

day_package
    use packages(daily, $delta)
    when
        packages.package('daily')
        check $delta.days >= 1
        check $delta.days < 7

On souhaite aussi pouvoir appliquer un prix journalier même si la durée dépasse une semaine si et seulement s’il n’existe pas de prix hebdomadaire, pour ça on va utiliser la directive notany :

day_package_for_week
    use packages(daily, $delta)
    when
        packages.package('daily')
        check $delta.days >= 7
        notany
            packages.package('weekly')

Nous avons créé notre base de règles, faisons appel maintenant à Pyke pour charger celle-ci :

from pyke import knowledge_engine

def which_package(packages, duration):
    engine = knowledge_engine.engine((__file__, '.rules'))
    engine.activate('pricing')

Nous devons maintenant charger notre base de faits, c’est à dire les forfaits qui s’appliquent pour l’objet :

packages = ['daily', 'weekly']
for package in packages:
    engine.assert_('packages', 'package', (package,))

Vous retrouvez, ici, la syntaxe que nous avons utiliser dans nos règles “packages.package” pour faire référence à l’existence d’un forfait.

Il est maintenant possible de prouver si un forfait s’applique à l’objet par rapport à notre durée de location :

try:
    vals, plans = engine.prove_1_goal('pricing.packages($type, $delta)', delta=duration)
    print vals['type']
except knowledge_engine.CanNotProve:
    print "No package applies"

Pour cela, on cherche a prouver un seul “goal”, vous retrouvez la similitude de nommage du nom du goal avec notre base de règles, sauf qu’ici on fourni delta et on cherche le type de forfait.

Au final, on peut vérifier que notre moteur d’inférence fait bien son travail :

>>> which_package(['daily', 'weekly'], datetime.timedelta(days=4))
'daily'
>>> which_package(['daily', 'weekly'], datetime.timedelta(days=8))
'weekly'
>>> which_package(['daily'], datetime.timedelta(days=9))
'daily'
>>> which_package(['weekly'], datetime.timedelta(days=1))
'No package applies'

Vous voila initié à Pyke, vous pouvez essayer d’autres durées ou ajouter de nouveaux forfaits plus complexes. N’oubliez pas que vous pouvez importer des modules python dans vos règles, comme suit :

bc_extras
    import calendar

Vous pouvez aussi retrouver le code de cet exemple sur github ainsi qu’une version commentée.

+

Evil par Tom Scott

daeschner:

Tom Scott expose les numéros de téléphone publics d’utilisateurs de Facebook.

L’expérience a pour vocation de mettre en garde ceux qui créent des groupes pour récupérer les numéros de leurs contacts après avoir perdu un téléphone sans considérer que le groupe qu’ils créent est public et accessible au monde entier.

Une recherche pour téléphone perdu sur Google montre que la pratique existe aussi en France.

+

Dieppe, sur le port

+

Cuisine & dépendances

Imaginons brièvement que vous ayez un superbe projet écrit avec Django tel que, au hasard, Croisé dans le Métro. Il se trouve que vous avez plusieurs problèmes :

  • Vous aimeriez déployer votre projet sur votre super-ordinateur sans difficultés.
  • Vous aimeriez éviter le casse-tête des nombreuses et éclectiques dépendances de votre projet et qui ont la sale habitude de changer régulièrement.
  • Vous aimeriez aussi éviter de devoir expliquer à Alfred comment installer ces mêmes dépendances sur sa machine dès que vous modifiez quelque chose.

Il s’avère que la solution est presque évidente, la voici étape par étape.

Pip

Pip est un easy_install sur-vitaminé, il va nous permettre de décrire les dépendances de notre projet et se chargera ensuite de les installer pour nous de la façon la plus agréable possible. Mais d’abord commençons par l’installer :

$ sudo easy_install pip

Ensuite, direction la racine de notre projet, nous allons supposer qu’il contient déjà un dossier qui contient l’ensemble de votre projet. Nous allons donc créer un dossier deploy qui va contenir nos différents fichiers décrivant nos dépendances pour chaque déploiement.

$ mkdir deploy
$ touch production.txt development.txt

L’idée est que chaque fichier décrit chaque dépendance nécessaire à votre projet pour tourner dans l’environnement correspondant :

$ nano production.txt
ipython==0.9.1
python-memcached>=1.44
perlinpinpin>=0.1
-e git://github.com/brosner/django-announcements.git@master#egg=announcements
-e svn+http://code.djangoproject.com/svn/django/branches/releases/1.0.X#egg=django


$ nano development.txt
ipython==0.9.1
perlinpinpin>=0.1
fabric==0.1.1
-e git://github.com/brosner/django-announcements.git@master#egg=announcements
-e svn+http://code.djangoproject.com/svn/django/branches/releases/1.0.X#egg=django

Nous venons de décrire nos dépendances basiques, soit en précisant le nom du paquet et une exigence sur sa version. Nous pouvons aussi préciser des dépôts subversion, git, mercurial et bazaar, en précisant l’URL de celui-ci et facultativement un indicateur (numéro de révision, commit ou branche). Vous en apprendrez bien plus dans la documentation de pip.

Virtualenv

Maintenant que nous pouvons décrire les dépendances de notre projet, nous allons créer un environnement virtuel (du moins pour tout ce qui est relatif à python) afin nous isoler et de nous libérer du carcan de l’environnement python de notre OS.

Mais d’abord, l’installation :

$ sudo pip install virtualenv
Downloading/unpacking virtualenv
  Downloading virtualenv-1.3.3.tar.gz (1.0Mb): 1.0Mb downloaded
  Running setup.py egg_info for package virtualenv
Installing collected packages: virtualenv
  Running setup.py install for virtualenv
    Installing virtualenv script to /Users/tim/Projects/app/env/bin
Successfully installed virtualenv

Nous allons initialiser notre environnement virtuel à la racine de notre projet dans un dossier nommé env:

$ virtualenv --clear --no-site-packages env
Not deleting env/bin
New python executable in env/bin/python
Installing setuptools............done.

Pour ceux qui préféreraient préciser un python en particulier :

$ virtualenv --clear --no-site-packages env -p /usr/local/bin/python2.6
Running virtualenv with interpreter /usr/local/bin/python2.6
Not deleting env/bin
New python executable in env/bin/python
Installing setuptools...........done.

Nous allons maintenant installer les dépendances que nous avons décrites plus haut :

$ pip install -E env -r deploy/development.txt
[..]
Successfully installed

Si votre environnement virtualenv n’existe pas, pip se fera un plaisir de le créer pour vous (tant que virtualenv est installé).

Nos dépendances sont bien installées, il nous reste à activer notre environnement virtuel, pour pouvoir en profiter :

$ source env/bin/activate
(env) $ python
Python 2.5.1 (r251:54863, Feb  6 2009, 19:02:12)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import django
>>> django.file’/Users/tim/Projects/app/env/src/django/django/init.pyc’
>>>


$ source env/bin/activate
(env) $ python app/manage.py runserver

Vous pouvez ajouter à volonté progressivement d’autres dépendances :

$ pip install -E env “markdown>=2.0.1”
Downloading/unpacking markdown>=2.0.1
  Downloading Markdown-2.0.1.tar.gz (71Kb): 71Kb downloaded
  Running setup.py egg_info for package markdown
Installing collected packages: markdown
  Running setup.py install for markdown
    changing mode of build/scripts-2.5/markdown from 664 to 775
    changing mode of /Users/tim/Projects/app/env/bin/markdown to 775
Successfully installed markdown>=2.0.1

Il vous suffira ensuite de freeze votre environnement grâce à pip pour établir un nouveau fichier correspondant à votre nouvel environnement :

$ pip freeze -E env -r deploy/development.txt deploy/requirements.txt

Virtualenv & WSGI

Le secret pour utiliser notre environnement virtuel avec apache et mod_wsgi réside dans notre handler wsgi :

$ nano site.py
import os, sys, site
site.addsitedir('/var/www/app.com/current/env/lib/python2.5/site-packages')

sys.path.append('/var/www/app.com/current')
sys.path.append('/var/www/app.com/current/env/lib/python2.5/site-packages')
os.environ['DJANGO_SETTINGS_MODULE'] = 'app.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Pas de changement au niveau de la configuration de mod_wsgi dans apache, vous pouvez malgré tout préciser le nouveau path de python via la directive WSGIPythonHome tel que :

WSGIPythonHome /var/www/app.com/current/env

Fabric

Fabric va nous permettre de déployer notre application en production ou en staging en utilisant pip et virtualenv de concert. Si vous n’avez pas suivi, fabric était dans la liste des dépendances (dans development.txt) donc il devrait se trouver dans notre environnement virtuel.

(env)$ fab
Usage: fab [options] <command>
[:arg1,arg2=val2,host=foo,hosts='h1;h2&#8217;,...] ...

Toute la magie de Fabric se trouve dans le fichier fabfile.py qui va se trouver à la racine de notre projet :

$ touch fabfile.py
$ nano fabfile.py

Le principe d’un fichier fabfile est de déclarer plusieurs fonctions qui font chacune une opération. Le plus souvent, on déclare avant tout des fonctions qui permettent de déclarer nos différents environnements de travail :

def production():
    """Defines production environment"""
    env.user = "deploy"
    env.hosts = ['example.com',]
    env.base_dir = "/var/www"
    env.app_name = "app"
    env.domain_name = "app.example.com"
    env.domain_path = "%(base_dir)s/%(domain_name)s" % { 'base_dir':env.base_dir, 'domain_name':env.domain_name }
    env.current_path = "%(domain_path)s/current" % { 'domain_path':env.domain_path }
    env.releases_path = "%(domain_path)s/releases" % { 'domain_path':env.domain_path }
    env.shared_path = "%(domain_path)s/shared" % { 'domain_path':env.domain_path }
    env.git_clone = "git@github.com:example/app.git"
    env.env_file = "deploy/production.txt"


def staging():
    """Defines staging environment"""
    env.user = "deploy"
    env.hosts = ['dev.example.com',]
    env.base_dir = "/var/www"
    env.app_name = "app"
    env.domain_name = "app.example.com"
    env.domain_path = "%(base_dir)s/%(domain_name)s" % { 'base_dir':env.base_dir, 'domain_name':env.domain_name }
    env.current_path = "%(domain_path)s/current" % { 'domain_path':env.domain_path }
    env.releases_path = "%(domain_path)s/releases" % { 'domain_path':env.domain_path }
    env.shared_path = "%(domain_path)s/shared" % { 'domain_path':env.domain_path }
    env.git_clone = "git@github.com:example/app.git"
    env.env_file = "deploy/production.txt"

Ces deux déclarations peuvent sembler un peu cryptiques, mais elle ne font que définir des variables pour chaque environnement. Ces variables seront ensuite utiliser dans les fonctions qui executent vos actions. Cela permet de pouvoir executer ces actions dans l’environnement. Par exemple, ce jeu de fonctions fonctionnera pour l’environnement de production comme de staging :

def start():
    """Start the application servers"""
    sudo("/etc/init.d/apache2 start")

def restart():
    """Restarts your application"""
    sudo("/etc/init.d/apache2 force-reload")

def stop():
    """Stop the application servers"""
    sudo("/etc/init.d/apache2 stop")

def permissions():
    """Make the release group-writable"""
    sudo("chmod -R g+w $(domain_path)")
    sudo("chown -R www-data:www-data $(domain_path)")

def setup():
    """Prepares one or more servers for deployment"""
    run("mkdir -p $(domain_path)/{releases,shared}")
    run("mkdir -p $(shared_path)/{system,log}")
    permissions()


$ fab production setup restart
$ fab staging setup restart

Pour vous faciliter la vie, voici un fabfile tout fait qui va vous permettre de déployer vos applications facilement et qui a l’avantage de proposer quelques raffinements comme :

  • la possibilité de revenir à la version déployée précédemment.
  • de lancer des migrations via l’utilisation de l’excellent South
  • la possibilité de mettre en place une page de maintenance, mais n’oubliez pas d’ajouter ces quelques lignes à votre configuration apache :

    RewriteEngine on RewriteCond /var/www/app.com/shared/system/maintenance.html -f RewriteRule !^/maintenance.html$ /maintenance.html [R=503,L] ErrorDocument 503 /maintenance.html

Le déploiement de votre application s’effectuera dans le dossier /var/www/$domain_name/, l’application déployée sera accessible dans le dossier current et les fichiers dépendants de votre environnement se trouveront dans le dossier shared. Les derniers déploiements seront accessibles dans le dossier releases, la stratégie de déploiement est basée sur l’utilisation de git, mais il ne tient qu’à vous d’utiliser subversion ou mercurial.

Il vous reste simplement à télécharger le fichier fabfile.py.

+

Croisé dans le métro: Nous ne vous avons pas compris

Déjà un mois que vous pouvez nous préciser la date de votre rencontre de façon naturelle (autant que taper sur un clavier peut l’être) dans cette étape de notre formulaire de publication :

Formulaire de publication

Pour parvenir a ce quasi (non-)exploit, nous avons créé une toute petite librairie nommée

+