Rémy HUBSCHER avatar Rémy HUBSCHER committed d21ef11 Merge

Merge

Comments (0)

Files changed (4)

 
 AUTHOR = u"Rémy Hubscher"
 SITENAME = u"Natim's lab"
-SITEURL = 'http://natim.ionyse.com/'
+SITEURL = 'http://natim.ionyse.com'
 
 DISQUS_SITENAME = 'natimslab'
 GITHUB_URL = 'http://github.com/natim'

src/django-xworkflows.rst

+######################################
+Python xworkflows et django-xworkflows
+######################################
+
+:date: 2012-09-01 15:28
+:tags: python, django
+:category: Python
+:author: Rémy Hubscher
+
+************
+Introduction
+************
+
+Cela fait aujourd'hui un mois que j'ai rejoint l'équipe de Novapost.
+
+C'est donc l'occasion pour moi de faire un petit bilan de ce que j'ai
+appris durant ce mois.
+
+J'aimerais pour commencer vous parler de `django-xworkflows
+<http://django-xworkflows.readthedocs.org/en/latest/>`_.
+
+***************************************************
+Gestion des états d'un objet avec django-xworkflows
+***************************************************
+
+J'en ai entendu parlé lors de la dernière DjangoCong lorsque
+Polyconseil nous a présenté ce qu'ils avaient mis en place pour
+`Autolib <http://www.autolib.eu/>`_.
+
+L'objectif est de définir différents états d'un objet et des
+transitions qui permettent de passer d'un état à l'autre.
+
+Les transitions sont des méthodes ce qui permet de modifier des
+valeurs de l'objet lors d'un changement d'état.
+
+Concrétement dès lors que vous avez un processus en plus de 2 étapes
+vous êtes succeptibles de vouloir utiliser des états à la place d'un
+``BooleanField``.
+
+Prenons un exemple simple. Lors de l'upload d'un document sur votre
+coffre nous devons lancer la génération des miniatures de votre
+document.
+
+Il y a donc quatre états :
+
+ * **init** : Un nouveau document a été créé.
+ * **uploaded** : Le contenu du document a été uploadé le coffre sécurisé.
+ * **queued** : Le document a été envoyé aux workers de génération des miniatures.
+ * **ready** : Les miniatures ont bien été générées, le document est prêt.
+
+Cela évite d'afficher un lien preview alors que les miniatures ne sont
+pas prêtes notamment.
+
+Dans notre cas nous utilisons un model Django pour stocker notre
+document, nous allons donc utiliser ``django-xworkflows`` qui va
+enregistrer notre nouvel état à chaque transition.
+
+.. code-block:: python
+
+    from django_xworkflows import models as xwf_models
+
+    class DocumentWorkflow(xwf_models.Workflow):
+        """States and transitions for :py:class:`Document` model."""
+        #: Disable logging to database
+        log_model = ''
+        #: Available states.
+        states = (
+        ('init', _(u'Created')),
+            ('uploaded', _(u'Uploaded')),
+            ('queued', _(u'Queued for previews generation')),
+            ('ready', _(u'Ready')),
+        )
+        #: Available transitions.
+        transitions = (
+        ('upload', 'init', 'uploaded'),
+            ('queue', 'uploaded', 'queued'),
+            ('activate', 'queued', 'ready'),
+            # Administration command to restart preview generation
+            ('reset', 'queued', 'uploaded'),
+        )
+        #: Default state on instance creation.
+        initial_state = 'init'
+
+Ensuite nous modifions notre models pour y ajouter la gestion des
+workflows.
+
+.. code-block:: python
+
+    from django.db import models
+    from django.core.files.storage import get_storage_class
+    from django_xworkflows import models as xwf_models
+
+    storage_import_string = getattr(settings, 'VAULT_STORAGE',
+                                    'project.storage.DummyStorage')
+    Storage = get_storage_class(storage_import_string)
+    upload_to = Storage.upload_to()
+    
+    class Document(xwf_models.WorkflowEnabled, models.Model):
+        file = models.FileField(verbose_name=_('file'), storage=Storage(), upload_to=upload_to)
+        title = models.CharField(_('title'), max_length=100, blank=True)
+
+        state = xwf_models.StateField(DocumentWorkflow)
+
+        def __unicode__(self):
+            return u'%s' % self.title
+
+        def save(self, *args, **kwargs):
+            flag = self.pk is None
+            if not self.title:
+                self.title = self.file.name
+            super(Document, self).save(*args, **kwargs)
+            if flag:
+                self.upload() # On first save, the document is uploaded to the secure bucket
+
+Maintenant nous avons un models django qui est capable de sauvegarder son état.
+Il faut bien sur mettre à jour la base de données.
+
+.. code-block:: pycon
+
+    >>> from models import Document
+    >>> from django.core.files.base import ContentFile
+    >>> myfile = ContentFile("Foo bar", "foobar.txt")
+    >>> d = Document.objects.create(file=myfile)
+    >>> d.title
+    'foobar.txt'
+    >>> d.state
+    <StateWrapper: <State: 'uploaded'>>
+    >>> d.activate()
+    InvalidTransitionError: Transition 'activate' isn't available from state 'uploaded'.
+    >>> d.queue()
+    >>> print d.state
+    <State: 'queued'>
+    >>> print u'%s' % d.state
+    Queued for previews generation
+    >>> d.state.is_queued
+    True
+
+Nous avons donc des transitions de bases qui nous permette de valider
+les changements d'états.
+
+Ensuite nous pouvons définir des actions lors des transitions :
+
+.. code-block:: python
+
+    from django.core.urlresolvers import reverse_lazy as reverse
+    from django.db import models
+    from django_xworkflows import models as xwf_models
+    import xworkflows
+    import requests
+
+    THUMBNAILER_API = 'http://example.com/async/document/'
+    
+    class Document(xwf_models.WorkflowEnabled, models.Model):
+        file = models.FileField(verbose_name=_('file'))
+        title = models.CharField(_('title'), max_length=100, blank=True)
+        num_pages = models.PositiveIntegerField(editable=False, default=0)
+
+        state = xwf_models.StateField(DocumentWorkflow)
+
+        def __unicode__(self):
+            return u'%s' % self.title
+
+        def save(self, *args, **kwargs):
+            flag = self.pk is None
+            if not self.title:
+                self.title = self.file.name
+            super(Document, self).save(*args, **kwargs)
+            if flag:
+                self.upload() # On first save, the document is uploaded to the secure bucket
+        
+        def _queue(self):
+            """Send job for async preview generation request."""
+            # Add job to redis queue
+            requests.get(THUMBNAILER_API, params = {
+                    'url': self.file.url,
+                    'width': [1000, 750, 150],
+                    'max_pages': 20,
+                    'callback': reverse('vault:thumbnail_callback', self.pk)})
+        
+        @xworkflows.transition()
+        def upload(self):
+            """Change the state when the file has been uploaded to the secure bucket."""
+        
+        @xworkflows.transition()
+        def queue(self):
+            self._queue()
+        
+        @xworkflows.transition()
+        def activate(self, num_pages):
+            self.num_pages = num_pages
+        
+        @xworkflows.transition()
+        def reset(self):
+            self._queue()
+
+Il nous reste simplement à réaliser une view qui va nous permettre de
+mettre à jour le nombre de pages lors du callback.
+
+.. code-block:: python
+
+    from django.shortcuts import get_object_or_404
+    from django.http import HttpResponse
+    from decorators import api_key_validation, post_only
+    from models import Document
+    from xworkflows import InvalidTransitionError
+
+    @api_key_validation
+    @post_only
+    def thumbnailer_callback(request, pk):
+        document = get_object_or_404(Document, pk=pk)
+        num_pages = request.POST.get('num_pages', 1)
+        try:
+            document.activate(num_pages)
+        except InvalidTransitionError, e:
+            return HttpResponse(e.message(), status_code=400)
+        return HttpResponse('Document activated')
+
+Dans nos templates, si nous souhaitons tester si nous devons afficher la preview :
+
+.. code-block:: django
+
+    {% load thumbnailer_tags %}
+
+    {% if object.state.is_activated %}
+    <img src="{% version object.file '150' %}" alt="{{ object.title }}" />
+    {% else %}
+    <img src="{% static 'img/loading.gif' %} alt="{{ object.title }}" />
+    {% endif %}
+
+Comme vous le voyez, il est très simple de tester l'état d'un objet à l'aide d'un boolean.
+
+**********
+Conclusion
+**********
+
+En conclusion : **les workflows c'est bon, mangez-en !**
+
+Ça simplifie grandement la gestion de l'état d'un objet, les
+transitions garantissent que l'objet est toujours dans un état
+stable et correct.
+
+Si vous souhaitez en savoir plus sur notre service de génération des
+miniatures, allez voir `la documentation de Thumbnailer
+<http://thumbnailer.rtfd.org/>`_.

