foxmask – Sam & Max http://sametmax.com Du code, du cul Wed, 23 Dec 2020 13:35:02 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 Histoire de ne pas perdre le fil : TrackingFields http://sametmax.com/histoire-de-ne-pas-perdre-le-fil-trackingfields/ http://sametmax.com/histoire-de-ne-pas-perdre-le-fil-trackingfields/#comments Sun, 07 Feb 2016 11:03:06 +0000 http://sametmax.com/?p=18067 Ceci est un post invité de Foxmask posté sous licence creative common 3.0 unported.

Préambule

Je sais bien qu’une partie de ce billet ne plaira pas à Sam&Max (thanks to the Django CBV & Mixin:)

Introduction
Le but du billet sera de montrer comment, sans rien changer dans un formulaire, on peut arriver à pister les modifications des données effectuées dans l’application.

La première partie va planter le décor en commençant par vous montrer comment s’articule une application avec formulaire composé d’un sous formulaire en sus (j’expliquerai pourquoi après :)

Pour ce faire, je vous emmène dans l’univers du 7° art, viendez on va refaire StarWars!

Un modèle, un formulaire, une vue, un template et ca sera fini.

le models.py

    from django.db import models


    class Movie(models.Model):
        """
            Movie
        """
        name = models.CharField(max_length=200, unique=True)
        description = models.CharField(max_length=200)

        def __str__(self):
            return "%s" % self.name


    class Episode(models.Model):
        """
           Episode - for Trilogy and So on ;)
        """
        name = models.CharField(max_length=200)
        scenario = models.TextField()
        movie = models.ForeignKey(Movie)

        def __str__(self):
            return "%s" % self.name

le forms.py, tout rikiki :

    from django import forms
    from django.forms.models import inlineformset_factory

    from starwars.models import Movie, Episode


    class MovieForm(forms.ModelForm):

        class Meta:
            """
                As I have to use : "exclude" or "fields"
                As I'm very lazy, I dont want to fill the list in the "fields"
                so I say that I just want to exclude ... nothing :P
            """
            model = Movie
            exclude = []

    # a formset based on the model of the Mother "Movie" and Child "Episode" + 1 new empty lines
    # for more details, have a look at https://docs.djangoproject.com/fr/1.9/topics/forms/modelforms/#inline-formsets
    EpisodeFormSet = inlineformset_factory(Movie, Episode, fields=('name', 'scenario'), extra=1)

la vue views.py, très sèche, très DRY ;)

    from django.http import HttpResponseRedirect
    from django.core.urlresolvers import reverse
    from django.views.generic import CreateView, UpdateView, ListView

    from starwars.models import Movie
    from starwars.forms import MovieForm, EpisodeFormSet


    class MovieMixin(object):
        model = Movie
        form_class = MovieForm

        def get_context_data(self, **kw):
            """ init form with data if any """
            context = super(MovieMixin, self).get_context_data(**kw)
            if self.request.POST:
                context['episode_form'] = EpisodeFormSet(self.request.POST)
            else:
                context['episode_form'] = EpisodeFormSet(instance=self.object)
            return context

        def get_success_url(self):
            """ where to go back, once data are validated """
            return reverse("home")

        def form_valid(self, form):
            """ form validation """
            formset = EpisodeFormSet((self.request.POST or None), instance=self.object)
            if formset.is_valid():
                self.object = form.save()
                formset.instance = self.object
                formset.save()

            return HttpResponseRedirect(reverse('home'))


    class Movies(ListView):
        model = Movie
        context_object_name = "movies"
        template_name = "base.html"


    class MovieCreate(MovieMixin, CreateView):
        """
            MovieMixin manages everything for me ...
        """
        pass


    class MovieUpdate(MovieMixin, UpdateView):
        """
            ... and as I'm DRY I wont repeat myself myself myself ;)
        """
        pass

Pour finir de planter le décors et les costumes (merci Roger Hart et Donald Cardwell)

le template base.html

    
    
    
        Manage stories for StarWars
    
    
    

Stories Manager for Starwars

{% block content %} Add a movie

Movie list

{% endblock %}

enfin movie_form.html (le template utilisé par les UpdateView & CreateView)

    {% extends "base.html" %}
    {% block content %}
    
