Le blog du Doc’

N'a pas de facteur d'impact, mais y travaille.

Carnet d’observations PERL/XML

Sans commentaires (lu 16 fois)

Etant donné que je m’ennuyais (presque) ce week-end, je me suis replongé dans ce qui reste un des seuls langages informatiques qui me semble à peu près logique : PERL. Le but du jeu étant de créer un carnet d’observations de bêtes à nageoires, utilisable de plein de manières différentes, et reposant sur un fichier XML.

Je mets a disposition ici quelques lignes du code, soit parce qu’elles m’ont causé une bonne galère, soit parce qu’elles sont aisément réutilisables, et qu’elle peuvent servir à plein d’autres choses…

Introducing the XML

Le fichier XML que j’utilise est assez simple :

<?xml version='1.0'?> <observations> 	<obs> 		<id>code généré automatiquement par le script</id> 		<lieu>de l'observation</lieu> 		<genre>Thalassoma</genre> 		<espece>pavo (oui, j'aime ce poisson!)</espece> 		<date>jjmmAAAA</date> 		<rem>observations diverses et variées</rem> 	</obs> </observations>

Le code d’identification est généré automatiquement pas le script. Pour faire barbare, on peut dire qu’il est égal à

 my $current_id = @keys+1;  
my $id = $current_id.uc(substr(param('obs_genre'),0,3)).lc(substr(param('obs_sp'),0,3)).uc(substr(param('obs_lieu'),0,3)); 

En gros, ce code est composé du numéro de l’observation, des trois premières lettres du genre en majuscules, des trois premières lettres du nom d’espèce en minuscules, et des trois premières lettres du lieu en majuscules aussi.

RegExp attitude

Les RegExp, ou Expressions Rationnelles[1] sont un des outils les plus puissants du langage PERL. Elles permettent de faire de la recherche de motifs dans un texte (même si le texte n’est pas tout à fait connu), du remplacement, … C’est particulièrement efficace si on veut vérifier la présence d’une séquence consensus[2] dans une séquence ADN, par exemple.

Bref, moi je voulais faire une fonction de recherche qui permette de retrouver un ou plusieurs enregistrements, sur différents critères. En l’occurrence, je me suis limité au genre, à l’espèce, et au lieu. Donc, mon numéro d’identification est assez utile. Le problème, c’est de pouvoir retrouver un enregistrement sans connaitre son numéro exact.

La, les RegExp s’imposaient à moi. J’ai donc procédé de la manière suivante :

  1. utilisation d’un caractère joker, en l’occurrence *
  2. remplacement de ce caractère joker par le motif de recherche le plus vaste : (.+)
  3. parcours du fichier XML et récupération dans un tableau de toutes les entrées matchant le motif de recherche
    • partie très peu optimisée, mais PERL a une puissance de traitement des chaînes qui fait que je peux m’autoriser ce genre de compromis (temps de développement/efficacité)
    • si l’enregistrement correspond aux critères de recherche, on affiche le résultat

Ce qui s’exprime finalement de la manière suivante :

  if ($accession =~ /\*/g)  	{  		$accession  =~ s|\*|\(\.\+\)|g;  		print b('Recherche partielle sur ',$accession);  		foreach my $obs (keys %{$base->{obs}})  		{  			if ($obs =~ m/$accession/g) { affiche_resultat($obs) }  		}  	} else {  		affiche_resultat($accession);  		}  }  

Par exemple, pour rechercher toutes les girelles que j’ai (hypothétiquement) vues, il suffit de rentrer *COR* (pour tous les membres du genre Coris, mais aussi Corallium, et d’autres!), ou *CORjul* (pour tous les Coris julis).

Le premier * est très important, puisqu’il sert a indiquer qu’on recherche n’importe quel numéro d’observation[3]. Le dernier * indique qu’on recherche n’importe quel lieu. Bien sur, on peut aussi faire *nomdulieu pour toutes les observations sur un lieu précis.

Cette petite fonction permet aussi de parcourir toute la base en une seule fois, en entrant simplement le caractère joker *.

Link it to the world

Ok, j’avais réussi a récupérer a n’importe quel enregistrement de mon fichier, je pouvais donc commencer les choses sérieuses : faire des liens vers quelques bases de données.

