$\quad\quad$Une association de découverte et protection de l'environnement, hébergée par une université propose à ses adhérents des sorties thématiques sur la faune et la flore locale et les milieux naturels.
Dans ce rapport, nous détaillerons les différentes étapes de la conception de ce site ainsi que les différentes idées et concepts l'accompagnant.
L'idée fondatrice est d'avoir une interface afin de communiquer des sorties pour les adhérents de ce club mais aussi, il dispose d'une portée plus pratique car il est question de créer une véritable encyclopédie de la faune locale selon un concept collaboratif. Le concept clef orientant la conception du site est d'avoir des articles dynamiques à la manière de Wikipédia où chaque information de la page est affichée selon sa pertinence jusqu'à l'ordonnancement des paragraphes.
Il sera question en première partie de la conception de la base de données ainsi que la génération des données qu'elle comporte puis en deuxième partie nous traiterons les difficultés rencontrées et les ambitions inachevées.
Le site web a été conçu à l'aide de la framework ReactJS ainsi que Python + Flask pour le backend.
À noter que ce projet a été passionnant tant sur la réalisation que la conception initiale et les choix qu'il comporte (notamment visuels). Ils sont le fruit de longues réflexions dont notamment le logo, dessiné à la main reprenant celui de l'université avec un colibri sur une fleur. Le tableau choisi pour la page d'accueil de la version ordinateur est Paysage aux deux chênes. Jan Van Goyen s'alignant avec le thème du site. De l'ambition en découlait naturellement avec un nombre extensif de données factices disponibles (plus de 300 sorties, 250 espèces répertoriées et plus de 150 habitats et une centaine d'utilisateurs). Le site est disponible en français comme en anglais. En thème sombre comme en thème clair (pensé pour être tout aussi agréable avec du beige en couleur principale).
Quelques créations originales pour l'interface y sont présentées dont PagesFlow, le component pour afficher les numéros de pages basé sur une « borne compensante » mais aussi le Biocodex, pensé pour créer de l'harmonie dans ce qui initialement ne devait pas en avoir. Il s'assimile à une page structurée avec des sections mais en réalité se compose de plusieurs dizaines de petits posts classifiés par catégorie et l'ajout de post passe par la « Floating Textarea », de nouveau un concept original permettant de changer de section sans vous soucier de copier votre texte.
Ce rapport n'est néanmoins pas pensé pour présenter le site en lui-même mais plus le déroulé du projet et ses choix techniques plutôt que créatifs.
Une fois l'archive téléchargée, (supposant psql installé) lancez successivement:
createdb arlpsql -d arl -v -f db/dump.sqlpython3 -m venv .venvpip install -r requirements.txtpython3 main.pyCertains comptes sont mis à votre disposition pour des tests, disponibles dans le fichieruser-tests.mdlouis_marin:Ader106!pour un compte adminbenchbadr:Lavoisier25!sinon
Si vous êtes administrateur, vous pouvez accéder à la page dédiée en saisissant /admin dans l'URL. Elle vous permet d'effectuer de nombreuses actions dont créer des sorties, en supprimer, créer des espèces... etc. Le statut d'administrateur est déterminer par le statut "bureau" de l'adhérent (cf. ajouter adhérent).
Le reste est assez intuitif à découvrir
Pour toute question additionnelle merci de nous contacter sur discord, nous restons à votre disposition à cet effet : @benchbadr.
- Statut(IdStatut, libelle_statut)
- Habitat(IdHabitat, nomHabitat)
- Etre_vivant(IdEspece, nom, taille, groupe)
- Profil(IdProfil, prenom, nom, pw_hash)
- Attribut(IdAtt, nom, txt_descriptif, #IdEspece, #auteur)
- FK idEspece
$\to$ Etre_vivant(idEspece) - FK auteur
$\to$ Adherent(num)
- FK idEspece
- Adherent(num, #IdProfil, #special, #statut, XP)
- FK idProfil
$\to$ Profil(idProfil) - FK special
$\to$ Specialite(libelle) - FK statut
$\to$ Statut(idStatut)
- FK idProfil
- Cotisation_Inscription(IdPaiement, mode_paiement, montant, date, #num)
- FK num
$\to$ Adherent(num)
- FK num
- Sortie(IdSortie, nom, theme, lieu, date_rdv, distance_km, effectif_max, descriptif)
- Anime(#idProfil, #idSortie)
- FK idProfil
$\to$ Profil(idProfil) - FK idSortie
$\to$ Sortie(idSortie)
- FK idProfil
- Info_Habitat(IdInfoHab, #habitat, type_info, information, #auteur)
- FK habitat
$\to$ Habitat(idHabitat) - FK auteur
$\to$ Profil(idProfil)
- FK habitat
- Observe(#num, #IdEspece, #lieu, date, lieu, remarques, img)
- FK num
$\to$ Adherent(num) - FK idEspece
$\to$ Etre_vivant(idEspece) - FK lieu
$\to$ Habitat(idHabitat)
- FK num
- Renseigne(#IdProfil, #IdAtt, date, commit_msg)
- FK idProfil
$\to$ Profil(idProfil) - FK idAtt
$\to$ Attribut(idAtt)
- FK idProfil
- Cordonnee(idCoord, #profil, type_coord, coordonnee)
- FK profil
$\to$ Profil(idProfil)
- FK profil
- Inscription(#num, #idSortie)
- FK num
$\to$ Adherent(num) - FK idSortie
$\to$ Sortie(idSortie)
- FK num
- Specialise(#num, libelle)
- FK num
$\to$ Adherent(num)
- FK num
Après le schéma relationnel précédent, la suite semble évidente mais nous prendrons soin de détailler l'utilité et l'usage des tables.
- Tables indépendantes - À savoir les tables sans références externes
Statut: Permet de stocker les différents status possibles en leur associant un ID en prévision d'un éventuel renommage de statut.Habitat: associe ID / Nom habitat pour avoir des références constantes. Cette table englobe le statut éventuel de nichoir par l'intérmediaire de la table info_habitat.Etre_vivant: décrit une espèce, nom et relie à différents attributs. Nommé ainsi pour éviter la moindre ambiguïté sur les inclusions, les plantes étant des êtres vivantsProfil: Comme son nom l'indique, stocke un profil individuel indépendant du statut d'adhérent.
- Tables dépendantes - Avec références externes
Attribut: Attribut associé à un être vivant (eg. Forme des ailes, Taille, Couleur, Courbature des griffe, Densité du pelage...). Attributs propres à chaque espèce mais on utilisera des noms d'attributs similaires dans la mesure du possible pour permettre par la suite de filtrer les espèces stockées. Les noms d'attributs servent par la suite à créer des sections dans l'article dynamique.Coordonnees: Coordonnées liées a un utilisateur dont le type est permissif (num, mail, moodle, discord...) sont tous des types. On fait le choix d'ajouter un id, une personne pouvant renseigner plusieurs adresses mail. Permet aussi de stocker des informations diverses liées au profil (bio, photo de profil...).Adherent: Stocke les numéros d'adhérents associés (accessoirement permet de vérifier facilement si un profil est adhérent). Permet accessoirement de stocker la spécialité de l'adhérentCotisation_Inscription: Pour vérifier l'historique des paiements des adhérents. Les modes de paiements sont restreint à (1,2,3,4) correspondants respectivement a (espèce, carte, cheque, izly..)Sortie: définit une sortie. L'animateur est renseigné par son ID.Info Habitat: Stocke toutes les informations concernant un habitat donné. Eg. Superficie, climat, humidité, localisation et surtout: le statut conditionnel de nichoir permettant par la suite d'obtenir des informations relatives aux nichoirs.Observe: Permet de renseigner les espèces observées dans un lieu donné. Comme pour un petit jeu "repérer toutes les espèces d'un habitat". Le descriptif serait épisodique, indépendant des attributs de l'espèce simplement des informations du type "je l'ai trouvé dans un tronc d'arbre"Renseigne: L'écriture des attributs d'espèces est collaborative à la manière de Wikipédia. Cette table permet de garder un historique des modifications afin de récompenser les utilisateurs les plus actifs (fonctionnalité non mise en place, voire plus dans la section 3.3)Specialise: Renseigne des spécialités à un adhérent (eg. Ornithologue...). On ne crée pas de table "Spécialité" car cela impliquerait d'ajouter individuellement les spécialité à chaque nouveau renseignement et, par suite créer des craintes de duplication dans cette dernière.Inscription: Permet de retracer les inscriptions d'adhérents aux sorties.
[!check] Remarque : une distinction claire est faite entre les utilisateurs adhérents ou non sur le site. Un utilisateur non adhérent ne peut rien publier (info... / attribut... / observations...)* mais peut être ajouté en tant qu'animateur de sorties par un membre du bureau.
Le domaine étant très spécifique, il était naturellement compliqué d'obtenir un nombre extensif de données. Certains datasets publics sur Kaggle ou Hugging Face comprennent des animaux mais sont souvent peu exploitables en raison du fait qu'il s'agit d'images labellisées et non d'informations / observations.
Ci-dessous se trouve l'ensemble des datasets utilisés pour la construction de la base de donnée ainsi qu'une brève description sur ce qu'ils ont permis d'obtenir. Ils sont accompagnés du nom de fichier associé dans le dossier data/ du projet.
-
Génération des habitats
-
Bordeaux-metropole.fr
bor_abrisfaune.csv- Nichoirs labellisés et coordonnées
-
data.iledefrance.fr
znieff-type2.csv- Zones naturelles d’intérêt écologique, faunistique et floristique - ZNIEFF - type 2
-
Bordeaux-metropole.fr
-
Génération d'une liste d'espèces
- Scraping de Faune-IleDeFrance.org
- A permis d'obtenir toutes les espèces répertoriées à Champs-sur-Marne dont les oiseaux séparément
- Stockées par la suite dans
oiseaux.csvetautres.csvselon des couples nom d'usages / nom scientifiques
-
Génération d'informations d'espèces
- Utilisation de l'API de Wikipédia + redirection automatique activée
- On obtient un nombre aléatoire de paragraphes depuis la section
descriptionconcernant l'espèce - Nombre aléatoire d'images hébergées sur Wikimédia concernant l'espèce
- Taille par recherche du pattern
... (<1-9>) cm(correspond souvent à l'information souhaitée).- La taille est une information très importante. En effet, celle-ci permet d'éviter des situations absurdes (eg. Cygne dans un nichoir)
- La fréquence d'espèce découle grossièrement de leur nombre d'images disponibles étant donné leur recensement dépendant d'observations dans un lieu donné avec une image donnée par un utilisateur
-
Génération de profils et adhérents
- Utilisation de la librairie
fakerde Python - Génération manuelle de noms d'utilisateurs en fonction du nom, prénom et dans certains cas départements. Unicité garantie par un dictionnaire de fréquences.
- Code dans
data_faker.py
- Utilisation de la librairie
-
Génération des sorties
- Probablement la partie la plus délicate
- On génère via GPT 5.1 Nano (léger, rapide et suffisamment performant pour la tâche) des descriptions, titres et thèmes pour chaque sortie selon un nom d'habitat et le statut d'habitat (si c'est un nichoir ou non).
- On calcule la distance entre l'habitat et le Bâtiment Lavoisier avec la méthode de Haversine
- On génère aléatoirement l'effectif max
- Selon l'effectif max on génère aléatoirement un nombre d'inscrits
$(0 ≤ x ≤ \texttt{effectif_max})$ - On s'assure qu'il soit plus probable que les sorties soient pleines en terme d'inscrits.
- On spécifie dans le prompt de n'utiliser qu'un mot pour le thème, permettant d'avoir une consistence de thèmes entre les sorties
- Selon l'effectif max on génère aléatoirement un nombre d'inscrits
- La génération d'informations d'habitat est très analogue, aussi disponible dans le fichier
data_faker.py
[!info] Les problèmes en suspens sont marqués par
$\clubsuit$ Les améliorations possibles$(\star)$
-
$(\clubsuit)$ Statistiques peu réalistes d'habitat - À cause du choix aléatoire d'un adhérent pour chaque observation et du nombre extensif d'adhérent, tout triplet (image, adhérent, espèce) est unique résultant d'un manque de réalisme. Dans chaque instance on a des données du type, pour$n$ fixé : ($n$ images,$n$ observations,$n$ individus)-
Solution
- Inscrire des adhérents à des sorties qui
- n'ont rien observé
- ont observé des espèces déjà observées
- Inscrire des adhérents à des sorties qui
-
Problème
- Comme vu dans la section précédente le nombre d'observations dépendent du nombre d'images initialement trouvées sur Wikipédia
- Il est donc impossible de rajouter des observations pour une même espèces, les images étant toutes consommées
- de même, il aurait fallu faire en sorte que certaines personnes puissent observer plusieurs fois la même espèce ce qui contredirait l'unicité imposée par le modèle du Jeu du Snapshot.
- C'est donc un problème laissé en suspens. À discuter le jour de la soutenance.
-
Solution
-
Attributs incohérents - Par construction des attributs en « scrapant » les différents paragraphes Wikipédia, certains attributs sont incohérents pour les raisons suivantes :
- texte résiduel,
- ... pas du texte (eg. des sections de liens)
- pas de section ou nom de section incohérent (par exemple
=) -
Correctif
- Filtrer les attributs selon leur longueur et s'assurer que
$100 ≤ \text{longueur} ≤ 1050$
- Cette mesure ne concerne pas les observations manuelles mais seulement générées.
- Filtrer les attributs selon leur longueur et s'assurer que
-
$(\clubsuit)$ Images incohérentes - Certaines images sont des liens morts bien que cela soit très rare. Par ailleurs, certaines images ne contiennent pas l'espèce concernée mais d'autres éléments (œufs, carte, schéma...).-
Correctif proposé - Non mis en vigueur
- Correctif non mis en vigueur car il impliquerait la recréation du jeu de donnée, à fortiori la suppression de la quasi-totalité de la base de donnée en raison des clefs primaires
- Il aurait fallu filtrer selon les libellés d'images fournis par Wikipédia (les
alt) et exclure certains résultats selon l'occurence de certains morts (eg. Carte / Schéma / Œufs / Microscope / Maquette / Squelette ...)- Une méthode plus robuste (et chronophage) mais éliminant toute incohérence aurait été de les faire valider par une IA individuellement
- Pour ce qui est des liens mort les tester individuellement avec la librairie
requestsde Python avant de les utiliser.
-
Correctif « bricolé »
- Charge les images en série (en cas d'échec, passer à l'image suivante)
- Si une observation dépend d'une image dont le lien est mort, ne pas afficher l'observation.
-
Correctif proposé - Non mis en vigueur
-
$(\star)$ Descriptions et thèmes de sorties trop génériques - Cela est possiblement du à la mise en cache de certaines réponses par l'IA (cf. https://platform.openai.com/docs/guides/prompt-caching#how-it-works) il est question de vérification avec un hash. Il n'est pas impossible que certaines des requêtes ait donné lieu à une correspondance de 100% (il suffit d'une requête pour créer une sortie dans un même lieu pour s'y faire). Puisqu'il s'agit ici de hash, ajouter une simple information aurait empêché le hash. On aurait pu ajouter le thème choisi pour la sortie (en excluant par exemple nettoyage / maintenance si l'habitat concerné n'est pas un nichoir) -
$(\clubsuit)$ Problèmes avec les accents - PostgreSQL contre toute attente gère très mal les accents ce qui a été un véritable casse-tête pour identifier des problèmes de requête (... en particulier avec le thème de sortie « Désherbage »). Pour des raisons de temps et simplification, les occurrences de thèmes à accents ont simplement été supprimées après plusieurs vaines tentatives de correction.
Système d'XP
- Une contribution au Biocodex (type attribut ou observation) rapporte un nombre variable d'XP afin de classifier les utilisateurs par pertinence et confiance pour, sur le long terme trier les données selon différents critères dont la pertinence de l'utilisateur.
- Plus de détails ci-dessous
Missions
- Il se peut que des scientifiques ou rédacteurs confirmés du Biocodex aient besoin d'informations très précise concernant une espèce (par exemple: texture du bec) auquel cas ils publieront des quêtes mensuelles pour les utilisateurs.
- Ces derniers pourront soumettre des informations qu'ils pensent correspondre à la demande. Si elles sont validées des récompenses importantes seront reçues (>1000 XP).
Jeu du snapshot
- Pour construire une librairie d'images d'espèces, un petit jeu est mis en place. Pour un nombre donné d'espèces le jeu consiste à photographier toutes les espèces.
- Une espèce capturée : 50 XP / Un habitat complété : Bonus de 500 XP
- Afin de vérifier la véracité de leurs trouvailles, les utilisateurs doivent photographier l'espèce et donner une description (alt) à leur photo qui apparaît dans leur feed.
- Faute de moyens, pas d'IA pour déterminer l'authenticité et unicité des photos mais nous comptons sur la bienveillance des utilisateurs mais aussi sur leurs dénonciation d'éventuels cas de triche..
- Si la photo ne correspond pas ou est volée / trouvée sur internet d'autres utilisateurs peuvent la signaler, résultant d'une vérification humaine du problème. Si la triche est avérée des sanction sont encourues (réinitialisation du compteur d'XP).