{% csrf_token %} {{ formset.management_form }} {{ form.as_table }}
{{ episode_form.as_table }}
{% endblock %}

Mise à jour de la base de données

cela s’impose :

(starwars) foxmask@foxmask:~/DjangoVirtualEnv/starwars/starwars $  ./manage.py migrate

Operations to perform:
  Synchronize unmigrated apps: messages, starwars, staticfiles
  Apply all migrations: contenttypes, admin, sessions, auth
Synchronizing apps without migrations:
  Creating tables...
    Creating table starwars_movie
    Creating table starwars_episode
    Running deferred SQL...
  Installing custom SQL...

Voilà le tout est prêt (après le lancement du serveur bien sûr), je peux allégrement créer ma double trilogie pépère tel George Lucas.

Trackons l’impie

Seulement un jour arrive où, moi, George Lucas, je vends StarWars à Walt Disney, mais comme je ne veux pas rater de ce qu’ils vont faire de mon “bébé”, je rajoute un “tracker de modifications” à mon application, pour ne pas perdre le “field” de l’Histoire.

Installation de Tracking Fields

en prérequis django-tracking-fields requiert django-cuser, donc ze pip qui va bien donne :

    (starwars) foxmask@foxmask:~/DjangoVirtualEnv/starwars/starwars $ pip install django-tracking-fields django-cuser
    Collecting django-tracking-fields
      Downloading django-tracking-fields-1.0.6.tar.gz (58kB)
        100% |████████████████████████████████| 61kB 104kB/s 
    Collecting django-cuser
      Downloading django-cuser-2014.9.28.tar.gz
    Requirement already satisfied (use --upgrade to upgrade): Django>=1.5 in /home/foxmask/DjangoVirtualEnv/starwars/lib/python3.5/site-packages (from django-cuser)
    Installing collected packages: django-tracking-fields, django-cuser
      Running setup.py install for django-tracking-fields ... done
      Running setup.py install for django-cuser ... done
    Successfully installed django-cuser-2014.9.28 django-tracking-fields-1.0.6

comme de coutume, après un pip install, une modification dans le settings.py suit,

INSTALLED_APPS = (
        ...
        'cuser',
        'tracking_fields',
        ...
    )
    MIDDLEWARE_CLASSES = (
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'django.middleware.security.SecurityMiddleware',
        'cuser.middleware.CuserMiddleware',  ## <=== ne pas oublier, pour chopper le user qui fait le con avec mes films;)
    )

le petit migrate qui va bien aussi pour ajouter les tables pour les modèles de django-tracking-fields

    (starwars) foxmask@foxmask:~/DjangoVirtualEnv/starwars/starwars $  ./manage.py migrate
    Operations to perform:
      Synchronize unmigrated apps: staticfiles, messages, cuser, starwars
      Apply all migrations: auth, sessions, contenttypes, tracking_fields, admin
    Synchronizing apps without migrations:
      Creating tables...
        Running deferred SQL...
      Installing custom SQL...
    Running migrations:
      Rendering model states... DONE
      Applying tracking_fields.0001_initial... OK
      Applying tracking_fields.0002_auto_20160203_1048... OK

et nous voilà prêts à joueur les trackers.

Utilisation

On ne peut pas rêver plus simple, cela se résume à un décorateur sur le modèle qui identifie quelles données sont modifiées, et un field histo qui va lier le modele TrackingEvent de l'application TrackingFields, à ma table à surveiller. Et là, bien que le modele ait été modifié, inutile de faire un nouveau python manage.py migrate, rien ne bougera, histo sera une GenericRelation(). En effet, TrackingEvent repose sur ContenType aka "L'Infrastructure des Types de Contenu". Si vous avez déjà tripoté la gestion des permissions, vous avez déjà dû vous y frotter;)

Pour la faire courte, en clair ça donne :