src/pyconfr-2012.rst

+##########################
+PyconFR 2012 à la Villette
+##########################
+
+:date: 2012-08-31 13:55
+:tags: afpy, pycon, python
+:category: Python
+:author: Rémy Hubscher
+
+************
+Introduction
+************
+
+C'est décidé, Novapost sera sponsor GOLD à PyConFR 2012.
+
+Nous serons pas moins 5 développeurs Python à venir prêter main forte
+aux Sprints organisé porte de la Villette à Paris et participer aux
+conférences.
+
+*******
+Sprints
+*******
+
+Voici les sprints auxquels nous comptons participer :
+
+Circus : A Process & Socket Manager
+===================================
+
+ * Site internet : http://circus.readthedocs.org/en/latest/
+
+Description
+-----------
+
+Circus est un superviseur et un lanceur de processus écrit en
+python. Il peut être piloté depuis une interface web, une ligne de
+commande, via des messages json envoyés par ZeroMQ ou depuis ses API
+en python. Circus est utilisé en production chez Mozilla pour
+superviser des services web.
+
+
+Porter vos applications et projets vers Python 3
+=================================================
+
+ * Site internet : https://www.djangoproject.com/weblog/2012/aug/19/experimental-python-3-support/
+
+Description
+-----------
+
+Django est maintenant compatible avec Python 3 même si c'est encore
+sous un status expérimental, cependant avant de pouvoir l'utiliser en
+production, il va falloir passé de nombreuses applications annexes.
+
+ * https://github.com/benoitbryon/django-downloadview
+ * https://github.com/benoitbryon/django-formrenderingtools
+ * https://github.com/brutasse/django-floppyforms
+ * https://github.com/sehmaschine/django-filebrowser
+
+Ce serait l'occasion de commencer à les porter et éventuellement de
+participer à d'autres port proposés lors de l'évènement.
+
+Plus d'info sur les Sprints ici : http://www.pycon.fr/2012/sprints/
+
+***********
+Conférences
+***********
+
+Benoît BRYON sera le conférencier de notre équipe Novapost pour nous
+présenter une conférence sur comment valoriser sa documentation.
+
+Valorisez votre documentation
+=============================
+
+ * Conférencier : Benoît BRYON
+ * Site internet : http://www.pycon.fr/2012/schedule/presentation/11/
+
+Description
+-----------
+
+Rédiger une documentation efficace est un exercice difficile. La
+maintenir, c'est pire. Pourtant, la documentation peut s'avèrer très
+précieuse. Essayons donc de voir s'il existe des bonnes pratiques en
+la matière, et comment les mettre en place pour les projets Python,
+avec Sphinx.
+
+
+Les conférences à ne surtout pas manquer
+========================================
+
+ * `Django, une présentation du framework que l'on ne présente plus <http://www.pycon.fr/2012/schedule/presentation/21/>`_ par Jean-Michel ARMAND
+ * `La boite à outils pour Django <http://www.pycon.fr/2012/schedule/presentation/40/>`_ par Xavier ORDOQUY
+ * `Je configure mes serveurs avec fabric et fabtools <http://www.pycon.fr/2012/schedule/presentation/15/>`_ par Ronan AMICEL
+ * `Nouveautés de Python 3.3 <http://www.pycon.fr/2012/schedule/presentation/1/>`_ par Victor Stinner
+ * `REST in SPORE <http://www.pycon.fr/2012/schedule/presentation/25/>`_ par Arnaud GRAUSEM
+ * `Cornice, créez vos services web simplement <http://www.pycon.fr/2012/schedule/presentation/1/>`_ par Alexis METAIREAU
+ * `Programmation web asynchrone avec Tornado <http://www.pycon.fr/2012/schedule/presentation/19/>`_ par Ronan AMICEL
+ * `Django et Python 3, ce qui va changer <http://www.pycon.fr/2012/schedule/presentation/69/>`_ par Aymeric AUGUSTIN
+ * `Weasy Print : Générer des PDF en Python avec HTML/CSS <http://www.pycon.fr/2012/schedule/presentation/16/>`_ par Simon SAPIN
+ * `Circus - Gestionnaire de processus avancé <http://www.pycon.fr/2012/schedule/presentation/2/>`_ par Tarek ZIADÉ
+
+
+**********
+Conclusion
+**********
+
+Et bien sur PyConFr c'est aussi les repas avec toutes ces têtes
+connues qu'on revoit avec plaisir deux ou trois fois par ans.
+
+J'ai hâte d'y être. À très bientôt.

theme/templates/base.html

                 {% endfor %}
                 {% endif %}
                 {% for cat, null in categories %}
-                    <li {% if cat == category %}class="active"{% endif %}><a href="{{ SITEURL }}/category/{{ cat }}.html">{{ cat }}</a></li>
+                    <li {% if cat == category %}class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
                 {% endfor %}
                 </ul></nav>
         </header><!-- /#banner -->
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.