Closed alhyss closed 2 years ago
Rapidement à la première lecture 👍
Remarques :
PLUME
dans le QGIS3.INI
"Enregistrer la configuration dans les métadonnées"
, oui, mais pour sauvegarder le choix des trois options d'import ? ou autre choseCette option devra-t-elle faire partie de la première version diffusée ?
- Je pourrais sauvegarder les informations dans la section de
PLUME
dans leQGIS3.INI
Oui, tout à fait, le paramètre utilisateur urlCsw
serait à sauvegarder dans QGIS3.ini.
- Icone avec petite flèche ou faire une autre icone pour les imports CSW, ça se discute, moi j'aime bien les deux
Je dirais que c'est préférable avec un menu :
"Enregistrer la configuration dans les métadonnées"
, oui, mais pour sauvegarder le choix des trois options d'import ? ou autre chose
En fait je n'avais même pas prévu de sauvegarder ce paramètre-là :D (à voir, ce serait possible en créant une catégorie de métadonnées spécifique, mais je ne sais pas si c'est utile). Non, ce que je veux stocker, c'est l'URL du CSW du catalogue où se trouvent les métadonnées de la table, ainsi que l'identifiant de la fiche, pour éviter à l'utilisateur de devoir retourner chercher ces informations à chaque fois (spécialement l'identifiant de la fiche). Je me rends compte que j'ai oublié de l'écrire, mais c'est la fonction plume.rdf.metagraph.metagraph_from_iso
qui se charge de ça.
- Il faudrait aussi pouvoir supprimer des url créées non utilisées ou pouvoir les modifier ?
Pas nécessairement via cette boîte de dialogue-là. Je pense que ça devrait plutôt se faire dans ta boîte de dialogue de gestion de la configuration utilisateur. Si on commence à gérer la configuration à plusieurs endroits, j'ai peur que ça devienne vite illisible...
L'ajout à la liste de nouvelles URL pourrait d'ailleurs se faire aussi exclusivement dans la boîte de dialogue de gestion de la configuration. L'essentiel est que, dans l'interface d'import, l'utilisateur ait la possibilité d'utiliser des URL qui ne soient pas dans la liste, à voir si on lui permet ensuite (s'il le juge opportun) de sauvegarder son URL en cliquant sur un bouton ou s'il faut qu'il ouvre la boîte de dialogue du paramétrage pour le faire. Je n'ai pas de préférence a priori, d'autant qu'on parle d'une action très peu fréquente.
Cette option devra-t-elle faire partie de la première version diffusée ?
Pour moi oui. Ça va immédiatement rendre Plume beaucoup plus intéressant pour les services et, à l'inverse, diffuser une version qui ne proposerait pas ça risque de rebuter d'entrée. Ce serait aussi idiot que des services commencent à faire des copier-coller de métadonnées à la main, alors qu'on a prévu à court terme un mécanisme qui va leur faire gagner énormément de temps.
OK, Bon, beh tu te rends compte qu'il y a du taf. Mais vu le contexte promotionnel, je ferais comme je peux, tu es d'accord, sinon c'est via ... 👎
Je m'en rends compte.
Aucune urgence pour l'implémentation. Autant il serait souhaitable que ce soit là pour la première version officiellement mise en production, autant on peut faire sans pour la première version diffusée aux membres du GT. Et ce n'est pas non plus comme si tout était prêt de mon côté, loin de là.
Juste pour faire un test dans qgis
Et ça passe sans vpn dans la console, encore du temps à comprendre le pourquoi du comment
Fonctionne SANS VPN avec MetaSearch
Les requêtes qu'on va envoyer ont cette forme : http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable?service=CSW&REQUEST=GetRecordById&version=2.0.2&namespace=xmlns%3Acsw%3Dhttp%3A%2F%2Fwww.opengis.net%2Fcat%2Fcsw&outputFormat=application%2Fxml&outputSchema=http%3A%2F%2Fwww.isotc211.org%2F2005%2Fgmd&ElementSetName=full&Id=fr-120066022-jdd-23d6b4cd-5a3b-4e10-83ae-d8fdad9b04ab
Celle-ci est le résultat de :
from plume.iso.csw import getrecordbyid_request
request = getrecordbyid_request(
'http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable',
'fr-120066022-jdd-23d6b4cd-5a3b-4e10-83ae-d8fdad9b04ab'
)
Edit: L'idéal serait d'avoir un bête équivalent à urllib.request.urlopen
, mais où QGIS gère le proxy lui-même.
Pour les requêtes http en python sous QGIS la réponse décrite sur : https://gis.stackexchange.com/questions/343126/performing-sync-or-async-network-request-in-pyqgis me paraît toujours pertinente. Sinon pour répondre à Didier (cf plus haut) : WCS ≠ CSW... Bonne fêtes de fin d'année
Bonnes année 2022 à vous deux. Moi j'ai la reconstruction de ma maison, ce qui n'est pas une mince affaire.
Bjr Alain, Oui, je sais mais pourquoi cette remarque ? C'est le serveur qui me donne cette réponse, pas moi. Ensuite le lien que tu donnes peut servir, mais c'est pour le téléchargement avec authentification ? Si tu as des pistes pour chuinter le VPN pas de soucis, je compte sur toi !
Dans ta capture d'écran tu as comme message 'impossible d'obtenir les spécifications du serveur WCS' (Web Coverage Service)...et dans le lien on voit que tu lui passe une adresse de serveur CSW. Je suppose donc qu'il y a une petite confusion ? Pour le lien, peut-être que je n'ai pas compris la remarque de Leslie demandant un urlopen avec le proxy géré par QGIS. Je n'ai pas vu de référence au VPN dans les dialogues précédents... Donc non je n'ai pas de piste pour shunter le VPN si celui-ci est actif sur le poste au moment de la requête....
OK Alain, nous sommes d'accord
Bonne année à tous les deux !
Merci @FERRATON pour les infos. Tu as peut-être fait plus de tests, @WREATCHED , mais QgsNetworkContentFetcher
m'a l'air de correspondre à ce que j'avais en tête ?
from PyQt5.QtCore import QUrl
from qgis.core import QgsNetworkContentFetcher
from plume.iso.csw import getrecordbyid_request
request = getrecordbyid_request(
'http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable',
'fr-120066022-jdd-23d6b4cd-5a3b-4e10-83ae-d8fdad9b04ab'
)
url = QUrl(request)
fetcher = QgsNetworkContentFetcher()
fetcher.fetchContent(url)
raw_xml = fetcher.contentAsString()
J'ai testé avec et sans VPN, ça semble fonctionner.
Si cette classe est bien la bonne, il y aura la question de la gestion des erreurs. Visiblement contentAsString
renvoie une chaîne de caractères vide quand le serveur retourne une erreur, et si tu donnes ça en argument à mes fonctions, ça produira un formulaire vide, ce qui ne serait pas catastrophique. Sinon il doit être possible d'utiliser QgsNetworkContentFetcher.reply
pour générer un message d'erreur... Comme tu préfères, @WREATCHED .
J'ai continué à implémenter de mon côté, sinon.
Une fois que tu as récupéré le XML (le raw_xml
de mon message précédent), il faudra le passer à la fonction plume.rdf.metagraph_from_iso
pour récupérer le nouveau graphe de métadonnées.
from plume.rdf.metagraph import metagraph_from_iso
metagraph = metagraph_from_iso(raw_xml, old_metagraph)
Ça marche comme clean_metagraph
. old_metagraph
est le graphe de métadonnées - metagraph
- actuel. Tu peux l'utiliser dès à présent. C'est dans mes nouveaux scripts, mais ça reste compatible avec la vieille fonction rdf_utils.build_dict
.
Les paramètres - URL du serveur CSW et identifiant de la fiche - seront gérés via la propriété linked_record
du graphe. NB. Remplace ce que j'avais appelé csw_parameters
dans mon tout premier message de cette issue.
Pour récupérer les paramètres sauvegardés :
url_csw, file_identifier = metagraph.linked_record
Pour sauvegarder de nouveaux paramètres (si l'utilisateur ne décoche pas la case Enregistrer la configuration dans les métadonnées
) :
metagraph.linked_record = url_csw, file_identifier
Cette propriété n'existe qu'avec la nouvelle classe Metagraph
, donc tu ne peux pas encore l'appeler dans tes scripts, mais si tu veux voir ce que ça donne :
>>> from plume.rdf.metagraph import Metagraph
>>> metagraph = Metagraph()
>>> metagraph.datasetid = None
>>> metagraph.linked_record = 'http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable', 'fr-120066022-jdd-d3d794eb-76ba-450a-9f03-6eb84662f297'
>>> metagraph.linked_record
('http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable', 'fr-120066022-jdd-d3d794eb-76ba-450a-9f03-6eb84662f297')
Je vais mettre tout ça dans la doc, évidemment.
Super, mais Bon, moi ça n'a pas fonctionné en l'état. J'ai du rajouté les évènements de "finsihed" Donc pour tests dans la console (sans les appels) si vous voulez essayez, l'ensemble des codes LL et DL
from urllib.parse import urlencode, urljoin
from urllib import request
from PyQt5.QtCore import QUrl
from qgis.core import QgsNetworkContentFetcher
#==================================================
"""Utilitaires pour le dialogue avec les services CSW des catalogues.
"""
def getrecordbyid_request(url_csw, file_identifier):
"""Crée une requête GetRecordById pour envoi en HTTP GET.
Parameters
----------
url_csw : str
L'URL de base du service CSW du catalogue, sans aucun paramètre.
file_identifier : str
L'identifiant de la fiche de métadonnées sur le catalogue.
Correspond à la valeur de la balise ``gmd:fileIdentifier``
des fiches ISO 19139.
Returns
-------
str
Examples
--------
>>> r = getrecordbyid_request(
... 'http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable',
... 'fr-120066022-jdd-d3d794eb-76ba-450a-9f03-6eb84662f297'
... )
>>> from urllib.request import urlopen
>>> with urlopen(r) as src:
... xml = src.read()
"""
url_csw = url_csw.rstrip('?/')
config = {
'service' : 'CSW',
'REQUEST': 'GetRecordById',
'version': '2.0.2',
'namespace': 'xmlns:csw=http://www.opengis.net/cat/csw',
'outputFormat': 'application/xml',
'outputSchema': 'http://www.isotc211.org/2005/gmd',
'ElementSetName': 'full',
'Id': file_identifier
}
data = urlencode(config)
return '{}?{}'.format(url_csw, data)
#==================================================
def mainGeoideID_OK(urlCatalogue, nameCatalogue, idSet) :
print("\n====== {} ======".format(str(nameCatalogue)))
resultQueryId = getrecordbyid_request(urlCatalogue, idSet)
url = QUrl(resultQueryId)
fetcher = QgsNetworkContentFetcher()
fetcher.fetchContent(url)
#-
evloop = QEventLoop()
fetcher.finished.connect(evloop.quit)
evloop.exec_(QEventLoop.ExcludeUserInputEvents)
fetcher.finished.disconnect(evloop.quit)
#-
raw_xml = fetcher.contentAsString()
print("raw_xml {} ".format(str(raw_xml)))
#-
ret = fetcher.reply()
print("fetcher.reply() {} ".format(str(ret)))
#==================================================
urlGEOIDE, nameGEOIDE = "http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable", "Catalogage GEOIDE"
mainGeoideID_OK(urlGEOIDE, nameGEOIDE, "fr-120066022-jdd-23d6b4cd-5a3b-4e10-83ae-d8fdad9b04ab")
#==================================================
Par contre, cela fonctionne de chez moi avec ou sans vpn, c'est cool J'attaque le code demain
Merci @WREATCHED ! ça nous fait une très bonne base.
Quelques remarques en vrac :
Importer depuis un fichier
et Importer depuis un service CSW
(en ajoutant explicitement "Importer", donc).Enregistrer la configuration dans les métadonnées
, mais peut-être que tu ne l'avais simplement pas encore mise ?URL du CSW
en label, avec URL du service CSW, sans aucun paramètre.
en infobulle, et peut-être http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable
en placeholder pour que ce soit bien clair ?Identifiant de la fiche de métadonnées
en label ? Infobulle : Cet identifiant, à ne pas confondre avec l'identifiant de la ressource, apparaît généralement dans l'URL de la fiche sur le catalogue. Il s'agit de la propriété gmd:fileIdentifier des XML.
(je suis preneuse, si tu as une meilleure manière pour expliquer comment trouver l'identifiant). Placeholder : fr-120066022-jdd-a307d028-d9d2-4605-a1e5-8d31bc573bef
.Mode d'import
?QGIS3.ini
, seules les URL des CSW peuvent l'être (pour éviter d'avoir à resaisir trente fois la même URL quand on veut récupérer trente fiches d'un même catalogue). Par contre, sous réserve de ne pas décocher Enregistrer la configuration dans les métadonnées
, l'URL du CSW et l'identifiant utilisés pour générés une fiche de métadonnées PostgreSQL seront enregistrés dans celle-ci, afin de faciliter d'éventuelles futures mises à jour (via la propriété Metagraph.linked_record
que j'évoquais précédemment). Désolée si je n'étais pas claire là-dessus.Ok, bien reçu J'ai donc du travail ........
C'est super !
Merci d'avoir là aussi rendu paramétrable la couleur des cadres, ça va nous épargner de grands débats ^^.
Quelques micro-remarques :
Importer depuis un fichier
et Importer depuis un service CSW
du menu ?QGIS3.ini
s'ajoutent plutôt en fin de liste ? Il y a quand même des chances que celles que l'utilisateur mettra en premier soient celles qu'il sera amené à utiliser le plus et ça justifierait de les garder au début.URL du CSW
. D'ailleurs, pour le titre du tableau, est-ce qu'on pourrait imaginer URL de CSW mémorisées
ou quelque chose de ce genre ? Il me semble que ça aiderait à comprendre le mécanisme.QGIS3.ini
pourrait être à droite du champ URL du CSW
? Le fait qu'il soit en dessous de l'identifiant pourrait laisser entendre que ce dernier va être sauvegardé également (même si ce n'est pas ce que suggère l'infobulle, on est d'accord). Je me dis aussi qu'une disquette serait peut-être plus parlante qu'un bouton plus ? Tu pourrais réutiliser celle du menu, le fichier plume/icons/general/save.svg ?plume/icons/general
: delete.svg (en reprenant celle d'Asgard).Merci @WREATCHED. C'est parfait ! Comme tu dis, c'est vraiment la cerise sur le gâteau de pouvoir réordonner les URL. Je n'osais pas en demander tant, mais c'est super que tu l'aies fait.
Salut @WREATCHED,
Comme prévu, j'ai modifié plume.rdf.metagraph.metagraph_from_iso
, pour qu'elle prenne un argument supplémentaire preserve
, correspondant au mode de fusion des informations du catalogue distant et de la fiche locale dans laquelle on réalise l'import.
Pour la correspondance avec les cases à cocher :
Mettre à jour avec les métadonnées distantes
: valeur 'if blank'
.Compléter avec les métadonnées distantes
: valeur 'always'
(défaut).Remplacer par les métadonnées distantes
: valeur 'never'
.Code
mOptions = "if blank" if self.option1.isChecked() : # Mettre à jour avec les métadonnées distantes : valeur 'if blank'.
mOptions = "if blank"
elif self.option1.isChecked() : # Compléter avec les métadonnées distantes : valeur 'always' (défaut).
mOptions = "always"
elif self.option1.isChecked() : # Remplacer par les métadonnées distantes : valeur 'never'.
mOptions = "never"
Faut-il mettre à l'ouverture de la boite de dialogue l'option 2 activée puisque c'est l'option par défaut ?
#options d'imports
mOptions = "always"
if self.option1.isChecked() : # Compléter avec les métadonnées distantes : valeur 'always' (défaut).
mOptions = "always"
elif self.option2.isChecked() : # Mettre à jour avec les métadonnées distantes : valeur 'if blank'.
mOptions = "if blank"
elif self.option3.isChecked() : # Remplacer par les métadonnées distantes : valeur 'never'.
mOptions = "never"
C'est bien ça !
Pour échanger sur la manière dont on voit les choses avant d'implémenter chacun les parties qui nous occupent.
Je pense qu'il faut prévoir un nouveau paramètre utilisateur
urlCsw
, contenant une liste d'URL de base de CSW saisies par l'utilisateur. Par défaut on pourrait mettre l'URL du CSW de GéoIDE (http://ogc.geo-ide.developpement-durable.gouv.fr/csw/dataset-harvestable).Telles que je vois les choses - mais ça et tout ce qui suit est totalement discutable - l'icône d'import de la barre d'outils se parerait d'une petite flèche donnant accès à un menu qui contiendrait à ce stade deux items :
depuis un fichier
- déjà implémenté - etdepuis un service CSW
(nom pas nécessairement définitif), qui est donc à implémenter.Lorsque l'utilisateur clique sur
Importer > depuis un service CSW
, une boîte de dialogue ou équivalent s'ouvre.Deux boutons d'actions en bas :
Importer
etAnnuler
.Deux champs de saisie :
urlCsw
, mais aussi de saisir de nouvelles URL ;Il serait intéressant que le bouton
Importer
soit désactivé quand l'un des deux champs est vide.Lorsque l'utilisateur a saisi une nouvelle URL de CSW, il serait bien de lui permettre de la sauvegarder dans
urlCsw
.Je préciserai ça quand je vais concrètement implémenter le mapping des métadonnées INSPIRE, mais j'imagine que l'utilisateur pourrait choisir entre trois modes d'import :
Mettre à jour avec les métadonnées distantes
(valeur par défaut) : pour toutes les catégories de métadonnées renseignées dans la fiche du catalogue, la valeur de la fiche locale est remplacée par celle du catalogue ou ajoutée s'il n'y avait pas de valeur.Compléter avec les métadonnées distantes
: pour toutes les catégories de métadonnées renseignées dans la fiche du catalogue qui n'ont pas de valeur dans la fiche locale, la valeur de la fiche distante est ajoutée.Remplacer par les métadonnées distantes
: génère une nouvelle fiche à partir des métadonnées distantes. La différence avec l'option 1 est que les informations des catégories non renseignées dans la fiche du catalogue mais qui auraient pu exister en local vont être effacées.Il y aurait aussi une option
Enregistrer la configuration dans les métadonnées
qui pourrait prendre la forme d'une case à cocher cochée par défaut.À l'ouverture de la boîte de dialogue, tu pourras exécuter
plume.rdf.metagraph.Metagraph.csw_parameters
sur le graphe de métadonnées courant (pas d'autre argument queself
), qui te renverra un tuple avec l'URL du CSW et l'identifiant de la fiche si une configuration avait préalablement été mémorisée, etNone
sinon. Cela permettra de pré-remplir les deux champs de configuration. À défaut, le champ d'URL prendra la première valeur de la liste d'urlCsw
et le champ d'identifiant restera vide.Quand l'utilisateur active l'import, on procède en trois temps :
plume.iso.csw.getrecordbyid_request
(prend en argument l'URL du CSW et l'identifiant de la fiche), qui te donne la requête à envoyer en HTTP GET. J'ai écrit la fonction ce matin, comme ça tu l'auras pour tes tests ;plume.rdf.metagraph.metagraph_from_iso
(prendra en argument l'URL du CSW, l'identifiant de la fiche, le résultat de la requête, le mode de fusion des fiches et le booléen disant si la configuration de moissonnage doit être sauvegardée dans les métadonnées). Elle renvoie un nouveau graphe de métadonnées, à partir duquel tu pourras générer un nouveau dictionnaire de wigdets, etc.Qu'en penses-tu ?
PS: Ne cherche pas les fonctions
csw_parameters
etmetagraph_from_iso
dans mon code, je ne les ai pas encore écrites.