le models.py arrangé pour l'occasion du décorateur

    from django.db import models
    from django.contrib.contenttypes.fields import GenericRelation

    from tracking_fields.decorators import track
    from tracking_fields.models import TrackingEvent


    @track('name', 'description')   # decorator added
    class Movie(models.Model):
        """
            Movie
        """
        name = models.CharField(max_length=200, unique=True)
        description = models.CharField(max_length=200)
        # to get the changes made on movie
        histo = GenericRelation(TrackingEvent, content_type_field='object_content_type')

        def episodes(self):
            return Episode.objects.filter(movie=self)

        def __str__(self):
            return "%s" % self.name

    @track('name', 'scenario')   # decorator added
    class Episode(models.Model):
        """
           Episode - for Trilogy and So on ;)
        """
        name = models.CharField(max_length=200)
        scenario = models.TextField()
        movie = models.ForeignKey(Movie)
        # to get the changes made on episode
        histo = GenericRelation(TrackingEvent, content_type_field='object_content_type')

        def __str__(self):
            return "%s" % self.name

bon là c'est simplissime comme une recette de pate à crêpes: 3 imports de rigueur, le décorateur et la GenericRelation() on mélange le tout et ca donne ce qui suit J'ai, au passage, rajouté une fonction episodes à ma classe Movie, dont je vous reparlerai plus bas.

le template de la DetailView (pour afficher uniquement les details d'un film) qui va bien

    
    {% for h in object.histo.all %}
       {% for f in h.fields.all %}
           
       {% endfor %}
    {% endfor %}
       
History of the modification of {{ object }}
Old ValueNew ValueByat
{{ f.old_value }}{{ f.new_value }}{{ h.user }}{{ h.date }}

A présent si je me rends dans ma page pour modifier le scénario d'un Episode, mon template ci dessus, ne m'affichera pas ces modications ! Pourquoi bou diou ? Parce qu'ici j'affiche "l'histo" de Movie pas de Episode... On comprend à présent ici mon intéret pour le sous formulaire. Le "problème" aurait été masqué si je m'étais arrêté à un seul simple formulaire.

Corrigeons

c'est là qu'entre en jeu la fonction episodes à ma classe Movie, pour me permettre d'itérer dessus et afficher tout le toutim

le template de la DetailView qui va bien (bis)

    
    {% for h in object.histo.all %}
       {% for f in h.fields.all %}
           
       {% endfor %}
    {% endfor %}
        
History of the modifications of {{ object }}
Old ValueNew ValueByat
{{ f.old_value }}{{ f.new_value }}{{ h.user }}{{ h.date }}
{% for ep in object.episodes %} {% if ep.histo.all %} {% for h in ep.histo.all %} {% for f in h.fields.all %} {% if f.old_value == f.new_value %} {# they are the same when the new value is created to avoid to display "null" #} {% else %} {% endif %} {% endfor %} {% endfor %}
history of the modifications of Episode
Old ValueNew ValueByat
{{ f.old_value }}{{ f.new_value }}{{ h.user }}{{ h.date }}
{% endif %} {% endfor %}

Voili voilou ! Et en prime, si vous êtes curieux, coté admin, vous avez aussi la liste de toutes les modifications si besoin ;)

Aux utilisateurs avertis qui diraient :

pourquoi l'avoir recodé coté front puisque c'est déjà géré coté admin sans lever le petit doigt ?

Parce que George Lucas veut montrer les modifications apportées à son bébé StarWars par Walt Disney, au monde entier pardi !

Ah un détail en passant : dans l'admin la vue qui affiche la liste des modifications donne : "Episode Object" ou "Movie Object". Pour éviter ça, zavez dû remarquer que j'ai mis la fonction __str__ dans mes modèles ce qui vous rendra une valeur plus "lisible" sur ce qui a été modifié.

Conclusion :

Dans la vraie vie de votre serviteur, ne se voyait pas créer un modele "history" lié "physiquement" par une FK à chaque modèle, entreprenait de chercher au travers de la toile quelques ressources.

C'est finallement sur #django-fr@freenode qu'il a posé la question et a obtenu de Gagaro le grââl : une application nommée tracking-fields, dont il est l'auteur.

Pour une fois qu'il fait sa faignasse en ne codant pas tout by himself, ça fait plaisir de tomber sur une appli pareille !

Si vous voulez jouer avec le code de ce gestionnaire de films c'est par ici la bonne soupe

]]>
http://sametmax.com/histoire-de-ne-pas-perdre-le-fil-trackingfields/feed/ 16 18067
Prendre le contrôle d’internet ? C’est possible ! Dégainez & Tirez avec Trigger Happy http://sametmax.com/prendre-le-controle-dinternet-cest-possible-degainez-tirez-avec-trigger-happy/ http://sametmax.com/prendre-le-controle-dinternet-cest-possible-degainez-tirez-avec-trigger-happy/#comments Sun, 28 Dec 2014 06:45:36 +0000 http://sametmax.com/?p=12972 Ceci est un post invité de foxmask posté sous licence creative common 3.0 unported.

“C’est l’histoire d’un mec qu’est su’l’pont de l’Alma et regarde dans” … le python, et comme il débute, se demande mais putain de bordel, des projets à la con à pondre pour se lancer à l’assaut d’un langage, c’est toujours les mêmes trucs chiants que plus personne n’a envie de voir tels : forum, blog, wiki, cms. Alors comment être novateur un poil plus que ces projets sans (plus aucun) défit technique ?

A cette question je me suis dit, pourquoi ne pas produire “simplement” (toute proportion gardée) un équivalent libre au célébrissime IFTTT ?

Mais qu’est-ce ?

IFTTT est un service qui vous permet de brancher entre eux, les services internet où vous possédez un compte, comme Twitter, Facebook pour ne citer qu’eux (parce que la liste est longue comme un python au moins;) Et donc quand un évènement défini se produit sur votre compte twitter, genre un tweet de votre poto tombe, le service réagit au quart de tour pour rebalancer les données ailleurs, sur Facebook, un blog et n’importe quoi qui vous chante.

Hé bien cet équivalent libre est Trigger Happy, et ce “principe” décrit ci-dessus défini un ESB (Entreprise Service Bus, très connu du monde Java), un BUS qui récupère des données de droite et vous les expédie à gauche.

Wikipedia le défini ainsi :
L’enterprise service bus (ESB) est une technique informatique intergicielle. Son but est avant tout de permettre la communication des applications qui n’ont pas été conçues pour fonctionner ensemble

Pourquoi un tel projet ?

3 aspects :

  • Avec ce projet je me suis lancé le défit d’arriver à cerner au mieux Django et abordé au mieux Python.
  • Comme tout ce que j’entreprends, se produit par pur réaction allergique à un truc qui m’a gonflé, explications les zamis :
    Au début on fait (plus ou moins) comme tout le monde, sa veille techno à partir de flux RSS de sites web choisis, avec un lecteur de flux RSS moins bien choisi comme iGoogle à une certaine époque ou encore … Google Reader.
    Là le truc qui m’a pris le chou, ce fut d’avoir dans Evernote, le résultats des flux RSS de mes sites favoris complètement atrophiés.
    Du genre “il a été à l’école” devenait “il a t l’cole”. Or le problème ne vient pas d’Evernote qui affiche parfaitement nos chers caractères latins, mais de IFTTT qui les bouffait au passage. Outre IFTTT il existe des versions payantes de services mais qui sont vite très vite limitées dans le nombre de triggers déclenchés que sont Zapier et CloudWork.
  • Le dernier aspect concerne sa chère vie privé.
    Oui en effet, pour que IFTTT puisse récupérer des donnés d’un service à l’autre, il faut que vous ayez un compte sur chaque service comme Twitter et Facebook. Pourquoi IFTTT en a besoin ? Pour que, “en votre nom”, avec les accréditations que vous leur octroyez, puisse lire/écrire les informations de part et d’autre.
    Mais pourquoi faire confiance à IFTTT / Cloudwork / Zapier ? Pourquoi leur offrir NOS accréditations les yeux fermés ?
    Trigger Happy repond à la question puisque l’application vous appartient, les accréditations obtenues de Twitter, Facebook etc, sont stockées dans VOTRE application Trigger Happy. Et personne n’ira les exploiter pour voir ce que vous faites sur vos comptes.

Comment fonctionne le projet ?

Dans la version simplifiée ceci donne :

Trigger Happy wokring process

Image tirée de ma main “gauche” gauche, enfin la gauche pas adroite


De part et d’autres (dans les nuages) on a les services qui nous intéressent
Les éclairs oranges sont les connecteurs qui permettent de dialoguer avec les services
Au milieu .. (non ne coule pas une rivière) le moteur Trigger Happy qui gère, via les connecteurs, l’échange des données.

Dans la version complète ceci donne :

Trigger Happy Micro ESB

Trigger Happy Micro ESB

On a au milieu un “tube” (pipeline) qui va permettre, via des “command” & settings & url & service provider, d’identifier les services “django th 1, 2, 3, 4”.
A un moment donné, à l’entrée du tube, arrive un “flux” de donnés identifié pour admettons “django th 1” aka un flux RSS (au pif) puis on identifie une destination, admettons “django th 2” aka “twitter”, et les données collectées repartent donc du tube vers twitter, comme le service s’attend à les recevoir.

Dans la vraie vie comment ça se passe ?

Ça se passe bien mon zami : je m’en va (la fote est volontaire tout comme l’reste;) vous montrer la création d’un trigger, permettant d’extraire les billets du flux de mon blog et de les renvoyer sur Twitter en 5 étapes :

Ici en premier lieu vous pouvez voir la liste des services que Trigger Happy gere pour l’utilisateur courant (mézigues)

Trigger Happy : services activés

Trigger Happy : services activés

Puis l’accueil de l’appli où on remarquera que je suis radin en nombre de trigger affichés par page parce que … je me sers de l’appli via un browser sur mon smartphone m’sieur ‘dames :

Trigger Happy : Accueil

Trigger Happy : Accueil

Etape 1:

Trigger Happy : Etape 1 de la création d'un trigger

Choix du service fournissant les données

Etape 2:

Trigger Happy : Etape 2 de la création d'un trigger

nommage du service et fourniture de l’origine des données

Etape 3:

Trigger Happy : Etape 3 de la création d'un trigger

Choix du service accueillant les données

Etape 4:

Trigger Happy : Etape 4 de la création d'un trigger

Trigger Happy : Etape 4 de la création d’un trigger

Ensuite le moment venu, se déclenche ce trigger, et pour en voir le résultat, on peut consulter le billet que j’avais préparé le mois dernier pour une présentation Django Paris. Et qui au moment de la publication du billet, à 19h15 pétantes, est tombé sur twitter directement et Evernote dans la foulée.

Voilà !

Le défit est parti et n’attend plus qu’à ce que le nombre de services croissent. Pour cela rien de plus simple, j’ai fait une doc expliquant comment pondre un module django qui exploite le service de votre choix tel Buffer, Trello, Dropbox et j’en passe et des meilleurs. Tout ce dont on a besoin : l’API du service visé en python, créer un compte pour avoir accès au service et suivre le howto sur readthedoc

Last but not least aux dev : ca tourne avec django 1.7 / Python 2.7 et 3.4

Dernier détail: comme je suis sûr que vous vous demandez pourquoi ce nom de projet ? C’est un perso de la franchise Skylander auquel joue mon fils et comme le projet “trigger” à tirelarigot sur l’net, ca collait pile poil :)

]]>
http://sametmax.com/prendre-le-controle-dinternet-cest-possible-degainez-tirez-avec-trigger-happy/feed/ 4 12972
Python a le don d’Ubiquité : Multiprocessing http://sametmax.com/python-ubiuite-multiprocessing/ http://sametmax.com/python-ubiuite-multiprocessing/#comments Sun, 02 Feb 2014 16:41:23 +0000 http://sametmax.com/?p=8939

Ceci est un post invité de Foxmask posté sous licence creative common 3.0 unported.

Tout récemment j’ai voulu donner un coup de fouet à mon script de traitement de Trigger Happy (que je fais tourner sur ma “raspberry pi” parce que chuis un w4rl0rdZ:P) que j’estimais être trop long dans ses traitements de données.

Actuellement avec Trigger Happy, j’ai 30 sources de données (essentiellement des flux rss), que je parcours, et quand un nouvel item arrive, je l’envoi à pétaouchnock (Evernote ;) Le tout prend 7min, soit 14secondes par source. La loose totale.

Voici le corps du délit :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import datetime
import time

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_th.settings")
from django_th.services import default_provider
from django_th.models import TriggerService
from django.utils.log import getLogger

# create logger
logger = getLogger('django_th.trigger_happy')

def go():
    """
        run the main process
    """
    trigger = TriggerService.objects.filter(status=True)
    if trigger:
        for service in trigger:
[...]
    else:
        print "No trigger set by any user"


def main():
    default_provider.load_services()
    # let's go
    go()

if __name__ == "__main__":

    main()

Mais avant que je ne me penche sur le code du script pour l’améliorer, je me suis dit que plutôt que de chercher à corriger un problème, autant chercher la source de celui-ci d’abord (normal hein).

Un HTOP m’a révélé :

  1. que le CPU était à 100% tout le temps, que le script tourne ou pas
  2. quye la raison était double : rabittmq et celery…

Une fois shootés ces derniers, tout va pour le mieux :P
Je ne dis pas que ceux ci sont de la merde, mais que, pour mon cas, la crontab se suffit à elle-même.

Donc une fois désinstallés c’est 2 (sur)consommateurs de ressources, je relance le script pour tomber à un temps de traitement à 5mn

2014-02-02 14:40:51,693 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Sam et Max nothing new
2014-02-02 14:40:53,865 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News de Numerama nothing new
2014-02-02 14:40:56,013 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de La Ferme du Web nothing new
2014-02-02 14:41:01,005 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - flux de Paulds nothing new
2014-02-02 14:41:20,098 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux d'un Odieux Connard nothing new
2014-02-02 14:41:22,142 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Strict minimum nothing new
2014-02-02 14:41:25,868 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Humeurs Illustrées nothing new
2014-02-02 14:41:33,497 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux du Journalisme Total nothing new
2014-02-02 14:41:35,658 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Kernel Panic nothing new
2014-02-02 14:41:44,897 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de AngularJS nothing new
2014-02-02 14:41:49,016 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Odeon nothing new
2014-02-02 14:41:54,186 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Nicolargo nothing new
2014-02-02 14:42:12,525 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de JEEK nothing new
2014-02-02 14:42:21,349 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Recher nothing new
2014-02-02 14:42:31,266 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Un blog d'adminsys Libres nothing new
2014-02-02 14:42:35,824 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - flux de Le bloc-notes de Gee nothing new
2014-02-02 14:42:36,647 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de mere code (atom) nothing new
2014-02-02 14:42:39,616 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Alex Mac Caw nothing new
2014-02-02 14:42:42,985 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Yearofmoo Articles nothing new
2014-02-02 14:43:42,732 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Novapost nothing new
2014-02-02 14:43:46,722 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Les Numériques nothing new
2014-02-02 14:43:58,303 INFO fire 5142 date 2014-02-02 13:00:00 >= date triggered 2014-02-02 09:02:36 title Test du Quechua Phone 5, le smartphone des montagnards ?
2014-02-02 14:44:08,010 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Frandroid = 1 new data
2014-02-02 14:44:17,624 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de TechCrunch Mobile nothing new
2014-02-02 14:44:20,339 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux Django annonces nothing new
2014-02-02 14:44:20,744 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - La Hyène - Python nothing new
2014-02-02 14:44:24,237 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News de PCInpact nothing new
2014-02-02 14:44:29,055 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - PointGPhone nothing new
2014-02-02 14:44:31,299 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - HumanCoders Python nothing new
2014-02-02 14:44:52,751 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - HauteFeuille Lab (python) nothing new
2014-02-02 14:44:58,850 INFO fire 5142 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Python Plone Planet nothing new

Comme je suis un éternel insatisfait de bibi, j’ai cherché des moyens un peu partout, jusqu’à ce que Sam me souffle une suggestion ;)

A présent donc une version modifiée pour exploiter le multiprocessing est la suivante :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import datetime
import time
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "th.settings")

from django_th.services import default_provider
from django_th.models import TriggerService
from django.utils.log import getLogger

# create logger
logger = getLogger('django_th.trigger_happy')

def go(service):
    """
        run the main process
    """
    [...]


def main():
    default_provider.load_services()
    # let's go
    trigger = TriggerService.objects.filter(status=True)
    if trigger:
        from multiprocessing import Pool
        pool = Pool(processes=5)
        result = pool.map(go, trigger)
    else:
        print "No trigger set by any user"

if __name__ == "__main__":

    main()

fait tomber le temps de traitement à … 1min …:

