Téléverser une contribution communautaire sur data.gouv.fr

Ou, s’il l’on préfère, uploader une community resource sur data.gouv.fr.

Objectif

Tous les mois le ministère de l’intérieur met à jour les données concernant les faits constatés par département depuis 1996. Ces données sont mises à disposition sous forme d’un fichier Excel, donc difficilement manipulable, avec un onglet par département (encore pire).

Pour pouvoir les utiliser efficacement (sous R par exemple), on a besoin de regrouper ces données dans un fichier .csv (plus exactement .tsv pour mettre en évidence qu’on utilise la tabulation comme délimiteur).

Cette transformation est décrite dans l’article Conversion Excel vers tsv des données sur les faits constatées.

Une fois ce travail effectué, le site data.gouv.fr permet d’en faire bénéficier les autres utilisateurs en ajoutant au jeu de données initial une contribution communautaire qui apparaitra sous les fichiers principaux.

Le but de ce tutoriel est de montrer comment, en utilisant l’API fournie par data.gouv.fr, on peut automatiser la création de cette contribution communautaire sans intervention manuelle.

Pour cela, on utilisera la commande curl, qui est un peu le couteau suisse lorsque qu’il faut parler une API Web, et la commande jq qui permet de travailler les réponses JSON.

Il faut maintenant tenter de décrypter la documentation API pour comprendre les bonnes commandes à passer.

Variables

Avant toute chose, il faut penser à récupérer sa clé API qui figure dans les paramètres de son profil utilisateur. Comme on va faire des mises à jour et des créations, il faut être identifié et la clé est le reflet de cette identification. Dans la suite, on considère que cette clé figure dans la variable KEY

KEY="<clé APPI personnelle>"

Pour simplifier les appels API, on crée également la variable suivante:

API="https://www.data.gouv.fr/api/1"

Enfin, on aura besoin de l’identifiant du jeu de données. On trouve cet identifiant en cliquant sur le bouton bleu détails, sur la droite de l’écran des données concernant les faits constatés.

Pour le jeu de données qui m’intéresse, je fais donc l’affectation suivante

DATASET=5617ad4dc751df6211cdbb49

Uploader le fichier

On considère que le fichier qui va constituer la ressource est dans la variable FILE

FILE="exemple.tsv"

La première phase consiste à uploader le fichier. Dans la documentation API, on trouve la syntaxe sous /datasets/{dataset}/upload/community/. Les accolades autour de dataset signifie qu’il faut passer l’identifiant du jeu de données dans l’URL.

Dans la documentation API, on voit qu’il faut passer une commande POST en fournissant le nom du fichier en paramètre file.

Traduit en commande curl, cela donne

curl  \
    -H "X-API-KEY:$KEY" \
    -X POST \
    -F "file=@$FILE" \
    "$API/datasets/$DATASET/upload/community/"

Cette commande analyse le fichier transmis et renvoit un objet JSON qu’il faut conserver car il va être utile pour la commande qui va suivre

{
  "checksum": {
    "type": "sha1",
    "value": "1e7309678c995c11c9163d1f59add3ea84b2c63b"
  },
  "created_at": "2016-06-02T10:52:41.782282",
  "description": null,
  "filesize": 11284,
  "filetype": "file",
  "format": "tsv",
  "id": "483ed173-6f6d-4f15-a9e5-512b6b0463e1",
  "is_available": true,
  "last_modified": "2016-06-02T10:52:41.782311",
  "metrics": {
    "nb_hits": 0,
    "nb_uniq_visitors": 0,
    "nb_visits": 0,
    "views": 0
  },
  "mime": "text/tab-separated-values",
  "published": "2016-06-02T10:52:41.782493",
  "success": true,
  "title": "exemple.tsv",
  "url": "https://www.data.gouv.fr/s/resources/chiffres-departementaux-mensuels-relatifs-aux-crimes-et-delits-enregistres-par-les-services-de-police-et-de-gendarmerie-depuis-janvier-1996/20160602-105241/exemple.tsv",
}

Mettre à jour la ressource communautaire

A ce stade, la ressource communautaire existe et a un identifiant qui apparait dans le champ id du JSON ci-dessus : 483ed173-6f6d-4f15-a9e5-512b6b0463e1 dans notre cas. Mais elle n’apparait toujours pas dans les ressources liées au jeu de données, malgré le fait qu’on avait transmis l’identifiant du jeu de données.

Il faut passer une nouvelle commande pour faire le lien entre la ressource communautaire et le dataset.

Dans la documentation API, on trouve la syntaxe sous /datasets/community_resources/{community}/.

On voit déjà qu’il faut qu’on récupère l’identifiant de la ressource qui apparait dans la réponse JSON précédente. On le fait en utilisant la commande jq qui permet de travailler sur les fichiers JSON. En l’occurence, pour récupérer l’identifiant dans la variable $CR, on fait

CR=$(jq -r '.id' $JSON)

Pour mettre à jour la ressource, la documentation API indique qu’il faut transmettre une commande PUT avec un objet JSON. Ce qui donne la commande suivante:

curl  \
    -H "Content-Type: application/json" \
    -H "X-API-KEY:$KEY" \
    -X PUT \
    -d @$JSON \
    "$API/datasets/community_resources/$CR/"

Quelques remarques sur cette commande :

  • -H “Content-Type: application/json” : l’ajout de cet header est important car la commande ne fonctionne pas correctement sinon.
  • la variable $JSON contient le nom du fichier qui contient l’objet JSON

J’avais l’espoir que la transmission en paramètre de l’objet JSON de l’étape précédente pouvait suffire, mais ce n’est pas le cas. La ressource reste non rattachée au dataset, et donc invisible.

Après plusieurs tentatives, et en observant les appels réseau lorsqu’on transmet manuellement une ressource depuis le site data.gouv.fr, j’ai repéré qu’il fallait que le champ dataset soit présent dans l’objet JSON, simplement en ajoutant le champ correspondant, en dernière ligne sur l’exemple ci-dessous:

{
  "checksum": {
    "type": "sha1",
    "value": "1e7309678c995c11c9163d1f59add3ea84b2c63b"
  },
  "created_at": "2016-06-02T10:52:41.782282",
  "description": null,
  "filesize": 11284,
  "filetype": "file",
  "format": "tsv",
  "id": "483ed173-6f6d-4f15-a9e5-512b6b0463e1",
  "is_available": true,
  "last_modified": "2016-06-02T10:52:41.782311",
  "metrics": {
    "nb_hits": 0,
    "nb_uniq_visitors": 0,
    "nb_visits": 0,
    "views": 0
  },
  "mime": "text/tab-separated-values",
  "published": "2016-06-02T10:52:41.782493",
  "success": true,
  "title": "exemple.tsv",
  "url": "https://www.data.gouv.fr/s/resources/chiffres-departementaux-mensuels-relatifs-aux-crimes-et-delits-enregistres-par-les-services-de-police-et-de-gendarmerie-depuis-janvier-1996/20160602-105241/exemple.tsv",
  "dataset": "5617ad4dc751df6211cdbb49"
}

La même commande curl que ci-dessus, mais avec ce fichier complété, fonctionne et la ressource apparait bien sur le site data.gouv.fr.

Cette deuxième commande renvoit également un objet JSON dont on remarque qu’il est beaucoup plus complet, en particulier au niveau du champ dataset ou owner:

{
  "checksum": {
    "type": "sha1",
    "value": "1e7309678c995c11c9163d1f59add3ea84b2c63b"
  },
  "created_at": "2016-06-02T10:52:41.782000",
  "dataset": {
    "class": "Dataset",
    "id": "5617ad4dc751df6211cdbb49",
    "page": "https://www.data.gouv.fr/fr/datasets/chiffres-departementaux-mensuels-relatifs-aux-crimes-et-delits-enregistres-par-les-services-de-police-et-de-gendarmerie-depuis-janvier-1996/",
    "title": "Chiffres départementaux mensuels relatifs aux crimes et délits enregistrés par les services de police et de gendarmerie depuis janvier 1996",
    "uri": "https://www.data.gouv.fr/api/1/datasets/chiffres-departementaux-mensuels-relatifs-aux-crimes-et-delits-enregistres-par-les-services-de-police-et-de-gendarmerie-depuis-janvier-1996/"
  },
  "description": null,
  "filesize": 11284,
  "filetype": "file",
  "format": "tsv",
  "id": "483ed173-6f6d-4f15-a9e5-512b6b0463e1",
  "is_available": true,
  "last_modified": "2016-06-02T11:26:48.794702",
  "metrics": {
    "nb_hits": 0,
    "nb_uniq_visitors": 0,
    "nb_visits": 0,
    "views": 0
  },
  "mime": "text/tab-separated-values",
  "organization": null,
  "owner": {
    "avatar": "https://www.data.gouv.fr/s/avatars/f7/b0f99299ec484ea8054d5f24841450-100.png",
    "class": "User",
    "first_name": "Philippe",
    "id": "53700bc3a3a7294600435fbe",
    "last_name": "Chataignon",
    "page": "https://www.data.gouv.fr/fr/users/philippe-chataignon/",
    "slug": "philippe-chataignon",
    "uri": "https://www.data.gouv.fr/api/1/users/philippe-chataignon/"
  },
  "published": "2016-06-02T10:52:41.782493",
  "title": "exemple.tsv",
  "url": "https://www.data.gouv.fr/s/resources/chiffres-departementaux-mensuels-relatifs-aux-crimes-et-delits-enregistres-par-les-services-de-police-et-de-gendarmerie-depuis-janvier-1996/20160602-105241/exemple.tsv"
}

Script final

Qui dit automatisation dit script shell.

L’ensemble des éléments ci-dessus peut être regroupé dans un script unique qui enchaîne les différentes étapes.

Avec les explications ci-dessus, le script devrait être compréhensible.

Le seul élément non expliqué est l’utilisation de jq pour ajouter le champ dataset. La syntaxe de la commande jq n’est pas des plus limpides. '.' signifie tous les champs que l’on passe à un filtre | puis on crée le champ dataset et on lui affecte la variable shell DATASET en échappant les " .

jq ". | .dataset=\"$DATASET\""

Le script prend 2 paramètres : le fichier à transmettre et l’identifiant du dataset concerné. Dans l’article Conversion Excel vers tsv des données sur les faits constatées, ce script s’appelle cr_upload : il est appelé à la fin du programme R, une fois la conversion réalisée pour téléverser le fichier créé vers data.gouv.fr:

#!/bin/sh

KEY="<clé API>"
API="https://www.data.gouv.fr/api/1"
JSON=$(tempfile -p json)
FILE=$1
DATASET=$2

## upload file
curl  \
    -s \
    -H "X-API-KEY:$KEY" \
    -X POST \
    -F "file=@$FILE" \
    "$API/datasets/$DATASET/upload/community/" | jq ". | .dataset=\"$DATASET\"" > $JSON

# get cr id
CR=$(jq -r '.id' $JSON)
echo "community_ressource_id:" $CR

### update rc
curl  \
    -s \
    -H "Content-Type: application/json" \
    -H "X-API-KEY:$KEY" \
    -X PUT \
    -d @$JSON \pi
    "$API/datasets/community_resources/$CR/"

rm $JSON

Effacer la ressource

Lors de test, on peut-être amener à créer des ressources qu’on veut effacer. Il suffit de passer la commande curl suivante avec l’identifiant de la ressource concernée

curl -H "X-API-KEY:$KEY" -X DELETE "$API/datasets/community_resources/483ed173-6f6d-4f15-a9e5-512b6b0463e11/"

Conclusion

Les API, c’est bien. Comme le dit Henri Verdier dans cet article, “les API fournissent l’ensemble des instructions pour qu’une application puisse se “brancher” facilement sur une base de données ou une autre brique logicielle”. Dans le cas expliqué ici, l’API permet effectivement d’automatiser le travail et de le faire faire par une machine, ce qui est l’objectif.

Après, la question reste posée sur l’utilisation du terme facilement. La documentation API a le mérite d’exister. Mais j’ai ressenti un grand sentiment de solitude pour aborder l’utilisation concrète de cette API et je n’ai pas trouvé d’exemple d’utilisation, via Google par exemple. Peut-être que je n’ai pas cherché au bon endroit ? Peut-être existe-t-il un cercle secret des utilisateurs de l’API data.gouv.fr ? En tout cas, l’absence d’exemples concrêts d’utilisation de cette API est ce qui m’a à écrire cet article pour laisser une trace éventuellement utile à d’autres.