Quantcast
Channel: AFPy's Planet
Viewing all 3409 articles
Browse latest View live

[Biologeek] Avenir de Libération

$
0
0

Les actionnaires « regrettent vivement » la grève observée jeudi. Estiment à ce point Libération qu’ils jugent que le journal « ne doit son salut qu’à l’agrégation de subventions de la puissance publique ». […] Annoncent « un déménagement devenu inéluctable » et la transformation de Libération en « un réseau social, créateur de contenus, monétisable sur une large palette de supports multimédias (print, vidéo, TV, digital, forums, événement, radio, etc.) ». Précisent qu’« avec l’aide de Philippe Starck » le siège historique du journal, rue Béranger, deviendra « un espace culturel de conférences comportant un plateau télé, un studio radio, une newsroom digital, un restaurant, un bar, un incubateur de start-up ». […] Cris, hurlements et rires.

Les jours noirs d’un quotidien, Libération

Disclaimer : j’ai travaillé pour Libération lorsque j’étais à mon compte pour les aider dans leur migration sous Django, j’y ai rencontré des personnes que j’estime beaucoup avec lesquelles nous avons aussi eu l’occasion de discuter de l’avenir de Libération. L’avis suivant ne tient pas compte de ces remarques internes, confidentielles et/ou privées.

Le passé et le présent de Libération sont assez emblématiques d’une presse qui n’a pas su s’adapter au Web. Il n’y a finalement que le Canard Enchaîné qui aura réussi cette non-transition avec brio en choisissant délibérément de ne pas y aller. Quelques propositions pour qu’un quotidien devienne pertinent sur Internet :

  • un modèle participatif en ligne avec la création de lieux numériques d’échanges et de débats auxquels les journalistes participent (pour remonter le niveau des commentaires actuels !), les journalistes proposent des thématiques qui permettent une co-rédaction des articles au cours de la journée pour les enrichir des remarques des internautes. Chaque espace daté en ligne devenant un lieu d’échanges pour la journée sur les actualités apportées par le journal. Les visiteurs qui ne sont pas abonnés n’ont accès qu’à une version statique des thématiques du jour J qui ne s’affichent enrichies des commentaires qu’à J+1. La fraicheur des participations fait partie intégrante du modèle économique.
  • un modèle transparent sur le terrain avec la remontée des informations brutes afin que les abonnés puissent les utiliser et réutiliser lors de leurs contributions en ligne. La valeur du quotidien ne se fait plus sur le scoop mais sur le remixage des informations par et pour ses lecteurs. Les internautes peuvent également fournir leurs sources pour enrichir ces données.
  • un modèle collaboratif en présence avec la publication d’un journal papier à J+1 qui fait une synthèse des discussions et des différents éléments publiés pour en faire un journal d’opinion. Réduction de l’instantanéité (facile à trouver par ailleurs) mais amélioration de la pertinence qui devient très rare (et donc chère). Le réaménagement d’une partie des locaux est envisageable pour accueillir des lecteurs/contributeurs lors de cette étape également. L’implication devient la clé de la fidélisation.
  • un modèle coopératif en statut avec la transformation en SCOP incluant l’ensemble des abonnés sur les décisions stratégiques avec « une personne = une voix ». Je ne sais pas dans quelle mesure ce statut serait compatible avec les aides publiques dédiées au journalisme mais ce serait l’occasion de faire bouger les choses dans ce domaine aussi. L’utilisation de la réserve obligatoire permettrait à terme d’assurer une véritable indépendance du journal.

Ces propositions sont naïves car je n’ai pas suffisamment d’éléments pour pouvoir prendre en compte toutes les contraintes. Mais elles ont le mérite de faire un peu plus que crier, hurler ou rire.


[cyp] Ajouter un moteur de rendu à Pyramid

$
0
0

Il y a beaucoup de moteur de rendu disponible pour pyramid mais il peut arriver qu'on ait besoin de créer le sien ou d'en ajouter un dont le support n'a pas encore été fait.

Plutôt que donner des exemples abstraits, je vais illustrer avec un module relativement simple que j'ai écrit (lire dont je comprends le code) : pyramid_xslt

C'est quoi xsl ?

Dans le cadre de mon travail, je manipule beaucoup de fichiers XML et notamment je les transforme en fichiers HTML. XSLT est le langage XML qui transforme un contenu XML en XML ou texte.

Soit le XML suivant :

<document>
  <title>Some title</title>
  <section>
    <section-title>First Section</section-title>
    <content>foo bar</content>
  </section>
  <section>
    <section-title>Second Section</section-title>
    <content>baz baz</content>
  </section>
</document>

La XSLT suivante le transforme en HTML :

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="document">
  <html>
    <head>
       <xsl:apply-templates select='title' mode="meta" />
    </head>
    <body>
     <article>
       <xsl:apply-templates select='title' />
       <xsl:apply-templates select="section" />
     </article>
    </body>
  </html>
</xsl:template>

<xsl:template match="title" mode="meta">
  <title><xsl:value-of select="." /></title>
</xsl:template>

<xsl:template  match="title">
  <h1><xsl:value-of select='.' /></h1>
</xsl:template>

<xsl:template match="section">
  <div>
    <xsl:apply-templates />
  </div>
</xsl:template>

<xsl:template match="section-title">
 <h2><xsl:value-of select="." /></h2>
</xsl:template>

<xsl:template match="content">
  <p>
    <xsl:value-of select="." />
  </p>
</xsl:template>

</xsl:stylesheet>

On exécute en CLI :

$ xsltproc sample.xsl sample.xml > sample.html

ce qui donne comme HTML :

<?xml version="1.0"?>
<html>
 <head>
   <title>Some title</title>
 </head>
 <body>
   <article>
     <h1>Some title</h1>
     <div>
    <h2>First Section</h2>
    <p>foo bar</p>
  </div>
     <div>
    <h2>Second Section</h2>
    <p>baz baz</p>
  </div>
   </article>
 </body>
</html>

Dans python, on peut utiliser lxml :

from lxml import etree

