Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connecter TaxHub à wikidata pour en récupérer les médias #150

Open
camillemonchicourt opened this issue Nov 14, 2017 · 23 comments
Open

Comments

@camillemonchicourt
Copy link
Member

camillemonchicourt commented Nov 14, 2017

Dans le cadre de la prestation GeoNature/Opendata/Interopérabilité qui a été lancée récemment (http://geonature.fr/documents/cctp/2017-10-CCTP-GeoNature-interoperabilite.pdf), @amandine-sahl a fait des premiers tests exploratoires.

Les tests s'appuient sur la démarche https://fr.okfn.org/2017/03/03/diplonum-mieux-proteger-les-especes-menacees-avec-wikidata/

L'objectif est de récupérer les médias de Wikidata et Commons pour les intégrer dans les taxons présents dans une instance de TaxHub (taxonomie.t_medias) : https://github.com/PnX-SI/TaxHub/blob/master/data/taxhubdb.sql#L359-L372

Ces tests ont ainsi permis au PnCevennes de récupérer 6261 médias pour 4255 taxons sur 7262.

olicmeoehkoclfoj

Il serait aussi possible de récupérer les cartes de répartition (P181) et les médias audio (p51).

Code expérimental du test :

A VENIR
@samuelpriou
Copy link

samuelpriou commented Nov 14, 2017

C'est top ça ! Merci @amandine-sahl

@Splendens
Copy link

Génial !

@amandine-sahl
Copy link
Contributor

amandine-sahl commented Nov 14, 2017

Ci joint le code, même si ça reste à l'état de brouillon

Pour que ça fonctionne il faut installer les librairies python : SPARQLWrapper et psycopg2

import requests
import psycopg2

from SPARQLWrapper import SPARQLWrapper, JSON

try:
    conn = psycopg2.connect("dbname='mabase' user='monuser' host='monhost' password='monpass'")
except:
    print "I am unable to connect to the database"

#DbMedia Query
query = """SELECT ?item ?itemLabel ?nomSc ?image ?identifiant_TAXREF  WHERE {
  ?item wdt:P225 ?nomSc.
  ?item wdt:P18 ?image.
  ?item wdt:P3186 '%s'
 
 SERVICE wikibase:label { bd:serviceParam wikibase:language "fr" }
} LIMIT 200"""

sparql = SPARQLWrapper("https://query.wikidata.org/sparql")

cur = conn.cursor()
#cur.execute("""SELECT cd_ref from taxonomie.bib_noms LIMIT 100""")
#cur.execute("""SELECT cd_ref from atlas.vm_taxons_plus_observes LIMIT 100""")
cur.execute("""SELECT DISTINCT cd_ref from taxonomie.bib_noms LEFT OUTER JOIN taxonomie.t_medias USING(cd_ref) WHERE id_media IS NULL""")

rows = cur.fetchall()

sqlI = "INSERT INTO taxonomie.t_medias(cd_ref, titre, url,is_public, id_type, auteur) VALUES (%s, '%s', '%s', true, 2, '%s')"

for cd_ref in rows:
    try:
        print("Taxon %s" % cd_ref[0])
        sparql.setQuery(query % cd_ref[0])
        sparql.setReturnFormat(JSON)
        results = sparql.query().convert()

        for result in results["results"]["bindings"]:
            if (result['image']['value']):
                print ' -- INSERT IMAGE'
                            
                from lxml import etree
                #Recuperation des donnees sur commons
                url = "https://tools.wmflabs.org/magnus-toolserver/commonsapi.php?image=%s"% result['image']['value'].split('Special:FilePath/', 1 )[1]
                r = requests.get(url)
                import xmltodict
                a = xmltodict.parse(r.content)
                try:
                    aut = 'Commons'
                    if 'author' in a['response']['file']:
                        if len( a['response']['file']['author'] ) < 500:
                           aut = a['response']['file']['author']
                    sql= sqlI % (cd_ref[0], a['response']['file']['name'], result['image']['value'], aut)
                    cur.execute(sql)  
                    conn.commit()
                except Exception as e:
                    print('         ERREOR')
                    print(e)
                    conn.rollback()
                    pass
    except Exception as e:
        pass

cur.execute("""
    UPDATE taxonomie.t_medias SET id_type = 1
    WHERE id_media IN (SELECT max(id_media)
    FROM taxonomie.t_medias
    GROUP BY cd_ref);
""")

cur.execute("REFRESH MATERIALIZED VIEW atlas.vm_medias;")
cur.execute("REFRESH MATERIALIZED VIEW atlas.vm_taxons_plus_observes;")

conn.commit()

conn.close()

@samuelpriou
Copy link

@amandine-sahl, faut il une version minimale de taxhub ? Au PNM nous utilisons la version 1.1.1.
Merci

@sig-pnrnm
Copy link

Super nouvelle !
Mais alors, du coup, il faudra sans doute songer à ajouter des catégories de médias pour ces médias "Communs".
Pour les photos, nous avons actuellement "photo principale" et "photo". Il faudrait ajouter une catégorie "photo (communs)", pour bien les distinguer et éventuellement les afficher (Atlas, ...) après les photos "normales", qui sont chez nous autant que possibles "locales".

@camillemonchicourt
Copy link
Member Author

A voir si il est utile d'ajouter un type, peut-être.
Mais c'est surtout le champs source discuté précédemment (#126) qu'il serait utile d'ajouter pour bien identifier d'où vient le média (et éventuellement l'afficher sur GN-atlas).

@camillemonchicourt
Copy link
Member Author

camillemonchicourt commented Dec 12, 2017

Commit principal : 00da9a6

Les champs Source et Licence ont été ajoutés à la BDD dans la table t_medias et ils sont renseignés par ce script.
Ne manque plus qu'à expliquer l'usage du script.

@camillemonchicourt
Copy link
Member Author

camillemonchicourt commented Dec 13, 2017

DÉBUT D'EXPLICATION : Wikidata permet de récupérer les ID des images associés au CD_REF (propriété 3186 dans Wikidata).

Ensuite on récupère les images et leurs infos dans COMMONS avec les ID IMAGES récupérés dans Wikidata.

Pour choisir pour quels CD_REF on souhaite interroger Wikidata et rapatrier des images, il faut adapter la requête SQL dans data/scripts/import_wikimedia_commons/run_import.py.

  • Comment l'installer ?
  • Comment l’exécuter ?
  • Comment gérer les Photos_principales (1 par CD_REF, id_type = 1) et les Photos (id_type = 2)

@amandine-sahl
Copy link
Contributor

amandine-sahl commented Dec 15, 2017

Installation de l’environnement python

  • Pour être plus propre il est conseillé de créer un nouveau virtualenv et d'y installer les paquets python
virtualenv -p /usr/bin/python3 venv #Python 3 n'est pas requis
source venv/bin/activate
pip install lxml psycopg2 requests SPARQLWrapper xmltodict
deactivate

Configuration du script

  • Paramètres de connexion à la base :
    Pour récupérer les paramètres de connexion à taxhub, créer un lien symbolique config.py
    ln -s ../../../config.py .
    Vous pouvez aussi définir votre propre connexion

  • Sélection d'une liste de cd_ref: Trois requêtes exemple figurent dans le script.

    • liste des cd_refs des taxons n'ayant pas de média
    • 10 premiers cd_refs de bib_nom (activé par défaut)
    • 100 premiers cd_refs de la table vm_taxons_plus_observes
  • Paramétrage de la fonction main :

    • connexion à la base de données
    • liste des cd_refs
    • rafraichir les tables de l'atlas (True/False)
    • simulé l'insertion (True/False)

Execution du script

  • Activer le virtualenv
    source venv/bin/activate

  • lancer le script
    python run_import.py > import_mediawiki.log

@camillemonchicourt
Copy link
Member Author

camillemonchicourt commented Jun 27, 2018

A noter qu'il est possible de récupérer les photo wikimedia à partir du cd_nom. Exemple avec 163394 :
https://query.wikidata.org/bigdata/namespace/wdq/sparql?format=json&query=SELECT%20%3Fitem%20%3Fimage%20%0AWHERE%20{%0A%20%20%3Fitem%20wdt%3AP18%20%3Fimage.%0A%20%20%3Fitem%20wdt%3AP3186%20%27163394%27%0A}%0ALIMIT%20100

Voir aussi la nouvelle API Taxref dans laquelle les médias CC sont interrogeables : https://taxref.mnhn.fr/taxref-web/api/doc

Exemple : https://taxref.mnhn.fr/api/media/cdNom/134387

@Splendens
Copy link

Voilà un test de récupération des photos avec l'API Taxref (2 scripts shell indépendants, un avec suivi des ajouts de photos, un sans) : Splendens/atlas_biodiv_pdl@2780bea

capture du 2018-07-31 11-22-27

@camillemonchicourt
Copy link
Member Author

camillemonchicourt commented Aug 24, 2018

L'INPN indique qu'il est important de véhiculer source et licences associés. La licence doit donc aussi être affichée donc.

Je ferai quelque chose comme : H. Tinguy (INPN - CC BY-NC-SA).

@camillemonchicourt
Copy link
Member Author

Je me demande aussi si c'est pertinent de télécharger les photos ou si il vaudrait pas mieux juste utiliser l'URL de l'image sans la stocker localement.

@gildeluermoz
Copy link
Contributor

Je dirais que si la source est fiable et durable il est préférable de ne pas la stocker. Mais peut on mettre en place un mécanisme pour tirer les sources pertinentes de celles qui ne le sont pas ?

@camillemonchicourt
Copy link
Member Author

Quand on se connecte à l'API on peut choisir de renseigner l'URL du média là où il est ou bien de la rapatrier en local en de stocker son chemin.

@camillemonchicourt
Copy link
Member Author

Le script d'import de médias INPN a été réécrit en Python et ajusté par @amandine-sahl et est maintenant disponible ici : https://github.com/PnX-SI/TaxHub/tree/develop/data/scripts/import_inpn_media

@Amegilla
Copy link

Amegilla commented Mar 14, 2019

Bonjour,

Je suis en train de tester le script d'import et j'ai l'erreur suivante. Je ne comprends pas ce qui pose pb.
Des suggestions ?
merci

python import_inpn_media.py
...venv/lib/python3.5/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order
to keep installing from binary please use "pip install psycopg2-binary" instead. For details
see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
  """)
TAXON : cd_ref = 60295

Traceback (most recent call last):
  File "import_inpn_media.py", line 161, in <module>
    if '_embedded' in r.json():
  File "...venv/lib/python3.5/site-packages/requests/models.py", line 897, in json
    return complexjson.loads(self.text, **kwargs)
  File "/usr/lib/python3.5/json/__init__.py", line 319, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.5/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.5/json/decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

@DonovanMaillard
Copy link
Contributor

DonovanMaillard commented Apr 8, 2019

Désolé @RomainBaghi , pas de réponse à te donner pour ma part, j'espère que tu as pu solutionner ton soucis.

Pour ma part, ça a marché tout seul. Un grand merci à @amandine-sahl pour ce script !

(pour info, j'ai également pris le premier media pour chaque cd_ref afin de le rendre de type media_principal :

WITH first_media AS (SELECT MIN(id_media), cd_ref FROM taxonomie.t_medias
GROUP BY cd_ref)
UPDATE taxonomie.t_medias
SET id_type=1
WHERE id_media IN (SELECT min FROM first_media) 

@amandine-sahl
Copy link
Contributor

Il faudrait si possible que tu indiques la requete utilisée dans ton script.

@xavyeah39
Copy link

Salut à tous,

Je poste ici car c'est connexe mais nécessite peut-être une autre issue.

J'ai utilisé moi aussi le script d'@amandine-sahl pour peupler notre liste de taxons avec les médias INPN (grand merci à elle en passant !).
Aujourd'hui sur notre atlas, plus aucun médias servis par les URL de l'API ne remontent et une erreur 500 côté API Taxref est levée.

Après échange avec @DonovanMaillard, même comportement sur son instance. Il est allé aux nouvelles auprès du MNHN qui lui confirme que le problème vient bien de chez eux, qu'il a été identifié et déjà réglé une 1ere fois ce matin. Là, c'est reparti semble-t'il.

Cela (re)pose la question de l'inter-dépendances des webservices même auprès de ceux réputés stables et suivis (on peut faire le parallèle avec les géoservices IGN qui ont été plus que capricieux ces derniers mois...).

Comment améliorer cela selon vous ?

  • Permettre via 1 paramètre dans la config du script de récupération des médias de choisir entre stocker les URL (comportement actuel) ou télécharger les médias en dur sur son serveur et faire le lien dans t_medias ?
    C'est quand même dommage de stocker chacun chez soi des médias déjà hébergés (et accessibles ailleurs)... En même temps, pour des applications grand public, c'est aussi embêtant que certaines infos ne remontent plus sans que l'on ne soit forcément informés (pour l'IGN il y a le quand même le blog GéoServices qui fait état des incidents "presque" en temps réel).
  • Pour limiter le fait de stocker tous les médias de tous les taxons de son territoire sur son serveur, peut-être que l'alternative serait de ne télécharger chez soi qu'un seul média par taxon en lui attribuant le type "Photo principale" et les autres médias via URL afin d'avoir toujours les imagettes et au moins 1 photo sur chaque espèce quand les services tiers sont down ?
  • Ou encore, mettre en place des contrôles de réponses des URL et API ? Si des erreurs de tels ou tels types sont levées, alors on affiche autre chose... ? J'y avais pensé pour les services IGN quand ils étaient vraiment capricieux (chez nous c'est le fond de cartes par défaut). Exemple : si les tuiles renvoyées pèsent moins de tant de Ko alors j'affiche un fond OSM...

Bon c'est sûr que l'idéal c'est que tous les webservices qu'on utilisent fonctionnent toujours au top (redondances, délais de rétablissement très rapide etc.). Ce qui est déjà pas loin d'être le cas en passant. Mais il y a forcément des couacs de temps en temps...
Ou encore d'avoir sa propre photothèque avec des belles photos pour l'ensemble des taxons de son territoire... et puis de créer ses propres géoservices de fonds de cartes via un serveur carto... mais ce pas toujours évident/souhaitable :)

Merci pour vos retours,

@camillemonchicourt
Copy link
Member Author

En effet les médias ne sont pas stockés. Je pense que c'est bien de faire comme ça, mais un paramètre pour laisser le choix à chacun serait une évolution intéressante.

Par contre on a maintenant un mécanisme de génération dynamique de vignettes et celles-ci sont stockés en cache sur le serveur après avoir été appelés une fois.
Du coup, toutes les images des listes et de pages détail sont alors utilisées localement sur le serveur TaxHub, sans dépendance au serveur distant.

Pour toutes les avoir directement, @amandine-sahl a pré-généré les vignettes en faisant une boucle de wget.

Ajouter un test qui affiche une autre image par défaut est intéressant aussi.

@DonovanMaillard
Copy link
Contributor

Pour info, niveau maintenance, l'API inpn a été réparé en quelques heures après leur avoir fait remonter l'information.

Mais en effet, un paramètre serait intéressant pour pouvoir stocker en local soi rien, soit le média principal, soit tous les médias. A voir pour la suite :)

@jpm-cbna
Copy link
Contributor

jpm-cbna commented Sep 2, 2020

@RomainBaghi je pense que l'erreur en question venait du fait que l'API renvoyait un code HTTP qui n'était pas 200 et donc une réponse vide. J'ai eu le cas avec un appel à l'API qui renvoyait le code 404 (not found). J'ai modifié le code pour éviter ce problème dans la pull request #240 .

J'en ai profité pour compléter le README du script en ajoutant la proposition de requête de @DonovanMaillard pour sélectionner une photo principale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants