{"action":"created","comment":{"url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/comments/28510244","id":28510244,"diff_hunk":"@@ -0,0 +1,470 @@\n+Comment passer de LIMIT / OFFSET aux Continuation Tokens ?\n+##########################################################\n+\n+:date: 2015-04-16 13:25\n+:authors: Rémy Hubscher\n+:summary: De LIMIT / OFFSET aux continuations tokens\n+\n+\n+Si vous êtes là, c'est que quelqu'un a du vous le dire : Paginer sur des\n+grosses tables avec LIMIT et OFFSET ça ne monte pas en charge et plus\n+on avance dans les pages, plus c'est lent.\n+\n+Il y a même une initiative \"STOP OFFSET\" pour que les développeurs y\n+pensent : http://use-the-index-luke.com/no-offset\n+\n+\n+Pourquoi c'est mal d'utiliser OFFSET et LIMIT ?\n+===============================================\n+\n+Le post ci-dessus l'explique mieux que moi, mais ce qu'il faut retenir\n+c'est que la base de données doit faire la requête entière pour\n+ensuite aller se déplacer jusqu'à la bonne ligne.\n+\n+Si pendant que vous changez de page quelqu'un modifie la collection\n+vous pouvez aussi louper des entrées ou avoir des doublons dans les\n+résultats. Ce n'est pas consistant.\n+\n+\n+Comment faire pour s'en passer ?\n+================================\n+\n+Pour bien comprendre, prenons le cas le plus simple.\n+\n+Si vous triez par ``id`` (la clé primaire), il vous suffit de garder\n+trace du dernier ``id`` de la page précedente.\n+\n+Prenons comme exemple la table de salariés suivante.\n+Pour les besoins de l'exercice la date de recrutement est unique.\n+Les données sont inexactes mais plausibles.\n+\n+.. code-block:: sql\n+\n+    CREATE TABLE `salaries` (\n+      `id` int(11) NOT NULL AUTO_INCREMENT,\n+      `nom` text NOT NULL,\n+      `societe` text NOT NULL,\n+      `date_embauche` date NOT NULL,\n+      PRIMARY KEY (`id`),\n+      UNIQUE KEY `date_embauche` (`date_embauche`)\n+    ) CHARSET=utf8 ;\n+\n+\n+.. code-block::\n+\n+    +----+----------+----------+---------------------+\n+    | ID | Nom      | Société  | Date de recrutement | \n+    +----+----------+----------+---------------------+\n+    | 1  | Rodolphe | Novapost | 2014-09-03          |\n+    +----+----------+----------+---------------------+\n+    | 2  | Tarek    | Mozilla  | 2009-03-01          |\n+    +----+----------+----------+---------------------+\n+    | 3  | Benoit   | Novapost | 2012-02-25          |\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+    | 5  | Bruno    | Novapost | 2013-06-14          |\n+    +----+----------+----------+---------------------+\n+    | 6  | Rémy     | Mozilla  | 2014-03-11          |\n+    +----+----------+----------+---------------------+\n+    | 7  | Mathieu  | Mozilla  | 2014-12-06          |\n+    +----+----------+----------+---------------------+\n+    | 8  | Natal    | Novapost | 2013-08-05          |\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+\n+Pour les besoins de l'exemple, nous allons également paginer nos\n+résultats par deux.\n+\n+\n+Voici les requêtes que nous ferions en utilisant OFFSET et LIMIT\n+----------------------------------------------------------------\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `id`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 1  | Rodolphe | Novapost | 2014-09-03          |\n+    +----+----------+----------+---------------------+\n+    | 2  | Tarek    | Mozilla  | 2009-03-01          |\n+    +----+----------+----------+---------------------+\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `id`\n+    LIMIT 2\n+    OFFSET 2\n+\n+    +----+----------+----------+---------------------+\n+    | 3  | Benoit   | Novapost | 2012-02-25          |\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Comment peut-on obtenir le même résultat sans utiliser OFFSET ?\n+---------------------------------------------------------------\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `id`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 1  | Rodolphe | Novapost | 2014-09-03          |\n+    +----+----------+----------+---------------------+\n+    | 2  | Tarek    | Mozilla  | 2009-03-01          |\n+    +----+----------+----------+---------------------+\n+\n+    SELECT * FROM `salaries`\n+    WHERE `id` > 2 \n+    ORDER BY `id`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 3  | Benoit   | Novapost | 2012-02-25          |\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Quand on fait le tri sur une valeur unique pour toute la collection,\n+on se rend compte que l'on peut sauvegarder la valeur du dernier\n+élément de la liste et l'utiliser pour faire une condition where.\n+\n+Si on fait maintenant le tri sur la date d'embauche :\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `date_embauche`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 2  | Tarek    | Mozilla  | 2009-03-01          |\n+    +----+----------+----------+---------------------+\n+    | 3  | Benoit   | Novapost | 2012-02-25          |\n+    +----+----------+----------+---------------------+\n+\n+    SELECT * FROM `salaries`\n+    WHERE `date_embauche` > '2012-02-25'\n+    ORDER BY `date_embauche`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+    | 5  | Bruno    | Novapost | 2013-06-14          |\n+    +----+----------+----------+---------------------+\n+\n+    SELECT * FROM `salaries`\n+    WHERE `date_embauche` > '2013-06-14'\n+    ORDER BY `date_embauche`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 8  | Natal    | Novapost | 2013-08-05          |\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Et pour la page précédente ?\n+----------------------------\n+\n+Soit on se rappelle du continuation token précédent, soit on peut\n+ruser en intervertissant l'ordre de tri puis en selectionnant les\n+résultats supérieurs au premier élément de la page courante et en\n+affichant les résultats dans l'ordre inverse :\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    WHERE `date_embauche` < '2013-08-05'\n+    ORDER BY `date_embauche` DESC\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 5  | Bruno    | Novapost | 2013-06-14          |\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+\n+Qu'il faut afficher à l'utilisateur comme cela :\n+\n+.. code-block:: sql\n+\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+    | 5  | Bruno    | Novapost | 2013-06-14          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Et pour les tris sur des clées non uniques ?\n+============================================\n+\n+Des lors qu'une clé de tri n'est plus unique comme c'était le cas avec\n+``date_embauche`` et ``id``, il faut trouver une requête qui permet\n+d'identifier de manière unique la ligne à partir de laquelle\n+continuer.\n+\n+Prenons la requête suivante :\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `societe`, `nom`\n+\n+    +----+----------+----------+---------------------+\n+    | ID | Nom      | Société  | Date de recrutement | \n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+    | 7  | Mathieu  | Mozilla  | 2014-12-06          |\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+    | 6  | Rémy     | Mozilla  | 2014-03-11          |\n+    +----+----------+----------+---------------------+\n+    | 2  | Tarek    | Mozilla  | 2009-03-01          |\n+    +----+----------+----------+---------------------+\n+    | 3  | Benoit   | Novapost | 2012-02-25          |\n+    +----+----------+----------+---------------------+\n+    | 5  | Bruno    | Novapost | 2013-06-14          |\n+    +----+----------+----------+---------------------+\n+    | 8  | Natal    | Novapost | 2013-08-05          |\n+    +----+----------+----------+---------------------+\n+    | 1  | Rodolphe | Novapost | 2014-09-03          |\n+    +----+----------+----------+---------------------+\n+\n+Si on souhaite obtenir la deuxième page en utilisant `id` on se rends\n+compte qu'il y a un soucis.\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    WHERE `id` > 7\n+    ORDER BY `societe`, `nom`\n+\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+    | 8  | Natal    | Novapost | 2013-08-05          |\n+    +----+----------+----------+---------------------+\n+\n+Or la page deux attendues est :\n+\n+.. code-block::\n+\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+    | 6  | Rémy     | Mozilla  | 2014-03-11          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Tri avec une seule clé non unique\n+---------------------------------\n+\n+Commençons avec un tri sur une seule colonne non unique ``societe``:\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `societe`\n+\n+    +----+----------+----------+---------------------+\n+    | ID | Nom      | Société  | Date de recrutement | \n+    +----+----------+----------+---------------------+\n+    | 2  | Tarek    | Mozilla  | 2009-03-01          |\n+    +----+----------+----------+---------------------+\n+    | 4  | Alexis   | Mozilla  | 2012-09-24          |\n+    +----+----------+----------+---------------------+\n+    | 6  | Rémy     | Mozilla  | 2014-03-11          |\n+    +----+----------+----------+---------------------+\n+    | 7  | Mathieu  | Mozilla  | 2014-12-06          |\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+    | 1  | Rodolphe | Novapost | 2014-09-03          |\n+    +----+----------+----------+---------------------+\n+    | 3  | Benoit   | Novapost | 2012-02-25          |\n+    +----+----------+----------+---------------------+\n+    | 5  | Bruno    | Novapost | 2013-06-14          |\n+    +----+----------+----------+---------------------+\n+    | 8  | Natal    | Novapost | 2013-08-05          |\n+    +----+----------+----------+---------------------+\n+\n+On se rends compte qu'il s'agit en fait de cette requête :\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    ORDER BY `societe`, `id`\n+\n+Notre requête pour la page deux c'est donc :\n+\n+   - Les salariés qui ont un nom de société supérieurs à Mozilla ou\n+   - Les salariés de Mozilla qui ont un id supérieur à 4\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    WHERE `societe` > 'Mozilla'\n+    OR (`societe` = 'Mozilla' AND `id` > 4)\n+    ORDER BY `societe`, `id`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 6  | Rémy     | Mozilla  | 2014-03-11          |\n+    +----+----------+----------+---------------------+\n+    | 7  | Mathieu  | Mozilla  | 2014-12-06          |\n+    +----+----------+----------+---------------------+\n+\n+On peut vérifier que le cas limite fonctionne aussi:\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    WHERE `societe` > 'Mozilla'\n+    OR (`societe` = 'Mozilla' AND `id` > 7)\n+    ORDER BY `societe`, `id`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+    | 1  | Rodolphe | Novapost | 2014-09-03          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Tri sur de multiples clé non uniques\n+------------------------------------\n+\n+Maintenant revenons à notre tri par `societe` et par `nom`\n+\n+Pour atteindre la page deux, on a donc trois ensembles à concatener:\n+\n+ - Les salariés de ``Mozilla`` qui s'appellent à ``Mathieu`` et dont l'``id`` est supérieur à ``7``\n+ - Les salariés de ``Mozilla`` dont le nom est supérieur à ``Mathieu``\n+ - Les salariés dont le nom de société est supérieur à ``Mozilla``\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    WHERE `societe` = 'Mozilla' AND `nom` = 'Mathieu' AND `id` > 7\n+    OR (`societe` = 'Mozilla' AND `nom` > 'Mathieu')\n+    OR `societe` > 'Mozilla'\n+    ORDER BY `societe`, `nom`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+    | 6  | Rémy     | Mozilla  | 2014-03-11          |\n+    +----+----------+----------+---------------------+\n+\n+On peut tester notre cas limite en ajoutant \n+\n+.. code-block::\n+\n+    +----+----------+----------+---------------------+\n+    | 10 | Mathieu  | Mozilla  | 2015-03-22          |\n+    +----+----------+----------+---------------------+\n+    \n+Et on a bien :\n+\n+.. code-block:: sql\n+\n+    SELECT * FROM `salaries`\n+    WHERE `societe` = 'Mozilla' AND `nom` = 'Mathieu' AND `id` > 7\n+    OR (`societe` = 'Mozilla' AND `nom` > 'Mathieu')\n+    OR `societe` > 'Mozilla'\n+    ORDER BY `societe`, `nom`\n+    LIMIT 2\n+\n+    +----+----------+----------+---------------------+\n+    | 10 | Mathieu  | Mozilla  | 2015-03-22          |\n+    +----+----------+----------+---------------------+\n+    | 9  | Nicolas  | Mozilla  | 2014-02-27          |\n+    +----+----------+----------+---------------------+\n+\n+\n+Généralisation\n+==============\n+\n+La manière générique que j'ai trouvé pour utiliser un **Continuation\n+Token** tout en laissant l'utilisateur choisir sa requête de tri est\n+de générer le token à partir du dernier élément de la page et des\n+champs de tri.\n+\n+En complétant toujours les champs de tri par la clé primaire à la fin.\n+\n+Ensuite je génère une fonction recursive qui prends la liste des\n+champs de tri et la dernière entrée de la page précédente et me\n+retourne une liste de conditions de tri.\n+\n+\n+.. code-block:: python\n+\n+    def get_continuation_token_conditions(record, sorting, results=None):\n+        \"\"\"Return the list of conditions for a given record and sorting attributes.\n+    \n+        >>> get_continuation_token_conditions(\n+        ...     {'id': 7, 'nom': 'Mathieu', 'societe': 'Mozilla'},\n+        ...     ['societe', 'nom', 'id']\n+        ... )\n+        [(('societe', '=', 'Mozilla'), ('nom', '=', 'Mathieu'), ('id', '>', 7)),\n+         (('societe', '=', 'Mozilla'), ('nom', '>', 'Mathieu')),\n+         (('societe', '>', 'Mozilla'))]\n+\n+        >>> get_continuation_token_conditions(\n+        ...     {'id': 7, 'nom': 'Mathieu', 'societe': 'Mozilla'},\n+        ...     ['societe', '-nom', 'id']\n+        ... )\n+        [(('societe', '=', 'Mozilla'), ('nom', '=', 'Mathieu'), ('id', '>', 7)),\n+         (('societe', '=', 'Mozilla'), ('nom', '<', 'Mathieu')),\n+         (('societe', '>', 'Mozilla'))]\n+        \n+        \"\"\"\n+        \n+        result = []\n+        for field in sorting[:-1]:\n+            field_name = field.lstrip('-')\n+            result.append((field_name, '=', record[field_name]))\n+        \n+        field = sorting[-1]\n+        field_name = field.lstrip('-')\n+        direction = (field[0] == '-') and '<' or '>'\n+        result.append((field_name, direction, record[field_name]))\n+        \n+        if results is not None:\n+            results.append(result)\n+        else:\n+            results = [result]\n+        \n+        if len(sorting) == 1:\n+            return results\n+        else:\n+            return get_continuation_token_conditions(record, sorting[:-1], results)\n+\n+\n+\n+Conclusion\n+==========\n+\n+Vous voyez que c'est possible de faire de la pagination à l'aide d'un\n+**continuation token** tout en gardant les fonctionnalités de tri.\n+\n+Qui dit conditions de tri dit aussi index et c'est là que bien cadrer\n+les fonctionnalités de tri autorisées devient intéressant. Est-ce bien\n+nécessaire de laisser l'utilisateur choisir les champs de tri, où\n+peut-on se contenter de lui faire changer l'ordre de tri ?\n+\n+Si votre condition de pagination est fixe, vous allez pouvoir créer","path":"content/mozilla/2015.055.contineous.token.rst","position":469,"original_position":469,"commit_id":"3902a896df70cf97670ef7ee1b88f5a395717e4c","original_commit_id":"3902a896df70cf97670ef7ee1b88f5a395717e4c","user":{"login":"leplatrem","id":546692,"avatar_url":"https://avatars.githubusercontent.com/u/546692?v=3","gravatar_id":"","url":"https://api.github.com/users/leplatrem","html_url":"https://github.com/leplatrem","followers_url":"https://api.github.com/users/leplatrem/followers","following_url":"https://api.github.com/users/leplatrem/following{/other_user}","gists_url":"https://api.github.com/users/leplatrem/gists{/gist_id}","starred_url":"https://api.github.com/users/leplatrem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leplatrem/subscriptions","organizations_url":"https://api.github.com/users/leplatrem/orgs","repos_url":"https://api.github.com/users/leplatrem/repos","events_url":"https://api.github.com/users/leplatrem/events{/privacy}","received_events_url":"https://api.github.com/users/leplatrem/received_events","type":"User","site_admin":false},"body":"condition de pagination ? ","created_at":"2015-04-16T13:46:01Z","updated_at":"2015-04-16T13:46:01Z","html_url":"https://github.com/mozilla-services/servicedenuages.fr/pull/4#discussion_r28510244","pull_request_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4","_links":{"self":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/comments/28510244"},"html":{"href":"https://github.com/mozilla-services/servicedenuages.fr/pull/4#discussion_r28510244"},"pull_request":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4"}}},"pull_request":{"url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4","id":33450217,"html_url":"https://github.com/mozilla-services/servicedenuages.fr/pull/4","diff_url":"https://github.com/mozilla-services/servicedenuages.fr/pull/4.diff","patch_url":"https://github.com/mozilla-services/servicedenuages.fr/pull/4.patch","issue_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/4","number":4,"state":"open","locked":false,"title":"Add an article about continuation tokens.","user":{"login":"Natim","id":229453,"avatar_url":"https://avatars.githubusercontent.com/u/229453?v=3","gravatar_id":"","url":"https://api.github.com/users/Natim","html_url":"https://github.com/Natim","followers_url":"https://api.github.com/users/Natim/followers","following_url":"https://api.github.com/users/Natim/following{/other_user}","gists_url":"https://api.github.com/users/Natim/gists{/gist_id}","starred_url":"https://api.github.com/users/Natim/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Natim/subscriptions","organizations_url":"https://api.github.com/users/Natim/orgs","repos_url":"https://api.github.com/users/Natim/repos","events_url":"https://api.github.com/users/Natim/events{/privacy}","received_events_url":"https://api.github.com/users/Natim/received_events","type":"User","site_admin":false},"body":"http://www.servicedenuages.fr/drafts/comment-passer-de-limit-offset-aux-continuation-tokens.html","created_at":"2015-04-16T13:26:45Z","updated_at":"2015-04-16T13:46:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9ce4c699cc6be7648c251f0840e2fa97a01f4552","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4/commits","review_comments_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4/comments","review_comment_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/4/comments","statuses_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/statuses/3902a896df70cf97670ef7ee1b88f5a395717e4c","head":{"label":"mozilla-services:continuation-tokens","ref":"continuation-tokens","sha":"3902a896df70cf97670ef7ee1b88f5a395717e4c","user":{"login":"mozilla-services","id":1066228,"avatar_url":"https://avatars.githubusercontent.com/u/1066228?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-services","html_url":"https://github.com/mozilla-services","followers_url":"https://api.github.com/users/mozilla-services/followers","following_url":"https://api.github.com/users/mozilla-services/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-services/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-services/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-services/subscriptions","organizations_url":"https://api.github.com/users/mozilla-services/orgs","repos_url":"https://api.github.com/users/mozilla-services/repos","events_url":"https://api.github.com/users/mozilla-services/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-services/received_events","type":"Organization","site_admin":false},"repo":{"id":34000996,"name":"servicedenuages.fr","full_name":"mozilla-services/servicedenuages.fr","owner":{"login":"mozilla-services","id":1066228,"avatar_url":"https://avatars.githubusercontent.com/u/1066228?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-services","html_url":"https://github.com/mozilla-services","followers_url":"https://api.github.com/users/mozilla-services/followers","following_url":"https://api.github.com/users/mozilla-services/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-services/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-services/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-services/subscriptions","organizations_url":"https://api.github.com/users/mozilla-services/orgs","repos_url":"https://api.github.com/users/mozilla-services/repos","events_url":"https://api.github.com/users/mozilla-services/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-services/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mozilla-services/servicedenuages.fr","description":"The da-french-team blog.","fork":false,"url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr","forks_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/forks","keys_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/teams","hooks_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/hooks","issue_events_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/events{/number}","events_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/events","assignees_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/assignees{/user}","branches_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/branches{/branch}","tags_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/tags","blobs_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/refs{/sha}","trees_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/statuses/{sha}","languages_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/languages","stargazers_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/stargazers","contributors_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/contributors","subscribers_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/subscribers","subscription_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/subscription","commits_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/commits{/sha}","git_commits_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/commits{/sha}","comments_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/comments{/number}","issue_comment_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/comments{/number}","contents_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/contents/{+path}","compare_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/merges","archive_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/downloads","issues_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues{/number}","pulls_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls{/number}","milestones_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/milestones{/number}","notifications_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/labels{/name}","releases_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/releases{/id}","created_at":"2015-04-15T15:07:57Z","updated_at":"2015-04-16T11:11:39Z","pushed_at":"2015-04-16T13:28:55Z","git_url":"git://github.com/mozilla-services/servicedenuages.fr.git","ssh_url":"git@github.com:mozilla-services/servicedenuages.fr.git","clone_url":"https://github.com/mozilla-services/servicedenuages.fr.git","svn_url":"https://github.com/mozilla-services/servicedenuages.fr","homepage":"http://www.servicedenuages.fr","size":0,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"mozilla-services:master","ref":"master","sha":"e047b04f48d16042c69898528ac9015c240b901e","user":{"login":"mozilla-services","id":1066228,"avatar_url":"https://avatars.githubusercontent.com/u/1066228?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-services","html_url":"https://github.com/mozilla-services","followers_url":"https://api.github.com/users/mozilla-services/followers","following_url":"https://api.github.com/users/mozilla-services/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-services/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-services/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-services/subscriptions","organizations_url":"https://api.github.com/users/mozilla-services/orgs","repos_url":"https://api.github.com/users/mozilla-services/repos","events_url":"https://api.github.com/users/mozilla-services/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-services/received_events","type":"Organization","site_admin":false},"repo":{"id":34000996,"name":"servicedenuages.fr","full_name":"mozilla-services/servicedenuages.fr","owner":{"login":"mozilla-services","id":1066228,"avatar_url":"https://avatars.githubusercontent.com/u/1066228?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-services","html_url":"https://github.com/mozilla-services","followers_url":"https://api.github.com/users/mozilla-services/followers","following_url":"https://api.github.com/users/mozilla-services/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-services/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-services/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-services/subscriptions","organizations_url":"https://api.github.com/users/mozilla-services/orgs","repos_url":"https://api.github.com/users/mozilla-services/repos","events_url":"https://api.github.com/users/mozilla-services/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-services/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mozilla-services/servicedenuages.fr","description":"The da-french-team blog.","fork":false,"url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr","forks_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/forks","keys_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/teams","hooks_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/hooks","issue_events_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/events{/number}","events_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/events","assignees_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/assignees{/user}","branches_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/branches{/branch}","tags_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/tags","blobs_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/refs{/sha}","trees_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/statuses/{sha}","languages_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/languages","stargazers_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/stargazers","contributors_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/contributors","subscribers_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/subscribers","subscription_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/subscription","commits_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/commits{/sha}","git_commits_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/git/commits{/sha}","comments_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/comments{/number}","issue_comment_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/comments{/number}","contents_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/contents/{+path}","compare_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/merges","archive_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/downloads","issues_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues{/number}","pulls_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls{/number}","milestones_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/milestones{/number}","notifications_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/labels{/name}","releases_url":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/releases{/id}","created_at":"2015-04-15T15:07:57Z","updated_at":"2015-04-16T11:11:39Z","pushed_at":"2015-04-16T13:28:55Z","git_url":"git://github.com/mozilla-services/servicedenuages.fr.git","ssh_url":"git@github.com:mozilla-services/servicedenuages.fr.git","clone_url":"https://github.com/mozilla-services/servicedenuages.fr.git","svn_url":"https://github.com/mozilla-services/servicedenuages.fr","homepage":"http://www.servicedenuages.fr","size":0,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4"},"html":{"href":"https://github.com/mozilla-services/servicedenuages.fr/pull/4"},"issue":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/4"},"comments":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/mozilla-services/servicedenuages.fr/statuses/3902a896df70cf97670ef7ee1b88f5a395717e4c"}}}}