{"id":1023,"date":"2010-10-30T15:00:00","date_gmt":"2010-10-30T13:00:00","guid":{"rendered":"http:\/\/j-mad.com\/blog\/?p=1023"},"modified":"2010-10-30T15:00:00","modified_gmt":"2010-10-30T13:00:00","slug":"django-qsstats-parce-quen-vraion-adore-tous-faire-des-stats","status":"publish","type":"post","link":"https:\/\/j-mad.com\/blog\/2010\/10\/30\/django-qsstats-parce-quen-vraion-adore-tous-faire-des-stats\/","title":{"rendered":"Django-qsstats, parce qu&#8217;en vrai,on adore tous faire des stats."},"content":{"rendered":"<p>Ce mois-ci, j&#8217;aurais pu \u00e9crire plusieurs django-app du mois. Si j&#8217;avais eu le temps. Mais bon, le jour o\u00f9 j&#8217;aurais le temps, \u00e7a se saura. Du coup, alors que l&#8217;horloge tourne et qu&#8217;il ne me reste plus qu&#8217;un jour et demi pour \u00e9crire ma django app du mois et mon polargeek, j&#8217;ai d\u00e9cid\u00e9 d&#8217;\u00e9crire ma django-app du mois pour parler de <a href=\"http:\/\/github.com\/mcroydon\/django-qsstats\">django-qsstats<\/a>, une petite application comme je les aime.<\/p>\n<p>C&#8217;est \u00e0 dire, qui ne fait qu&#8217;une chose, une toute petite chose, mais qui la fait bien. Et qui du coup est facilement int\u00e9grable, sans remord.<\/p>\n<h1>1- O\u00f9 on le trouve, comment on l\u2019installe, tout \u00e7a quoi (et la doc)\u00a0?<\/h1>\n<p>On la trouve sur sa page <a href=\"http:\/\/github.com\/mcroydon\/django-qsstats\">github<\/a>. Il me semble bien que le seul moyen d&#8217;installation soit \u00e0 travers github. Mais bon, cloner un repository git hub, ce n&#8217;est pas le plus compliqu\u00e9.<\/p>\n<p>La doc, elle, se limite au fichier <a href=\"http:\/\/github.com\/mcroydon\/django-qsstats\/blob\/master\/README.rst\">Readme<\/a> et \u00e0 la lecture des tests. Mais bon, honn\u00eatement, cela suffit.<\/p>\n<p>Par contre attention, en plus de n\u00e9cessiter django 1.1 \u00e0 minimal, cette petite app a besoin de <a href=\"http:\/\/labix.org\/python-dateutil\">python-dateutil<\/a> (une petite librairie qui ajoute pas mal d&#8217;extension plut\u00f4t puissante \u00e0 datetime, si vous ne connaissez pas, je vous recommande vivement d&#8217;aller jeter un oeil (mais pas les deux, apr\u00e8s vous ne pourrez plus lire la doc \u2026 ) )<\/p>\n<h2>2- Mais au fait, \u00e0 quoi \u00e7a sert\u00a0?<\/h2>\n<p>A presque rien, mais c&#8217;est bien utile.:) Lorsqu&#8217;on veut faire un minimum de stats, de comparaison ou autre, on se retrouve assez vite \u00e0 vouloir trouver combien il y a eu de nouveaux articles la semaine derni\u00e8re, combien d&#8217;histoires ont \u00e9t\u00e9 \u00e9crites par des r\u00f4listes durant le mois dernier, ou d&#8217;utilisateur enregistr\u00e9 les 6 derniers mois&#8230;<\/p>\n<p>Faire cela est tr\u00e8s simple, un petit queryset puis des petits filters. C&#8217;est tr\u00e8s simple, mais bon, c&#8217;est rapidement chiant, et puis c&#8217;est pas tr\u00e8s DRY.<\/p>\n<p>Donc on se retrouve assez vite \u00e0 faire des petits fonctions pour avoir les dates du mois en cours, ou d&#8217;un mois pr\u00e9cis..<\/p>\n<p>O\u00f9 alors on se dit que quelqu&#8217;un \u00e0 surement du le faire \u00e0 notre place, on cherche un peu et on tombe sur django-qsstats.<\/p>\n<h1>3- Comment \u00e7a marche\u00a0?<\/h1>\n<p>C&#8217;est vraiment tout simple. Il suffit de mettre l&#8217;app dans son python-path. Ensuite un peu l&#8217;utiliser. Rien \u00e0 faire de plus. M\u00eame pas de syncb vu que c&#8217;est une app sans model. (il y a un fichier models mais il n&#8217;est l\u00e0 que pour les tests).<br \/>\nEnsuite, ben un exemple sera plus parlant (je reprend l&#8217;exemple du site officiel) \u00a0:<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">from<\/span> django.<span class=\"me1\">contrib<\/span>.<span class=\"me1\">auth<\/span> <span class=\"kw1\">import<\/span> User<br \/>\n<span class=\"kw1\">import<\/span> qsstats<br \/>\n<br \/>\nqs <span class=\"sy0\">=<\/span> User.<span class=\"me1\">objects<\/span>.<span class=\"kw2\">all<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\nqss <span class=\"sy0\">=<\/span> qsstats.<span class=\"me1\">QuerySetStats<\/span><span class=\"br0\">&#40;<\/span>qs<span class=\"sy0\">,<\/span> <span class=\"st0\">'date_joined'<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<br \/>\n<span class=\"kw1\">print<\/span> <span class=\"st0\">'%s new accounts today.'<\/span> % qss.<span class=\"me1\">this_day<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw1\">print<\/span> <span class=\"st0\">'%s new accounts this month.'<\/span> % qss.<span class=\"me1\">this_month<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw1\">print<\/span> <span class=\"st0\">'%s new accounts this year.'<\/span> % qss.<span class=\"me1\">this_year<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw1\">print<\/span> <span class=\"st0\">'%s new accounts until now.'<\/span> % qss.<span class=\"me1\">until_now<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><\/div><\/div>\n<p>Vous le voyez, c&#8217;est assez facile. On construit son queryset et ensuite on le passe \u00e0 qsstats en lui indiquant le champ date qui devra servir au filtrage.<\/p>\n<p>D\u00e9j\u00e0 l\u00e0, \u00e7a serait pas mal. Mais il y a mieux. Il existe la fonction time_series qui permet de r\u00e9cup\u00e9rer une liste de valeur. Par exemple\u00a0:<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">from<\/span> django.<span class=\"me1\">contrib<\/span>.<span class=\"me1\">auth<\/span> <span class=\"kw1\">import<\/span> User<br \/>\n<span class=\"kw1\">import<\/span> <span class=\"kw3\">datetime<\/span><span class=\"sy0\">,<\/span> qsstats<br \/>\n<br \/>\nqs <span class=\"sy0\">=<\/span> User.<span class=\"me1\">objects<\/span>.<span class=\"kw2\">all<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\nqss <span class=\"sy0\">=<\/span> qsstats.<span class=\"me1\">QuerySetStats<\/span><span class=\"br0\">&#40;<\/span>qs<span class=\"sy0\">,<\/span> <span class=\"st0\">'date_joined'<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<br \/>\ntoday <span class=\"sy0\">=<\/span> <span class=\"kw3\">datetime<\/span>.<span class=\"me1\">date<\/span>.<span class=\"me1\">today<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\nseven_days_ago <span class=\"sy0\">=<\/span> today - <span class=\"kw3\">datetime<\/span>.<span class=\"me1\">timedelta<\/span><span class=\"br0\">&#40;<\/span>days<span class=\"sy0\">=<\/span><span class=\"nu0\">7<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<br \/>\ntime_series <span class=\"sy0\">=<\/span> qss.<span class=\"me1\">time_series<\/span><span class=\"br0\">&#40;<\/span>seven_days_ago<span class=\"sy0\">,<\/span> today<span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw1\">print<\/span> <span class=\"st0\">'New users in the last 7 days: %s'<\/span> % <span class=\"br0\">&#91;<\/span>t<span class=\"br0\">&#91;<\/span><span class=\"nu0\">1<\/span><span class=\"br0\">&#93;<\/span> <span class=\"kw1\">for<\/span> t <span class=\"kw1\">in<\/span> time_series<span class=\"br0\">&#93;<\/span><\/div><\/div>\n<p>qui donnera\u00a0:<br \/>\nNew users in the last 7 days: [3, 10, 7, 4, 12, 9, 11]<\/p>\n<p>Si cela it\u00e8re par jour, ce n&#8217;est pas magique, c&#8217;est parce jour (enfin day) est l&#8217;argument step par d\u00e9faut. Mais on peu bien entendu changer la taille du pas.<\/p>\n<p>Enfin, par d\u00e9faut, toutes les fonctions utilise Count pour calculer les r\u00e9sultat. (et quand je dis Count, c&#8217;est effectivement la class Count des Aggregat de django) Mais on peut changer ce comportement en donnant une valeur au param\u00e8tre agregate_class du QuerySetStats.<\/p>\n<h1>4-Conclusion<\/h1>\n<p>Comme je le disais en introduction, j&#8217;aime beaucoup les petites apps qui font des petits trucs, mais qui le font bien. C&#8217;est bien souvent plus utile que les grosses apps qui font tout et le caf\u00e9 et qui sont bien plus difficile \u00e0 int\u00e9grer (ceci n&#8217;est pas une critique de pinax hein, ne me faite pas dire ce que je n&#8217;ai pas dit).<\/p>\n<p>Donc j&#8217;aime forc\u00e9ment, beaucoup qsstats.<\/p>\n<p>Une petite information un fork de qsstats a \u00e9t\u00e9 cr\u00e9\u00e9, sur bitbucket (et que vous trouverez sur pypi). Il s&#8217;appelle <a href=\"http:\/\/bitbucket.org\/kmike\/django-qsstats-magic\/src\">django-qsstats-magic<\/a>. Il rajoute la gestion des heures et des minutes, une optimisation qui fonctionne avec mysql et modifie des trucs en interne. Mais effectivement, je n&#8217;ai pas pouss\u00e9 plus loin mes investigations.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ce mois-ci, j&#8217;aurais pu \u00e9crire plusieurs django-app du mois. Si j&#8217;avais eu le temps. Mais bon, le jour o\u00f9 j&#8217;aurais le temps, \u00e7a se saura. Du coup, alors que l&#8217;horloge tourne et qu&#8217;il ne me reste plus qu&#8217;un jour et demi pour \u00e9crire ma django app du mois et mon polargeek, j&#8217;ai d\u00e9cid\u00e9 d&#8217;\u00e9crire ma &hellip; <a href=\"https:\/\/j-mad.com\/blog\/2010\/10\/30\/django-qsstats-parce-quen-vraion-adore-tous-faire-des-stats\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Django-qsstats, parce qu&#8217;en vrai,on adore tous faire des stats.<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[35],"tags":[99,98],"class_list":["post-1023","post","type-post","status-publish","format-standard","hentry","category-la-django-app-du-mois","tag-django","tag-python"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p12cdp-gv","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts\/1023","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/comments?post=1023"}],"version-history":[{"count":2,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts\/1023\/revisions"}],"predecessor-version":[{"id":1025,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts\/1023\/revisions\/1025"}],"wp:attachment":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/media?parent=1023"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/categories?post=1023"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/tags?post=1023"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}