"Je dois exploiter le résultats JSON des API suivantes :
1 - https://offline.turfinfo.api.pmu.fr/...amme/24112023/
2 - https://offline.turfinfo.api.pmu.fr/...e/24112023/R1/
3 - https://offline.turfinfo.api.pmu.fr/...4112023/R1/C1/
4 - https://offline.turfinfo.api.pmu.fr/...1/participants
comment dois-je m'y prendre ?"
Dans une premier temps on oublie les variants pour traiter le résultat d'une API, surtout si l'on doit alimenter sa propre BDD. Si, pour une raison ou pour une autre, le format du résultat change, tout ton boulot sera à jeter. Par ailleurs, si le résultat est en binaire, il n'est pas exploitable directement. Enfin, en jetant un oeil sur le résultat du premier lien, on voit que nous avons un JSON de plus de 15000 ligne. Bon courage pour faire exploiter ça avec des boucles.
Il faut penser sérialisation/désérialisation.
1-La sérialisation, qu'est ce c'est ?
La sérialisation, la littérature parle aussi de marshalling, est une opération qui permet de transformer des données structurées qui se trouvent en mémoires (le résultat d'une requête sur un serveur par exemple) en un buffer qui sera transmis sur le réseau ou stocké. Les formats les plus couramment utilisés sont le JSON, le XML et le binaire, mais on n'est pas à l'abri d'un format maison qui devienne un standard.
A l'autre bout du réseau, l'utilisateur devra faire l'opération inverse, la désérialisation, pour obtenir en mémoires des données structurées facilement exploitables.
2- Préparation de la désérialisation
2.1-Exploiter le résultat
Le résultat obtenu correspond à des données structurées reprises, la plupart du temps, dans la doc de l'API. Il arrive toutefois, que ce ne soit pas le cas. Si l'on connaît le XML ou le JSON, on peut retrouver las structures "à la main", mais pour un gros volume de données, cela peut s'avérer fastidieux, voire impossible en cas. Il existe de nombreux outils en ligne qui permettent de nous mâcher le boulot. Dans la suite, je me baserai sur le lien 1 (https://offline.turfinfo.api.pmu.fr/...amme/24112023/) pour illustrer mes propos.
J'utilise l'outils PCSOFT WJSON qui permet d'obtenir les structures, mais aussi les classe utilisées.
2.2-Récupérer les classes ou les structures
En ce qui concerne les structures, un copier/coller fait l'affaire, en ce qui concerne les classes, c'est légèrement plus subtil.
En plaçant le curseur de la souris sur la colonne de droite (la colonne de résultats,) 2 boutons apparaissent :
-le bouton copier (à droite)
-le bouton importer les classes.
L'importation des classes permet de télécharger un .zip qui contient la déclaration des classes en .txt. Pour les incorporer dans le projet, il suffit, dans l'explorateur, de faire un clic droit sur Classe et de sélectionner "Importer des fichiers txt" (la sélection multiple est possible)
2.3-Classe ou structure ?
Désérialise accepte indifféremment les classes ou les structures, quand est-il préférable d'utilser tel ou tel type?
Si c'est du "one shot" et que les manipulation des données sont restreintes e.g. récupération ponctuelle de données il est préférable d'utiliser les structures.
Si ces données doivent êtres stockées et a fortiori, si l'on veut travailler en objet (e.g. alimentation de champs par data binding,) il faut utiliser les classes.
Le WL met à notre disposition 2 attributs d'extension qui vont nous simplifier la vie surtout si l'on ne veut pas utiliser les noms que nous impose le JSON.
2.4-Conventions
2.4.1-Conventions concernant les noms de membre
En ce qui concerne le préfixage, j'utilise les conventions de la charte standard du WL. Un membre est préfixé par m_y ou y est le préfixe du type du membre e.g. m_nMonMembre représente un entier. Toutefois pour les entier sur 8 j'utilise m_pk, pour les chaînes ANSI, m_sa et pour les chaînes UNICODE m_su.(Cela est facilement modifiable dans l'éditeur de charte)
2.4.2-Convention concernant le nom des colonnes
-PK_ : clé primaire
-FK_ : clé étrangère
-NDX_ : Index
-AK_ : clé alternative
-CC_ : clé composée.
2.5-Modification des classes obtenues
2.5.1 Adaptation de la déclaration aux conventions des noms de membre
Je me limiterai à la classe CProgramme.
En étudiant le code
Code windev : | Sélectionner tout |
1 2 3 4 5 6 7 8 | CProgramme est une Classe cached est un booléen 'date' est un numérique timezoneOffset est un numérique reunions est un tableau de Creunions datesProgrammesDisponibles est un tableau de chaînes prochainesCoursesAPartir est un tableau de chaînes // Tableau vide, type indéfini FIN |
Code Windev : | Sélectionner tout |
1 2 3 4 5 6 7 8 | CProgramme est une Classe m_bCached est un booléen <Sérialise="cached"> //Attention à la casse m_pkDate est entier sur 8 <Sérialise="date"> //Format TimeStamp Bon candidat pour une PK m_xTimeZoneOffset est un entier <Sérialise="timezoneOffset"> m_tabReunions est un tableau <Sérialise="reunion"> de Creunions //Attention à la place de l'attribut m_tabDatesProgrammesDisponibles est un tableau <Sérialise="datesProgrammesDisponibles"> de chaîne //Chaînes au format JJMMAAAA m_tabProchainesCoursesAPartir est un tableau <Sérialise="prochainesCoursesAPartir">de chaînes // Tableau vide, type indéfini FIN |
2.5.2-De la classe vers la table
On a la structure qui va recevoir le JSON en mémoire. Une fois les données récupérées, on peut déjà les utiliser telle quelle en liant les champs de notre fenêtre aux membres d'une variable de type classe, globale au minimum à cette fenêtre.
Toutefois, si l'on veut conserver les données récupérées, il va falloir créer des tables capables de récupérer ces données.
Certains AGL (e.g. AMC Designor) font cela automatiquement. Cela n'est pas le cas de windev. Cela n'a jamais été la grande histoire d'amour entre windev et la POO. On va donc devoir tout se taper à la main.
La méthode est simple
*-Chaque classe correspond à une table.
*-On ne conserve que les données atomiques (i.e. non structurées)
*-On cherche un candidat à la PK. S'il n'y en a pas, on en crée un. Rien de neuf par rapport à une création "classique"
On obtient donc la table suivante :
Remarque : Le 12/02/2051 aura-t-on besoin de savoir que le 01/12/2023 on pouvait avoir les programmes jusqu'au 07/12/2023 ? En français, La colonne DateProgrammesDisponnibles est elle vraiment utile ? C'est une chose à voir avec le pilote pour modifier les règles de gestion. Pour des raisons didactiques nous dirons que cette colonne n'est pas utile.
Une nouvelle question se pose à nous : Que fait-on des données structurées ?
Ces données vont se transformer en liaison entre 2 tables :
- *,1 pour les structures simples e.g. Réunions->Hippodromme
- *,n pour les tableau de structures e.g. Programme->Réunions
Là encore, si dans la table secondaire on a un candidat pour devenir FK (e.g. DateRéunion dans Réunions) on l'utilise, sinon on crée notre FK ex nihilo.
Nos tables sont modélisées nous pouvons passer à l'étape suivante.
Voici un extrait du MLD.
2.5.3-Adaptation de la déclaration aux conventions des noms de colonnes
Tout comme nous avons l'attribut sérialise pour passer du résultat d'une API à l'instance d'une classe, il existe un attribut pour récupérer sans douleurs les données de l'instance d'une classe dans une table, cet attribut s'appelle mapping.
La déclaration de la classe devient alors :
Code windev : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | CProgramme est une Classe <MAPPING=Programme> <MAPPING> m_bCached est un booléen <Sérialise="cached",mapping=Cached> //Attention à la casse m_pkDate est un entier sur 8 octets <Sérialise="date",mapping=PK_Date,clé unique> m_nTimeZoneOffset est un entier <Sérialise="timezoneOffset",mapping=TimeZoneOffset> <FIN> //m_tabReunions et m_tabProchainesCoursesAPartir ne sont pas atomiques donc pas dans la table, mapping inutile m_tabReunions est un tableau <Sérialise="reunions"> de CReunions //Attention à la place de l'attribut m_tabProchainesCoursesAPartir est un tableau <Sérialise="prochainesCoursesAPartir">de CCourses //m_tabDatesProgrammesDisponibles n'est pas dans la table, mapping inutile m_tabDatesProgrammesDisponibles est un tableau <Sérialise="datesProgrammesDisponibles"> de chaînes FIN |
PRIVE ou pas ?
C'est toujours la grande question. Les puristes diront :"Ce sont des classes donc les membres sont au minimum PROTEGE". Ils ont raison, SI on va manipuler nos classes via la POO et si on va les partager.
Si les classes créées ne servent que de réceptacle, ce n'est pas vraiment utile.
Nos classes sont maintenant prêtes à recevoir le résultat de l'API.
3-Récupération des données
Le code est simple (enfin presque.)
3.1-Code principal
Code Windev : | Sélectionner tout |
1 2 3 4 5 6 7 8 | clRésultats est CResult //Récupérer les données clRésultats:LireDepuisAPI("https://offline.turfinfo.api.pmu.fr/rest/client/7/programme/24112023/") //Au besoin sauvegarde des données récupérées clResultats:Sauver() |
Code Windev : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | PROCEDURE RécupérerRésultats(saURL est chaîne) oMaRequête est un httpRequête oMaRéponse est un httpRéponse oMaRequête.URL = saURL oMaRéponse = HTTPEnvoie(oMaRequête) Désérialise(objet,oMaRéponse..Contenu,psdJSON) |
3.2-Sauvegarde des instances de classe
3.2.1-Principe
*-Récupération des données atomiques via MémoireVersFichier
*-Récupération éventuelle des valeurs des FK (si la PK associée a été crée ex nihilo)
*-Sauvegarde des instances contenues dans l'instance en cours (e.g. sauvegarde des réunions d'un programme) cf 3.2.1
3.2.2-Application à notre cas
Sauver (CProgramme)
Code Windev : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | PROCÉDURE Sauver() clUneRéunion est CReunions //Sauvegarde des données atomiques MémoireVersFichier(objet,Programme) //Je n'utilise pas HEnregistre car la fonction ne déclenche pas les triggers SI PAS HLitRecherchePremier(Programme,PK_Date,:m_pkDate) ALORS HAjoute(Programme) SINON HModifie(Programme) FIN //Sauvegarde des structures POUR TOUT clUneRéunion DE :m_tabReunions clUneRéunion:Sauver() FIN //etc |