$ date && ./fire.sh && date 
dimanche 2 février 2014, 14:58:38 (UTC+0100)
2014-02-02 14:58:48,221 INFO fire 5334 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de La Ferme du Web nothing new
2014-02-02 14:58:48,243 INFO fire 5336 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Humeurs Illustrées nothing new
2014-02-02 14:58:48,256 INFO fire 5337 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Kernel Panic nothing new
2014-02-02 14:58:48,283 INFO fire 5333 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Sam et Max nothing new
2014-02-02 14:58:48,907 INFO fire 5335 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux d'un Odieux Connard nothing new
2014-02-02 14:58:49,267 INFO fire 5334 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - flux de Paulds nothing new
2014-02-02 14:58:49,446 INFO fire 5336 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux du Journalisme Total nothing new
2014-02-02 14:58:49,713 INFO fire 5333 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News de Numerama nothing new
2014-02-02 14:58:49,847 INFO fire 5335 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Strict minimum nothing new
2014-02-02 14:58:50,209 INFO fire 5334 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Odeon nothing new
2014-02-02 14:58:50,353 INFO fire 5337 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de AngularJS nothing new
2014-02-02 14:58:50,830 INFO fire 5333 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Un blog d'adminsys Libres nothing new
2014-02-02 14:58:51,338 INFO fire 5336 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de JEEK nothing new
2014-02-02 14:58:51,396 INFO fire 5334 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Nicolargo nothing new
2014-02-02 14:58:51,476 INFO fire 5337 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Yearofmoo Articles nothing new
2014-02-02 14:58:51,735 INFO fire 5333 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - flux de Le bloc-notes de Gee nothing new
2014-02-02 14:58:52,148 INFO fire 5335 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de mere code (atom) nothing new
2014-02-02 14:58:52,640 INFO fire 5336 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Recher nothing new
2014-02-02 14:58:52,971 INFO fire 5335 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Alex Mac Caw nothing new
2014-02-02 14:58:53,416 INFO fire 5334 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Les Numériques nothing new
2014-02-02 14:58:53,474 INFO fire 5333 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de TechCrunch Mobile nothing new
2014-02-02 14:58:53,870 INFO fire 5337 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Novapost nothing new
2014-02-02 14:58:54,072 INFO fire 5335 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - PointGPhone nothing new
2014-02-02 14:58:54,316 INFO fire 5333 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux Django annonces nothing new
2014-02-02 14:58:54,853 INFO fire 5336 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - La Hyène - Python nothing new
2014-02-02 14:58:55,111 INFO fire 5335 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - HumanCoders Python nothing new
2014-02-02 14:58:55,222 INFO fire 5334 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News Frandroid nothing new
2014-02-02 14:58:55,380 INFO fire 5337 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - HauteFeuille Lab (python) nothing new
2014-02-02 14:58:55,696 INFO fire 5336 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - News de PCInpact nothing new
2014-02-02 14:59:02,214 INFO fire 5337 user: foxmask - provider: ServiceRss - consummer: ServiceEvernote - Flux de Python Plone Planet nothing new
dimanche 2 février 2014, 14:59:02 (UTC+0100)

Comme on l’aura remarqué la différence entre les 2 versions est l’appel fait à a fonction go

avant :

def main():
    default_provider.load_services()
    # let's go
    go()

après :

def main():
    default_provider.load_services()
    # let's go
    trigger = TriggerService.objects.filter(status=True)
    if trigger:
        from multiprocessing import Pool
        pool = Pool(processes=5)
        result = pool.map(go, trigger)
    else:
        print "No trigger set by any user"

du coup l’appel de la fonction “go” implique de changer sa signature en lui filant comme argument “trigger” (le QuerySet de l’appli Django)

A présent donc pool.map fait l’itération des données trouvées dans le modele TriggerService et exécute tout le tintouin. *<:o) ps : @Sam : chose promise chose dûe ;) edit: apres de moult nouveaux essais sur le sujet, seul SQLite supporte cette façon de faire. MySQL, PostgreSQL non. La faute au multiprocessing, qui m’a-t-on confirmé de ci de là ne convient pas du tout pour gérer des connexions aux bases.

]]>
http://sametmax.com/python-ubiuite-multiprocessing/feed/ 7 8939