transform = etree.XSLT(etree.parse('sample.xsl'))
print(etree.tostring(transform(etree.parse('sample.xml')))

Voila pour XSLT.

Scénario d'utilisation

Le but du jeu est de me simplifier la vie. Sans l'ajout de moteur de rendu, le code serait comme cela :

from lxml import etree


# la contruction de cet objet pique un peu autant le faire qu'une seule fois.
transform = etree.XSLT(etree.parse('sample.xsl'))

@view_config(name='some_route', renderer='string')
def some_view(request):
    filename = request.POST['select_file']

    return etree.tostring(transform(etree.parse(filename)))

La finalité du moteur de rendu est que le code devienne :

@view_config(name='some_route', renderer='template/sample.xsl')
def some_view(request):
    filename = request.POST['selected_file']

    return etree.parse(filename)

On peut également passer des variables à une XSLT. ce qui donnerait dans notre scénario :

@view_config(name='some_route', renderer='template/sample.xsl')
def some_view(request):
    filename = request.POST['selected_file']
    name = request.POST['name']

    return etree.parse(filename), {'name': name}

L'implémentation

Pour ajouter un moteur de rendu, on a besoin d'une factory, d'un renderer et de rajouter ce rendu à pyramid.

Factory

La factory sert à construire le renderer qui effetura le rendu.

La signature de la factory est la suivante :

def factory(info):
    def renderer(value, system):
        pass

    return renderer

ou la version orientée objet :

class Factory(object):
    def __init__(self, info):
        pass

    def __call__(self, value, system):
        pass

renderer et __call__ doivent retourner une chaine contenant le rendu.

info contient un pyramid.renderers.RendererHelper.

Dans le cas de notre moteur de rendu xsl :

class XsltRendererFactory(object):
    def __init__(self, info):
        """
        Factory constructor.
        """
        self._info = info

    def __call__(self, value, system):
        """
        Call to renderer.

        The renderer is cached for optimize access.
        """

        xsl = XslRenderer(os.path.join(package_path(self._info.package),
                                       system['renderer_name']))
        return xsl(value, system)

XslRenderer est notre objet qui va effectuer la transformation.

Le Rendu

Le rendu doit implémenter l'interface IRenderer [1] de pyramid : juste la méthode __call__ avec la signature suivante :

[1]IRenderer est une interface ZCA : zope.interface : voir l'épisode précédent
def __call__(self, value, system):
    pass

La méthode __call__ doit retourner une chaine avec le résulat du rendu.

value contient la donnée retournée par la méthode décorée par le view_config. system est un dictionnaire contenant :

  • renderer_info : même RendererHelper que info passé à la factory,
  • renderer_name : valeur de renderer du décorateur view_config ; typiquement le chemin vers le template,
  • context : un object pyramid.traversal.DefaultRootFactory,
  • req : l'objet request,
  • request : le même objet request,
  • view : la fonction décorée correspondant à la vue.
@implementer(IRenderer)
class XslRenderer(object):

    def __init__(self, xslfilename):
        """
        Constructor.

        :param: xslfilename : path to the xsl.
        """
        self._transform = etree.XSLT(etree.parse(xslfilename))

    def __call__(self, value, system):
        """
        Rendering.
        """
        xslArgs = {}

        try:
            xslArgs  = {key: str(value[1][key]) for key in  value[1]}
        except IndexError:
            pass

        return etree.tostring(self._transform(value[0], **xslArgs))

Le rendu est très simple à écrire, la version sur github est à peine plus compliquée pour gérer des fichiers, url ou arbre etree.

Utilisation du registre

Si le rendu est très simple, la factory mérite d'être complexifiée. Comme dit plus haut, la construction de la xsl est coûteuse à construire. On va utiliser le registre de pyramid pour contruire une seule fois la classe de rendu. Le registre est un registre ZCA ; le nom de fichier de la xsl servira de clef. La première requête construira la xsl, les suivantes n'auront qu'à utiliser l'objet construit.

class XsltRendererFactory(object):
    def __init__(self, info):
        """
        Factory constructor.
        """
        self._info = info

    def __call__(self, value, system):
        """
        Call to renderer.

        The renderer is cached for optimize access.
        """

        registry = system['request'].registry
        try:
            xsl = registry.getUtility(IRenderer, system['renderer_name'])
        except ComponentLookupError:
            xsl = XslRenderer(os.path.join(package_path(self._info.package),
                                          system['renderer_name']))
            registry.registerUtility(xsl, IRenderer, system['renderer_name'])
        return xsl(value, system)

Inclusion dans pyramid

Il reste encore à ajouter le support du rendu à pyramid. Dans la fonction main de pyramid, on rajoute où config est la configuration de pyramid.

config.add_renderer('.xsl', XsltRendererFactory)

le .xsl permet de reconnaître le moteur de rendu via @view_config.

Rendre le rendu utilisable par d'autres applications pyramid

L'intérêt de porter ou d'écrire un moteur de rendu est de le réutiliser. pyramid a un mécanisme très simple pour cela.

Dans le __init__.py de notre rendu, il suffit d'inclure le code suivant :

def includeme(config):
    """
    Auto include pour pyramid
    """
    config.add_renderer('.xsl', XsltRendererFactory)

Pour l'utiliser dans notre application, où on l'inclus le code suivant dans le main de pyramid :

config.include('pyramid_xslt')

ou dans fichier .ini

pyramid.includes =
    pyramid_xslt

And that's all folk.

[tshirtman] Using jinja2 outside of the web.

$
0
0

I may be the last person out there to do that, but i hadn’t actually gotten to that yet.

So here is the thing, sometime you want to produce a text file, with some complex structure, and string.format is not really up to the task, so you scratch your head and wonder what could be useful to… template an output.

Then a nice lib like jinja2 totally makes sense.

For my use case, i want to generate java source code, using info parsed from other java source code (thanks to the awesome plyj lib), so it’s easy to give the general form of the output file, and to pass the content after that.

{% if package %}package {{ package }};{% endif %}
import {{ module.package_declaration.name.value }}.*;
{{ imports }}\

public class {{ cls.name }}{{ suffix }} extends {{ cls.name }} {
    public interface I{{ cls.name }} {\
    {% for method in methods %}
    {{ method.return_type | get_type }} {{ method.name }}({{ method.parameters | render_params }});\
    {% endfor %}
    }

    private I{{ cls.name }} implem = null;

    public void setImplem(I{{ cls.name }} implem) {
    this.implem = implem;
    }

    {% for method in methods %}
    {{ method.return_type | get_type }} {{ method.name }}({{ method.parameters | render_params }}) {
    if (this.implem)
        return this.implem.{{ method.name }}({{ method.parameters | render_params_values }});
    }{% endfor %}
}

One problem i had, though, was when it turned out it would be convenient to use custom filters inside my template, it took me some time to find the relevant documentation about how to register functions as filters in this situation (since people usually use jinja2 in a framework, they document how to do so using the environment used by this framework).

So the answer was not to use jinja2.Template directly, but to build an environment before.

env = jinja2.Environment()

then to setup my filters in this environment:

env.filters['render_params'] = render_params
env.filters['render_params_values'] = render_params_values
env.filters['get_type'] = get_type

and to use env.template_from_string(template_string) to create a Template object from my string.

[gvaroquaux] Hiring a programmer for a brain imaging machine-learning Python library

$
0
0
Work with us on putting machine learning in the hand of cognitive scientists Parietal is a research team that creates advanced data analysis to mine functional brain images and solve medical and cognitive science problems. Our day to day work is to write machine-learning and statistics code to understand and use better images of brain function [...]

[cubicweb] CubicWeb sprint / winter 2014

$
0
0

Priorities

Let us not forget the priorities from the roadmap:

Limit work in progress

In order to reduce work in progress, here is a list of patches that are reviewed or pending-review:

User (40)Projecthas createdStatecreation dateUser
sthenaultapycotrestore and unittest apycot.recipe.fullpending-review2013/03/08 14:17carlos
ddouardcubicweb-inventory0.2.0reviewed2013/03/09 20:59pydavid
ddouardcubicweb-inventory[wf] add an 'unused' workflow state for a Device (closes #2725676)reviewed2013/03/09 21:01pydavid
ksaurfeltcubicweb-simplefacetsplit `views.py` into `views.py` and `facets.py`reviewed2013/03/12 17:43arichardson
acampeascubicweb-timeseries[schema] relax format_preferences cardinality (closes #2556458)reviewed2013/03/22 20:23ksaurfelt
ksaurfeltcubicweb-simplefacet[html] remove an useless divreviewed2013/03/28 16:03sthenault
ksaurfeltcubicweb-orbui[html] remove trailing </li>reviewed2013/03/29 11:07vpopescu
ksaurfeltcubicweb-orbui[forms] form may hot have buttonsreviewed2013/03/29 11:07vpopescu
ksaurfeltcubicweb-orbui[forms] improve form errors cssreviewed2013/04/03 15:16adimascio
ksaurfeltcubicweb-orbui[pkg] add `data` subdirectoriesreviewed2013/04/04 14:17aleufroy
ksaurfeltcubicweb-orbui[css] fix missing "required.png" imagereviewed2013/04/11 11:24jcristau
ksaurfeltcubicweb-orbui[forms] fix EntityInlinedFormRendererOrbuireviewed2013/04/24 13:41alutz
ksaurfeltcubicweb-orbui[css] fix reledit cancel bugreviewed2013/05/02 12:42ddouard
dimitricubicweb-brainomicsChange the name of the Localizer subprojectreviewed2013/05/06 15:10alutz
dimitricubicweb-brainomicsNo spanish translation for Brainomicsreviewed2013/05/06 16:25alutz
dimitricubicweb-brainomicsAdd copyright noticereviewed2013/05/06 17:59celsoflores
dimitricubicweb-brainomicsUse register_and_replace instsead of unregister/registerreviewed2013/05/06 19:05celsoflores
dimitricubicweb-brainomicsOnly allow de-faced MRI anatomical imagesreviewed2013/05/07 17:05jcristau
dimitricubicweb-brainomicsExpand translationsreviewed2013/05/13 22:44celsoflores
dimitricubicweb-brainomics[views] Documentation typoreviewed2013/05/13 22:46celsoflores
dimitricubicweb-brainomicsMore precise messagesreviewed2013/05/14 18:18alutz
dimitricubicweb-brainomicsCompress imagesreviewed2013/05/16 14:17arichardson
dgaraudcubicweb-inventoryAdd a dependency to the cube 'Booking'.reviewed2013/05/24 09:30fcayre
ddouardcubicweb-inventory[entities] display the manufacturer in the dc_title of a DeviceModel (closes #2725679)reviewed2013/05/27 10:41ksaurfelt
dimitricubicweb-brainomicsPEP8reviewed2013/05/27 13:57acampeas
celsoflorescubicweb-orbui[Doc] Small improvements to /doc via CSSreviewed2013/06/04 10:08ptonelli
vpopescucubicweb-comment[doc] Document basic usage of the cubereviewed2013/06/17 04:53celsoflores
acampeascwtagsAutomatic escaping (closes #620533)reviewed2013/06/25 15:33dlaxalde
dimitricubicweb-brainomicstyporeviewed2013/06/28 17:12celsoflores
rcardonacubicweb[web/views] Extract legacyui (closes #2541404)reviewed2013/06/28 20:43celsoflores
fcayrecubicweb-testcard[packaging] add spec file for centosreviewed2013/07/01 19:17celsoflores
fcayrecubicweb-email[packaging] add spec file for centosreviewed2013/07/01 19:20celsoflores
ksaurfeltcubicweb-medicalexp[facet] use simplefacet `SimpleFacetRangeFacet` instead of RangeFacetreviewed2013/07/03 16:28jroy
fcayrecubicweb-tag[packaging] add spec file for centosreviewed2013/07/08 14:20ptonelli
ptonelliapycot[apycotlib] use new http_file_upload for uploading logspending-review2013/07/18 16:58aleufroy
ptonelliapycot[apycotlib] cannot have a . in a paramter name (closes #3024946)pending-review2013/07/18 16:58carlos
ddouardapycotRESTify apycotlibpending-review2013/07/18 18:49ksaurfelt
fcayrecubicweb-faqadd a test for FAQ breadcrumbsreviewed2013/07/25 09:46arichardson
sthenaultapycot[test] reload quick recipe on startupreviewed2013/07/25 14:55jcristau
ptonelliapycotCheckresults tabs are now ordered by starttime order (close #2923618)reviewed2013/07/30 10:29sginier

[anybox] Sphinx autodoc et modules OpenERP

[afpy.org] Naissance d'une communauté Saltstack francophone

$
0
0
Saltstack est le logiciel libre de gestion de configuration qui monte. Rejoignez sa communauté francophone !

[afpy.org] PyCon.FR 2014

$
0
0
Il est bientôt temps de commencer à organiser la conférence Python francophone. Cet article est un appel aux bonnes volontés.

[Biologeek] Manifeste de développeur

$
0
0

Like any experienced engineer, I understand the desire to build the best, most flexible and robust system for every project. I do. But I also understand the common business constraints of every project: time and money. Most projects have a definite deadline and/or a specific budget that must be met and, often times, building something grand is just not feasible within either them. This is where the developer must make a conscious decision to limit creativity to meet the goals. There is no excuse for spending a week to setup a "proper" caching layer for database queries on a 20-row table, that is only used from the administrative panel by three publishers. Understand the use cases. As cool as it may be to build a flexible and expendable XHR framework to support variable simultaneous requests; you don’t need to invest in it if the only feature that will be using it is an update to a visitors counter on one page. Understand the scope. I cannot stress it enough: a good engineer is not the one who knows how to build the most advanced system, but the one who knows when not to build that system.

Your Code May Be Elegant

Il s’agit d’un manifeste engagé qui fait état de mes réflexions actuelles sur le métier de développeur, il est amené à évoluer dans le temps :

De l’utilité plus que de la qualité,
De l’expérimentation plus que du troll,
De la convivialité plus que de l’exhaustivité,
Du partage plus que de la mise en ligne,
De l’inconfort plus que de la sécurité,
De l’économie plus que de la performance,
Du savoir-être plus que du savoir-faire.

Utilité

J’ai eu l’occasion de développer des produits de qualité. J’ai aussi eu l’occasion de développer des produits utiles. Étrange d’opposer les deux et pourtant j’ai rarement pu avoir les deux à la fois. Et rétrospectivement, ce sont clairement les produits utiles qui ont donné du sens à mon savoir-faire. Un code de (sur-)qualité n’aura pas forcément une durée de vie plus longue mais il y a de grande chances qu’il mette beaucoup plus de temps à sortir, ce qui met en péril le projet — aussi utile soit-il.

Expérimentation

Les développeurs passent (perdent ?) beaucoup trop de temps à discuter de technologies sans même les avoir essayées. Prenez React par exemple, il serait facile de troller sur le fait qu’introduire du HTML directement dans du JS est une hérésie et pourtant après quelques heures de pratiques ça devient beaucoup plus pertinent. Et je prends du fun à coder avec ; sans me préoccuper des design patterns et autres best practices, il faut savoir lâcher prise et avancer à son rythme.

Convivialité

J’ai déjà eu l’occasion d’exposer ce que j’entendais par Open-Source conviviale et mon désir de rendre l’informatique plaisante. Je suis fatigué des frameworks que l’on sort systématiquement pour publier une page statique, de ces bibliothèques qui cachent un manque de compréhension du langage et de toute la complexité dans laquelle s’enferme le développement Web sur l’autel de son industrialisation. Il faut savoir revenir à la base : du contenu, des liens et des interactions.

Partage

Le partage va plus loin que de la solidarité entre développeurs, il y a une notion d’apprentissage et de retour d’expérience. Libérer son code est une chose, expliquer ses choix d’implémentation, ses échecs et les leçons apprises est autrement plus intéressant. Github et Stack Overflow sont les fast-foods du code, je veux prendre le temps de raconter ma recette et l’enrichir avec d’autres.

Inconfort

Plus un milieu est changeant, plus il en va de sa survie d’explorer et de s’adapter rapidement ; c’est difficile à accepter mais le confort d’aujourd’hui est la mise au rebut de demain dans un domaine comme le Web. Il ne s’agit pas forcément d’aller dans le sens du courant mais d’avoir la curiosité de découvrir de nouveaux domaines et le goût d’explorer de nouveaux concepts. Sortir de sa zone de confort est ce qui rend ce métier si stimulant.

Économie

La création d’usines à gaz génère forcément un gaspillage numérique non négligeable. On construit des 4x4 numériques dont on essaye d’optimiser la consommation alors que l’on aurait besoin de simples vélos ! De bons défauts sont l’une des conditions pour obtenir un produit minimaliste qui soit pertinent mais ne suffisent pas, il faut avoir une évolution culturelle et une démarche de co-création.

Savoir-être

Enfin, je n’ai pas envie de travailler avec un gourou, un ninja ou une rockstar. J’ai envie de collaborer avec quelqu’un à l’écoute, qui sait faire preuve de diplomatie dans sa critique, qui arrive à me faire douter sans forcément passer par la confrontation. La sur-compétence technique est contre-productive à toute forme d’échanges car elle crée un déséquilibre malsain.

Est-ce que ce manifeste fait pour autant l’apologie du code-poubelle codé à la RACHE™ ? Je ne pense pas. Il s’agit avant tout de revenir à un développement responsable qui donne du sens à notre métier et aux relations que l’on peut avoir avec nos pairs.

[afpyro] AFPyro à Paris, Mercredi 19 février

$
0
0

Contexte

C’est le premier AFPyro parisien de 2014 !

Quand ?

Le Mercredi 19 février à partir de 19h.

Où ?

La Grosse Caisse au 160 rue Montmartre, dans le 2ème arrondissement de Paris.

Voyez la carte OpenStreetMap

[afpyro] AFPyro à Lyon - mercredi 26 février

$
0
0

Un Afpyro aura lieu le mercredi 26 février à partir de 20h à l’Antre Autre - 11 rue Terme - 69001 Lyon.

Une présentation sera donnée sur la réalisation de tests unitaires dans l’écosystème Python.

L’Antre Autre est un lieu où nous pouvons discuter autour d’un verre, et, pour ceux qui le souhaitent, prendre un repas.

Pour se rendre à l’Antre Autre :

  • en métro : arrêt Hôtel de Ville
  • en bus : lignes C13 et C18 arrêt Mairie du 1er ou lignes 19, C14 et C3 à l’arrêt Terreaux
  • en vélo’v : stations Place Sathonay, Carmélites Burdeau, Place de la paix

[logilab] FOSDEM PGDay 2014

$
0
0

I attended PGDay on January 31st, in Brussels. This event was held just before FOSDEM, which I also attend (expect another blog post). Here are some of the notes I took during the conference.

https://fosdem.org/2014/support/promote/wide.png

Statistics in PostgreSQL, Heikki Linnakangas

Due to transit delays, I only caught the last half of that talk.

The main goal of this talk was to explain some of Postgres' per-column statistics. In a nutshell, Postgres needs to have some idea about tables' content in order to choose an appropriate query plan.

Heikki explained which sorts of statistics gathers, such as most common values and histograms. Another interesting stat is the correlation between physical pages and data ordering (see CLUSTER).

Column statistics are gathered when running ANALYZE and stored in the pg_statistic system catalog. The pg_stats view provides a human-readable version of these stats.

Heikki also explained how to determine whether performance issues are due to out-of-date statistics or not. As it turns out, EXPLAIN ANALYZE shows for each step of the query planner how many rows it expects to process and how many it actually processed. The rule of thumb is that similar values (no more than an order of magnitude apart) mean that column statistics are doing their job. A wider margin between expected and actual rows mean that statistics are possibly preventing the query planner from picking a more optimized plan.

It was noted though that statistics-related performance issues often happen on tables with very frequent modifications. Running ANALYZE manually or increasing the frequency of the automatic ANALYZE may help in those situations.

Advanced Extension Use Cases, Dimitri Fontaine

Dimitri explained with very simple cases the use of some of Postgres' lesser-known extensions and the overall extension mechanism.

Here's a grocery-list of the extensions and types he introduced:

  • intarray extension, which adds operators and functions to the standard ARRAY type, specifically tailored for arrays of integers,
  • the standard POINT type which provides basic 2D flat-earth geometry,
  • the cube extension that can represent N-dimensional points and volumes,
  • the earthdistance extension that builds on cube to provide distance functions on a sphere-shaped Earth (a close-enough approximation for many uses),
  • the pg_trgm extension which provides text similarity functions based on trigram matching (a much simpler thus faster alternative to Levenshtein distances), especially useful for "typo-resistant" auto-completion suggestions,
  • the hstore extension which provides a simple-but-efficient key value store that has everyone talking in the Postgres world (it's touted as the NoSQL killer),
  • the hll extensions which implements the HyperLogLog algorithm which seems very well suited to storing and counting unique visitor on a web site, for example.

An all-around great talk with simple but meaningful examples.

http://tapoueh.org/images/fosdem_2014.jpg

Integrated cache invalidation for better hit ratios, Magnus Hagander

What Magnus presented almost amounted to a tutorial on caching strategies for busy web sites. He went through simple examples, using the ubiquitous Django framework for the web view part and Varnish for the HTTP caching part.

The whole talk revolved around adding private (X-prefixed) HTTP headers in replies containing one or more "entity IDs" so that Varnish's cache can be purged whenever said entities change. The hard problem lies in how and when to call PURGE on Varnish.

The obvious solution is to override Django's save() method on Model-derived objects. One can then use httplib (or better yet requests) to purge the cache. This solution can be slightly improved by using Django's signal mechanism instead, which sound an awful-lot like CubicWeb's hooks.

The problem with the above solution is that any DB modification not going through Django (and they will happen) will not invalidate the cached pages. So Magnus then presented how to write the same cache-invalidating code in PL/Python in triggers.

While this does solve that last issue, it introduces synchronous HTTP calls in the DB, killing write performance completely (or killing it completely if the HTTP calls fail). So to fix those problems, while introducing limited latency, is to use SkyTools' PgQ, a simple message queue based on Postgres. Moving the HTTP calls outside of the main database and into a Consumer (a class provided by PgQ's python bindings) makes the cache-invalidating trigger asynchronous, reducing write overhead.

http://www.logilab.org/file/210615/raw/varnish_django_postgresql.png

A clear, concise and useful talk for any developer in charge of high-traffic web sites or applications.

SkyTools: http://wiki.postgresql.org/wiki/SkyTools

The Worst Day of Your Life, Christophe Pettus

Christophe humorously went back to that dreadful day in the collective Postgres memory: the release of 9.3.1 and the streaming replication chaos.

My overall impression of the talk: Thank $DIETY I'm not a DBA!

But Christophe also gave some valuable advice, even for non-DBAs:

  • Provision 3 times the necessary disk space, in case you need to pg_dump or otherwise do a snapshot of your currently running database,
  • Do backups and test them:
    • give them to developers,
    • use them for analytics,
    • test the restore, make it foolproof, try to automate it,
  • basic Postgres hygiene:
    • fsync = on (on by default, DON'T TURN IT OFF, there are better ways)
    • full_page_writes = on (on by default, don't turn it off)
    • deploy minor versions as soon as possible,
    • plan upgrade strategies before EOL,
    • 9.3+ checksums (createdb option, performance cost is minimal),
    • application-level consistency checks (don't wait for auto vacuum to "discover" consistency errors).

Materialised views now and in the future, Thom Brown

Thom presented on of the new features of Postgres 9.3, materialized views.

In a nutshell, materialized views (MV) are read-only snapshots of queried data that's stored on disk, mostly for performance reasons. An interesting feature of materilized views is that they can have indexes, just like regular tables.

The REFRESH MATERIALIZED VIEW command can be used to update an MV: it will simply run the original query again and store the new results.

There are a number of caveats with the current implementation of MVs:

  • pg_dump never saves the data, only the query used to build it,
  • REFRESH requires an exclusive lock,
  • due to implementation details (frozen rows or pages IIRC), MVs may exhibit non-concurrent behavior with other running transactions.

Looking towards 9.4 and beyond, here are some of the upcoming MV features:

  • 9.4 adds the CONCURRENTLY keyword:
    • + no longer needs an exclusive lock, doesn't block reads
    • - requires a unique index
    • - may require VACUUM
  • roadmap (no guarantees):
    • unlogged (disables the WAL),
    • incremental refresh,
    • lazy automatic refresh,
    • planner awareness of MVs (would use MVs as cache/index).

Indexes: The neglected performance all-rounder, Markus Winand

http://use-the-index-luke.com/img/alchemie.png

Markus' goal with this talk showed that very few people in the SQL world actually know - let alone really care - about indexes. According to his own experience and that of others (even with competing RDBMS), poorly written SQL is still a leading cause of production downtime (he puts the number at around 50% of downtime though others he quoted put that number higher). SQL queries can indeed put such stress on DB systems and cause them to fail.

One major issue, he argues, is poorly designed indexes. He went back in time to explain possible reasons for the lack of knowledge about indexes with both SQL developers and DBAs. One such reason may be that indexes are not part of the SQL standard and are left as implementation-specific details. Thus many books about SQL barely cover indexes, if at all.

He then took us through a simple quiz he wrote on the topic, with only 5 questions. The questions and explanations were very insightful and I must admit my knowledge of indexes was not up to par. I think everyone in the room got his message loud and clear: indexes are part of the schema, devs should care about them too.

Try out the test : http://use-the-index-luke.com/3-minute-test

PostgreSQL - Community meets Business, Michael Meskes

For the last talk of the day, Michael went back to the history of the Postgres project and its community. Unlike other IT domains such as email, HTTP servers or even operating systems, RDBMS are still largely dominated by proprietary vendors such as Oracle, IBM and Microsoft. He argues that the reasons are not technical: from a developer stand point, Postgres has all the features of the leading RDMBS (and many more) and the few missing administrative features related to scalability are being addressed.

Instead, he argues decision makers inside companies don't yet fully trust Postgres due to its (perceived) lack of corporate backers.

He went on to suggest ways to overcome those perceptions, for example with an "official" Postgres certification program.

A motivational talk for the Postgres community.

http://fosdem2014.pgconf.eu/files/img/frontrotate/slonik.jpg

[tshirtman] Using tex_coords in kivy for fun and profit

$
0
0

Your gpu is an awesome thing, it can do all kind of calculations, very fast. For example, instead of telling it exactly how you want each pixels to be, you can throw textures and vertices at it, and have it correctly deduce how this things will have to look.

To to this, though, you must tell it what part of the texture will be stuck to each vertice. This is usually denoted using texture coordinates.

Texture coordinates, like usual coordinates, indicate the place of something. Instead of noting them x and y, they are often called u and v, which allows to clearly note what parts of an equation relate to each.

While kivy offers you high level canvas instructions, it gives you a pretty good access to lower level features, and you can actually manipulate the texture coordinates of your Rectangle instructions. This allow for cheap zooming, repeat-scrolling, and other image manipulations, since your gpu is doing all the actual work.

tex_coords = u, v, u + w, v, u + w, v + h, u, v + h

which is better understood as:

tex_coords = [
    u,     v,
    u + w, v,
    v + w, v + h,
    u,     v + h
]

considering a default tex_coords value, where u and v = 0, and w and h = 1 (all values are relatives, so usually between 0 and 1).

u, v + h-------u + w, v + h
|              |
u, v-----------u + w, v

which means the 4 angles of your texture, will be on the 4 angles of your rectangle, how dull!

One way to play with different values, is to create a little app showing you the effect of deformations.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty
from kivy.core.image import Image as CoreImage

kv = '''
#:import chain itertools.chain
RootWidget:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: root.pos
            size: root.size
            texture: app.texture
            # here is our usage of the calculated texture coordinates
            # we devide by 100 because we took the input with a 100x100
            # rectangle as default value
            tex_coords: [x / 100. for x in chain(*root.points)]

        PushMatrix
        # translate the rectangle to make it easier to play with
        Translate:
            xy: root.width / 2, root.height / 2

        Color:
            rgba: 1, 0, 0, .5
        Line:
            points: chain(*root.points + root.points[:1])
            width: 2
        PopMatrix
'''


def dist(point, pos):
    return ((point[0] - pos[0]) ** 2 + (point[1] - pos[1]) ** 2)
    # ** .5 # who cares about square root here? it doesn't change the order


class RootWidget(Widget):
    # the default values, a 100x100 square, displayed around the middle of the screen
    points = ListProperty([[0, 0], [100, 0], [100, 100], [0, 100]])

    def on_touch_down(self, touch, *args):
        # compensate the translation done in canvas
        pos = touch.pos[0] - self.width / 2, touch.pos[1] - self.height / 2

        touch.ud['point'] = min(
            range(4), key=lambda x: dist(self.points[x], pos))

    def on_touch_move(self, touch, *args):
        # compensate the translation done in canvas
        pos = touch.pos[0] - self.width / 2, touch.pos[1] - self.height / 2
        # update the point
        self.points[touch.ud['point']] = pos


class TexCoordsApp(App):
    texture = ObjectProperty(None)

    def build(self):
        self.root = Builder.load_string(kv)
        self.texture = CoreImage.load(
            'GrassGreenTexture0002.jpg').texture
        self.texture.wrap = 'repeat'
        return self.root


if __name__ == '__main__':
    TexCoordsApp().run()

The texture have been scrapped on the web and is not very interesting, but you can find it here.

Of course, this is much more a learning device (helful because these transformation are not quite straighforward for our brains) than a practical application, but a lot more can be done.

Here is, for example, a little infinite scroll app uting this concept.

[tarek] Running On It

$
0
0

I was reading @ednapiranha blog about her tips on running and recall that I wanted to write something about running in my blog for quite some time now.

Don't worry, this is not going to be yet another lose-some-weight improve-your-health post like the millions you'd find on the internet if you search for running. I won't distill tips or hints on how to run either - the topic is covered at 150% on the internet. (That said, if you want to talk about this topic, I can speak about it for hours.)

It's just my story on how running became a fundamental activity in my life - and I think some people that are in the Mozilla & Python community can relate to what happened to me.

Lost body

Last summer I was in San Francisco for some work at our Mozilla headquarters and as usual I was enjoying the city in the evenings with some friends - trying to taste all the good things to eat and drink this part of the world has to offer.

I have to say the beers are marvelous around the bay area, but after (quite) a few of them my nose started to bleed. I was not that worried, San Franscico can be quite dry sometime, and with all the plane travelling I suppose the nose skin gets very sensitive.

But it bled for 2 days non-stop - no matter how many tissue I was sticking in it. At some point I really freaked out and went to the hospital to check on it. I thought I had some vein issue and I really thought I was dying somehow, my body felt like it was simply going to stop working and my heart stop at any point.

I sat in the waiting room for over 4 hours in the evening and was seated with some homeless people and some other people that were in a very bad shape. One guy was in some kind of drug delirium and scared the crap out of me.

One sad thing I've notice was that most of the homeless that were seated in the waiting room were there just to be seated in a warm place for a while. At some point a couple of people from some kind of organization came to pick all of them to try to bring them to a shelter or something. But you could see they wanted to stay in that hospital waiting room rather than going to another place.

After 4 hours I saw a doctor and that lasted for around 5mn. It was just a typical nose bleeding that happens when the air is very dry. And drinking alcoholic beverages will expand your veins, making the bleeding harder to stop.

When I got back home after a few days I was still a bit shocked by this event: I realized I had lost my body over the years.

My body was not something I owned anymore. It just became a transport for all the food and drinks I wanted my brain to taste. It was just a tool to walk from point A to point B. The hardware to type on a keyboard and look at a screen from most of my days.

I realized that my body had limits I was not really worried about. It was trying to cope with my lifestyle excesses.

Running

After that incident I gave a break to my body for a few months.

No more drinking, healthier food, more sleep etc. I felt great of course, but knew this would not last for ever - I don't want to live like a monk and I want to enjoy most things in life.

I started to run around that time. I started to run every other day and it became obvious that running would help me have a more balanced life. Some weeks I run every day.

When I run, my body becomes a first class citizen. It gets all my attention and I rarely listen to some music. I just listen to my breathing and focus on how my legs and arms move. After a while I am able to reach a state where I don't think about anything else than my run.

Reaching this state takes at least 20 minutes. It turns out that's usually the warm up time needed before a race or a practice. After 20 minutes, your body has fully reached its abilities to run and is ready to sustain a long effort. There are a lot of studies that explain how the body switches to more sustainable energy sources after a while and what chemicals are generated by your brain so you can cope with the run. It's quite fascinating.

Running also became the best place for me to solve complex coding or design problems I can have at work. I guess it's mostly because when you run you can't get distracted by all the things that are distracting us on a screen - that forces us to do a lot of context switching.

Being a remote worker, I now organize my working day around my running sessions - if I am banging my head on a tough issue I am now running on it :)

There's one interesting read about all of this: Murakami's essay on running What I Talk About When I Talk About Running. I can relate to a lot of things he wrote there.

I don't think I am going to stop running anytime soon, I have never felt that way before: balanced.

https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Maslow%27s_Hierarchy_of_Needs.svg/450px-Maslow%27s_Hierarchy_of_Needs.svg.png

It looks like I was doing the Maslow hierarchy of needs a little bit up-side-down until now ;)

[Biologeek] Sport et plaisir

$
0
0

C’est devenu peu à peu une course à la performance et les seules questions que je me posais étaient mais pourquoi tu te traines autant, allez encore quelques kilomètres, etc. Je me suis mis à souffrir, je me suis blessé, et mon corps a fini par dire stop.

Je n’ai plus couru depuis des mois, et mon bracelet connecté m’indique que je ne pratique plus assez d’activité physique, mon corps se fige peu à peu. Je vais reprendre progressivement la marche et la course, mais uniquement pour le plaisir, pour me sentir vivant et sans me poser de questions.

Commentaire au sujet des pensées de coureur, Franck Taillandier

Mon rapport avec le sport a beaucoup évolué au cours de cette dernière année — notamment avec la préparation pour le Mont Blanc — lorsque je me suis rendu compte qu’au-delà de l’affûtage musculaire, l’entraînement intensif était surtout un moyen d’apprendre à gérer sa douleur. L’identifier, la réduire, l’accepter, l’oublier. Le mois qui a suivi l’arrivée au sommet a d’ailleurs été assez difficile à gérer : perte de motivation et manque d’énergie alors que mon corps était à bloc de globules rouges. Quand le mental prend le pas sur le physique.

J’ai pallié cette baisse de régime par une diversification en faisant du vélo et de l’escalade, découvrant de nouvelles pratiques, de nouvelles communautés aussi. En essayant d’être un peu plus complet en espérant accéder à d’autres courses plus techniques. Le trail est devenu une base pour gambader en nature, sans montre, sans sac, sans eau, libre. À nouveau.

Ces régions non habitées de notre planète ont un point commun : la nature y est grandiose et émouvante. Peut-être parce qu’à force de vivre dans un monde où l’Homme maîtrise tout, il est bon de se sentir à nouveau vulnérables et insignifiants face à un horizon libre de toute trace humaine. Ou au contraire, parce qu’à trop être dilué dans une société où l’individu ne contrôle plus rien à part ce qu’il achète au supermarché, le voyageur nature aime de temps en temps se sentir capable de vivre avec le minimum de technologie et de redevenir ainsi seul maître de son existence.

Edito l’autonomie longue durée - Carnets d’Aventures n°34, Coralie Le Rasle et Alexis Loireau

Et puis il y a eu l’arrivée d’Alexandre et même en essayant de continuer à prendre du plaisir, le muscle a fondu, s’est très vite transformé en graisse. J’ai redécouvert d’anciennes sensations : des muscles et tendons qui avaient su se faire oublier dans l’effort, dans leur routine quotidienne. Il est presque plaisant de se remettre à sentir des douleurs enfouies, de redevenir animal. D’avoir oublié accidentellement la part d’humanité qui me faisait supporter cet inconfort sans m’en rendre compte. De prendre du recul sur ce que ça m’apporte en contrepartie.

Aujourd’hui, je prends du plaisir à être en capacité de porter mon fils en écharpe pour une randonnée de plusieurs heures et à lui faire découvrir le monde à pied, un pas après l’autre. Je prends du plaisir aussi à faire des sorties sportives sans trop me poser de questions sur l’effort à produire car j’ai acquis des techniques qui me permettent d’être serein. Je prends du plaisir enfin à atteindre le flow sportif rapidement, celui où l’esthétique dépasse l’effort, où la méditation prend le pas sur la confusion.


[Biologeek] Naissance de la peur

$
0
0

Qui ne s’est, un jour, interrogé sur la vie ?
Qui ne s’est, une fois, demandé ce qu’elle est ?
Questions trop ambitieuses.
Et sans réponses.
Mais à qui, plus modestement, demande :
« Où la vie commence-t-elle ? Et quand ? »
Une réponse vient, immédiate, aussi simple qu’évidente :
« La vie commence à la naissance. »
Et toute inquiétude disparait.
Évidence ?
La vie commence à la naissance…
Vraiment ?
Dans le ventre…
Dans le ventre de sa mère, l’enfant n’est-il pas déjà vivant ? Ne bouge-t-il pas ?
Sans doute, il bouge. Mais il n’y a là, disent certains, que simple activité réflexe.
Activité réflexe ! Non !
Nous savons aujourd’hui, que, bien avant d’avoir « vu le jour », l’enfant perçoit de la lumière. Et qu’il entend. Et que, de son obscure retraite, il est à l’écoute du monde.
Nous savons même qu’il passe de la veille au sommeil. Et même qu’il rêve !
En sorte que, faire commencer la vie à la naissance, c’est se tromper grossièrement.
Qu’est-ce donc, alors, qui commence quand l’enfant vient au monde ? Qu’est-ce donc si ce n’est la vie ?

Ce qui commence
c’est la peur.
La peur et l’enfant
naissent ensemble.
Et ne se quitteront jamais.
La peur,
compagne secrète,
discrète comme l’ombre
et, comme elle, fidèle, obstinée.
La peur
qui ne nous lâchera
qu’à la tombe
où fidèlement
elle nous aura mené.

Shantala, un art traditionnel, le massage des enfants, Frédérick Leboyer (1976)

Comment l’aider à apprivoiser cette peur ? Lui apprendre à vivre avec, se laisser traverser par la peur dirait Franck Herbert pour qu’il puisse être lui. Rien que lui.

Ces réflexions m’amènent à penser à mes propres peurs, celles qui font mon quotidien et celles qui font mon histoire. De quelles peurs demain sera-t-il fait ? Et peut-on d’ailleurs se préparer à la peur ? Faut-il partager ses peurs pour les diluer ou se propagent-elles pleinement d’un individu aux autres telles des virus ?

Comment le rassurer sans être soi-même rassuré ? Comment lui transmettre optimisme et espoir dans un monde morose et laid ?

Dehors, dedans…
Voilà le monde coupé en deux.
Dedans, la faim.
Dehors, le lait.
L’espace
est né.

Dedans, la faim
dehors, le lait.
Et, entre eux deux
l’absence,
l’attente
qui est indicible souffrance.
Et qui s’appelle
le temps.

Et c’est ainsi
que, simplement
avec l’appétit
sont nés
l’espace
et la durée.

Ibid.

Après l’espace et la durée, il me reste à faire naître en lui le choix. La notion complexe du libre arbitre qui fait de nous des humains. Construisant du beau dans la confiance, créant de la joie dans la spontanéité. Une feuille d’arbre allant s’ajouter à d’autres pour former un tout, une feuille blanche qui s’écrit au présent dans la magie du quotidien. Lui apprendre à utiliser sa peur comme moteur de son émerveillement.

[afpy.org] 1er Python Meetup le 9 avril à la Cantine de Nantes

$
0
0
GrapheekDB une base de données graph rapide !

[logilab] A quick take on continuous integration services for Bitbucket

$
0
0

Some time ago, we moved Pylint from this forge to Bitbucket (more on this here).

https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2012/Oct/11/master-logo-2562750429-5_avatar.png

Since then, I somewhat continued to use the continuous integration (CI) service we provide on logilab.org to run tests on new commits, and to do the release job (publish a tarball on pypi, on our web site, build Debian and Ubuntu packages, etc.). This is fine, but not really handy since the logilab.org's CI service is not designed to be used for projects hosted elsewhere. Also I wanted to see what others have to offer, so I decided to find a public CI service to host Pylint and Astroid automatic tests at least.

Here are the results of my first swing at it. If you have others suggestions, some configuration proposal or whatever, please comment.

First, here are the ones I didn't test along with why:

The first one I actually tested, also the first one to show up when looking for "bitbucket continuous integration" on Google is https://drone.io. The UI is really simple, I was able to set up tests for Pylint in a matter of minutes: https://drone.io/bitbucket.org/logilab/pylint. Tests are automatically launched when a new commit is pushed to Pylint's Bitbucket repository and that setup was done automatically.

Trying to push Drone.io further, one missing feature is the ability to have different settings for my project, e.g. to launch tests on all the python flavor officially supported by Pylint (2.5, 2.6, 2.7, 3.2, 3.3, pypy, jython, etc.). Last but not least, the missing killer feature I want is the ability to launch tests on top of Pull Requests, which travis-ci supports.

Then I gave http://wercker.com a shot, but got stuck at the Bitbucket repository selection screen: none were displayed. Maybe because I don't own Pylint's repository, I'm only part of the admin/dev team? Anyway, wercker seems appealing too, though the configuration using yaml looks a bit more complicated than drone.io's, but as I was not able to test it further, there's not much else to say.

http://wercker.com/images/logo_header.png

So for now the winner is https://drone.io, but the first one allowing me to test on several Python versions and to launch tests on pull requests will be the definitive winner! Bonus points for automating the release process and checking test coverage on pull requests as well.

https://drone.io/drone3000/images/alien-zap-header.png

[hautefeuille] Traitement d’un fichier de log Apache et génération de svg avec Python

$
0
0

Objectif

Traiter un fichier de log Apache d’un hôte virtuel puis générer un graphique synthétique des visiteurs uniques à travers le monde.

Principes

On traite le fichier, on génère des listes de données, on géolocalise les adresses ip rencontrées puis on génère un graphique svg.

L’utilisation de ce script nécessite l’installation des modules Python suivants :

  • clize pour la gestion des arguments
  • geoip pour la géolocalisation
  • pygal pour la génération SVG.

Le script Python

Le script est largement adaptable à diverses situations. La documentation de Pygal est claire pour permettre une personnalisation aisée du graphique.

On utilise le script de cette manière :

python grabip.py log.txt

Le script, dans son état actuel, ne traite que les adresses ip, donc peu importe le formatage du fichier de log par le fichier de configuration d’Apache.

Le script produit un fichier rapport.txt et un fichier chart.svg.

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

from __future__ import print_function

"""
Usage: grabip.py [OPTIONS] filein

"""

import re
import collections

import GeoIP
import clize

import pygal
from pygal.style import NeonStyle

class Inspector(object):
    '''
    Extract IP from log file, count ip, localise with GeoIP country
    '''
    def __init__(self, in_file, **kwds):
        super(Inspector, self).__init__(**kwds)
        self.in_file = open(in_file, 'r')
        self.exclude = ["0.0.0.0", "127.0.0.1"]
        self.ip = []
        self.cnt = collections.Counter()
        self.cnt_u = collections.Counter()
        self.result = []
        self.hits = {}
        self.uniques = {}
        self.total = 0
        self.unique = 0
        self.killdoublon = set()
        self.geoip = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)

        self.filter()
        self.count()
        self.geo()
        self.hitbycountry()
        self.totalhits()
        self.totalunique()
        self.uniquebycountry()
        self.in_file.close()

    def filter(self):
        '''
        Strip line and regex for ip

        self.ip return ['78.46.16.11', '78.46.16.11']
        '''
        for line in self.in_file:
            ipp = re.search("([\d]+\.[\d]+\.[\d]+\.[\d]+)",
                            line.rstrip('\n\r'))
            if ipp and ipp not in self.exclude:
                self.ip.append(ipp.group())

        print("=> filter : %s" % self.ip)

    def count(self):
        '''
        Add a count for all ip found

        self.cnt.items() return [('78.46.16.11', 2)]
        '''
        for ip in self.ip:
            self.cnt[ip] += 1

        print("=> count : %s" % self.cnt.items())

    def geo(self):
        '''
        Add goip information to dataset

        self.result return [('78.46.16.11', 2, 'DE')]
        '''
        for ip, i in self.cnt.items():
            country = self.geoip.country_code_by_addr(ip)
            if country:
                self.result.append((ip, i, country))

        print("=> geo : %s" % self.result)

    def hitbycountry(self):
        '''
        Count hit by country

        self.hits return {'DE': 2}
        '''
        for ip, hit, country in self.result:
            if country in self.hits:
                tmp = int(self.hits.get(country)) + int(hit)
                self.hits[country] = tmp
            else:
                self.hits[country] = hit

        print("=> hits : %s" % self.hits)

    def uniquebycountry(self):
        '''
        Count unique by country
        '''
        for ip in self.killdoublon:
            country = self.geoip.country_code_by_addr(ip)
            if country:
                self.cnt_u[country] += 1
        self.uniques = dict(self.cnt_u)

        print("=> uniques : %s" % self.uniques)

    def totalhits(self):
        '''
        Return total hits

        self.total return 2
        '''
        for value in self.hits.itervalues():
            self.total += value

        print("=> total hits : %s" % self.total)

    def totalunique(self):
        '''
        Return total unique visitor

        self.unique return 1
        '''
        for elem in self.ip:
            self.killdoublon.add(elem)
        self.unique = len(self.killdoublon)

        print("=> total unique : %s" % self.unique)

class Reporting(Inspector):
    '''
    Create a report file text
    '''
    def __init__(self, in_file, **kwds):
        super(Reporting, self).__init__(in_file, **kwds)
        self.out_file = open("rapport.txt", 'w')
        self.simplereport()
        self.out_file.close()

    def simplereport(self):
        '''
        Print result and write file report
        '''
        for ip, i, country in self.result:
            self.out_file.write(ip + ' ' + str(i) + ' ' + country + '\n')

        print("=> Report done")

class Drawing(Reporting):
    '''
    Create visual representation

    '''
    def __init__(self, in_file, **kwds):
        super(Drawing, self).__init__(in_file, **kwds)
        chart = pygal.HorizontalBar(
            width=800,
            height=600,
            x_title="Visites",
            y_title="Pays",
            legend_at_bottom=True,
            tooltip_border_radius=10,
            human_readable=True,
            no_data_text='No result found',
            pretty_print=True,
            style=NeonStyle)
        chart.title = u"Visiteurs uniques à travers le monde sur\
        le site hautefeuille.eu en 2013"
        for k, v in self.uniques.iteritems():
            if v >= 50:
                chart.add(k, [{'value': v, 'label': k}])
        chart.render_to_file('chart.svg')

@clize.clize()
def main(filein):
    '''
    Main
    '''
    grab = Drawing(filein)

if __name__ == '__main__':
    clize.run(main)

Remarque : le script ne comporte pas de difficulté particulière. Ce qui est peut-être méconnu : l’utilisation des compteurs d’éléments (collections.Counter()) et l’utilisation des set() qui permettent facilement d’obtenir une liste sans doublon.

Le fichier svg produit

La version interactive en SVG.

[tshirtman] Kpritz

$
0
0

Just because it seemed not to hard, i spent the evening making a Spritz clone using kivy.

Code is a bit crude and all, but here it is for your enjoyment:

https://gist.github.com/9316552

Here is an apk if you want to actually try it.

Only flat text files are supported, opening the option window is a bit slow (because of ColorPickers), but it’s functionnal, progression is saved, all the options i though were needed are there, and it seems usable.

You can find a lot of free book in the .txt format over at project gutenberg.

Viewing all 3409 articles
Browse latest View live