J’ai choisi ADW, GBIF, OBIS, et la très célèbre FishBase. Pour créer les liens, je suis allé sur les sites, et j’ai lancé des requêtes, pour les analyser ensuite. Après, de manière assez simple, je les ai recopiées, en remplaçant les termes que j’avais entrés par des liens sur ma structure de données XML.

En gros, ca me donne quelque chose dans ce genre la :

   b('espece : '), i($base->{'obs'}->{$accession}->{'genre'}),' ',i($base->{'obs'}->{$accession}->{'espece'}),p,

a({-href=>'http://animaldiversity.ummz.umich.edu/site/accounts/information/' .$base->{'obs'}->{$accession}->{'genre'}.'_'

.$base->{'obs'}->{$accession}->{'espece'}.'.html',-target=>'_blank'}, "ADW"),' - ',

a({-href=>'http://www.gbif.org/Search/search?find_string=' .lc($base->{'obs'}->{$accession}->{'genre'}).'+'

.$base->{'obs'}->{$accession}->{'espece'},-target=>'_blank'}, "GBIF"),' - ',

a({-href=>'http://iobis.org/IndexSearch?sciname=' .lc($base->{'obs'}->{$accession}->{'genre'}).'+'

.$base->{'obs'}->{$accession}->{'espece'},-target=>'_blank'}, "OBIS"),' - ',

a({-href=>'http://www.fishbase.org/Summary/speciesSummary.php?&genusname=' .lc($base->{'obs'}->{$accession}->{'genre'})

.'&speciesname='.$base->{'obs'}->{$accession}->{'espece'},-target=>'_blank'}, "Fishbase"),' - ', a({-href=>'http://www.fishbase.org/NomenClature/ScientificNameSearchList.php?crit1_fieldname=SYNONYMS.SynGenus&crit1_fieldtype=CHAR&crit1_operator=EQUAL&crit1_value='

.lc($base->{'obs'}->{$accession}->{'genre'}) .'&crit2_fieldname=SYNONYMS.SynSpecies&crit2_fieldtype=CHAR&crit2_operator=contains&crit2_value=&group=summary&backstep=-2' ,-target=>'_blank'}, "Fishbase par genre"),p,

Le résultat, pour reprendre l’exemple de Coris julis, est le suivant:

ADW - GBIF - OBIS - Fishbase - Fishbase par genre

And now, what?

Il reste quelques trucs a rajouter à droite à gauche, pour être tout à fait fier de mon travail. Pour commencer, je n’ai pas réussi a trouver comment on accédait aux informations de taxonomie du NCBI. Si quelqu’un sait comment on fait, je suis super preneur.

D’un point de vue plus technique, on pourrait rajouter quelques infos dans le fichier XML, mais ce n’est pas le plus long. Je pensais notamment à des choses comme

  • les coordonnées GPS du lieu
  • une ligne avec des informations de photoidentification, si il y a (ce qui ne doit pas être très difficile a faire, des numéros de photos stockées dans un dossier qui ne change pas, un coup de RegExp, et c’est plié)
  • une recherche par date
  • une association entre deux fiches ou plus (pour le cas ou on observe deux bêtes intéressantes en même temps, et qu’on veut s’en souvenir)
    • soit en donnant le numéro d’ID dans les remarques
    • soit en trouvant une nouvelle fonction qui permette de les associer autrement, via un nouveau tag XML, ce qui ne devrait pas être plus difficile à faire
  • ajouter un lien vers pubmed (ce que j’ai oublié de faire, alors que c’est super facile)

Bref… Pour l’instant, je n’en ai pas l’utilité, mais ca pourrait venir un jour… On verra bien sur le terrain ce que ca donne.

A noter aussi que pour l’instant je suis parti sur un script en CGI, mais qu’une version light en mode console ne devrait pas demander trop de travail. Le problème du CGI, c’est qu’il faut installer et configurer un serveur web pour PERL, et que ce n’est pas toujours évident.

Notes

[1] traduites en Expressions régulières par abus de langage

[2] pas de mauvaise blague

[3] cf. la première partie

Ecrit par Timothée

17 déc 06 à 12:27

Laisser un commentaire

Sitemap