Double Itérateur

Je travaille à nouveau sur des codes, cette fois à partir du XML de la base Legi. Je vais mettre ici plusieurs morceaux du code. Aujourd’hui voici un exemple d’un patern de programmation que j’utilise beaucoup ces temps-ci, les itérateurs, ici dans une variante à double profondeur. A la base je récupère une liste d’éléments sans volume, des titres de niveaux variables et des articles de code. Il arrive que certaines branches de l’arborescence de titre soient vides car les articles qu’elles contenaient ont été abrogés. Une approche aurait consisté de donner du volume à cette cette liste de titres en fonction de leurs niveaux d’importance mais cela aurait été inutile car le but est ensuite de générer des PDF avec FOP et des EPubs avec perl. Bref, il faut supprimer certains titres car ils sont inutiles selon mes critères de droit positif.

$d est mon document vu en perl par XML::LibXML.
$root = $dom->getDocumentElement();
(@nodes) = $root->childNodes();
while ($node = shift(@nodes)) {
#iterateur principal
if ($node->nodeType ==3) {
#Si c’est une node de texte je la supprime si elle ne contient pas de texte
$node->parentNode->removeChild($node) unless($node->textContent =~ /\[\w\-]/);
} else {
if ($node->nodeName =~ /^h(\d)/) {
#Si c’est un titre, j’en inspecte le niveau hiérarchique.
$level = $1;
my @charette;
#Je prépare une charette dans laquel je mettrai l’hypothétique lot à supprimer si effectivement le soul arbre ne contient pas d’articles.
my @copie = @nodes
#Je fais une copie de mon nodeset pour itérer dans ses sous branches.
while ($cand = shift(@node)) {
#Itérateur secondaire
$ca = $cand->nodeName;
last() if ($ca eq ‘article’);
#Je sors de l’itérateur si un des enfants est un article
if (($ca =~ /hash(\d)/) and ($1 > $level)) {
#Si c’est un sous branche je l’inscrit à la charette sans la supprimer.
push(@charette,$cand);
}
if (($ca =~ /hash(\d)/) and ($1 <= $level)) {
#Si c’est un titre de niveau égal ou supérieur, je vide la charette je supprime les nodes
map {
$_->parentNode->removeChild($_);
} @charette;
$node->parentNode->removeChild($node);
}
}
}
}
}

Pas si compliqué que cela, j’ai juste longtemps hésité sur la copie.

JSON is fragile

La livebox Orange bugge. Après avoir fait le tour de mes browsers, même ceux du côté obscur, j’ai cherché dans les forums pour voir si d’autres avaient de problème d’interface de configuration indisponible sur livebox play. Me sentant décidément très seul, j’ai sorti mon firebug et me suis lancé dans un gros travail d’inspection.

Lors de JSON.parse il me dit:
[bus] error handling ajax request

SyntaxError: JSON.parse: bad control character in string literal

Ce sur plusieurs pages, les autres browsers loguant des choses tout à fait similaires.

Le JSON  en question ne semblait pas suspect dans la console mais, à le lire, observe que l’erreur de situe lorsqu’il récupère les infos sur les devices connectés. Un petit coup de JSONLint (http://jsonlint.com/) me révèle bel et bien une erreur dans la section relative à mon smart screen Samsung. J’ai cru au début à un problème d’encodage mais rien d’évident à la vue.

Ce n’est qu’en copiant la ligne dans un éditeur de texte orienté caractère comme pspad que je n’ai enfin compris quoi qu’en copiant la ligne dans mon terminal j’avais déjà un début de réponse.

« userClassID »: «  »,

Là on ne devine rien mais un prenant le même segment dans un éditeur précis (Sublime Text 2 sur mon mac) je vois non des doubles quotes vides mais contenant un caractère de contrôle S0H !! Je ne comprends pas vraiment la notation de Sublime mais il s’agit d’un caractère de contrôle que les gars d’Orange.fr ont oublié de filtrer, reste à savoir lequel. SOH est en fait le premier caractère de la table ascii, Start of Header pour être précis.

Je ne sais pas trop comment contourner ce bug pour le moment mais je l’ai identifié. Avec du XML l’erreur aurait été la même mais cela aurait été à mon avis plus simple à identifier.

Jason a tort

Pour moi il a deux erreurs fondamentales dans le format JSON que j’utilise par ailleurs.

La première est la gestion des encodages. A ce que je comprends elle est implicite et déduite des headers http mais elle peut être corrigée à la volée par les useragents (les navigateurs). C’est une erreur historique car cela fait perdre du temps à tout le monde et que c’est une source d’erreurs potentielle. Au moins en XML, les règles sont claires, si l’encodage n’est pas précisé c’est de l’UTF-8 et il n’y a pas à tergiverser.
L’autre erreur de design du format est la gestion du whitespace, problème qui impacte aussi de nombreux autres formats dont le XML bien sûr. Je reste convaincu que le whispace aurait du être normé dans JSON : cela aurait certes réduit sa lisibilité par les humains mais cela aurait produit un gain de performance non négligeable. Le JSON n’est pas voué à être human readeable donc l’argument relatif à sa lisibilité est fallacieux. Si le whitespace avait été réglementé voire réduit à néant alors les parseurs de JSON auraient été plus performants.
Quel dommage.

Scale to fit

Je voulais jouer avec un composant qui serait space to fit mais pas mal de bugs liés au fait que les browser mettent le letter-spacing entre le lettres mais aussi après la dernière lettre. Donc ma formule devient pour une node hq sans enfants:

    hq.style.margin = hq.style.padding = ‘0’;
    var spana = document.createElement(‘span’);
    spana.appendChild(document.createTextNode(hq.firstChild.nodeValue));
    hq.replaceChild(spana,hq.firstChild);
    hq.style.letterSpacing = ‘0’;
    hq.style.textAlign = ‘center’;
    v = (hq.offsetWidth – hq.getElementsByTagName(‘span’)[0].offsetWidth) / (hq.getElementsByTagName(‘span’)[0].firstChild.nodeValue.length);
    hq.style.letterSpacing = ~~(v)+’px’;
    hq.style.marginRight = ~~(-1*v)+’px’;
Merci.

GM Fight

Un peu de fun avec mon ami Greasemonkey, pour rebondir sur le code de Guillaume Adreani (http://userscripts.org/scripts/show/156097) qui a beaucoup de qualités, notamment conceptuelle mais qui risque de casser le DOM et de faire quirker.

A défaut de getTextNodes, un peu d’xPath, et à défaut de liste des .rcv, un scrap à la volée et des regexps.

 

// ==UserScript==
// @name        Habett Codlink
// @namespace   le
// @include     http://*
// @include     https://*
// @version     1.3
// @grant       GM_xmlhttpRequest
// @grant       GM_getValue	
// @grant       GM_setValue	
// ==/UserScript==
 
var noxx = document.evaluate('//body/descendant::*[contains(text(),"code") or contains(text(),"Code")]'
  ,document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
var nodds = new Array();
for (i=0; i<noxx.snapshotLength; i++) {
  na = noxx.snapshotItem(i).nodeName.toLowerCase();
  if ((na != 'script') && (na != 'a')) {
    nodds.push(noxx.snapshotItem(i));
  }
}
var safr = new RegExp('\\W','g');
var kh = new Object();
if (GM_getValue('cli')) {
  kh = JSON.parse(GM_getValue('cli'));
  rere();
} else {
  GM_xmlhttpRequest({
    method: "GET",
    url: "http://www.legifrance.gouv.fr/initRechCodeArticle.do",
    onload: function(response) {
      var cl = new DOMParser().parseFromString(response.responseText, "text/html");
      var options = cl.getElementsByTagName('select')[0].getElementsByTagName('option');
      for (var i=1; i<options.length; i++) {
        var cod = options[i].getAttribute('title').replace(safr,'.');
        kh[cod]=options[i].getAttribute('value');
      }
      GM_setValue('cli',JSON.stringify(kh));
      rere();
    }
  });
}
function rere() {
  for (var k in kh) {
    var re = new RegExp('((\\d[\\d\\.\\-]{0,9})[^<>]*? du '+k+')','gi');
    var rf = new RegExp('((([LRD]+)\\W*?(\\d[0-9\\.\\-]{0,9}))[^<>]*? du '+k+')','gi');
    for (var j=0; j<nodds.length; j++) {
      nodds[j].innerHTML = nodds[j].innerHTML.replace(
        rf,'<a style="color: red" href="http://www.legifrance.gouv.fr/rechCodeArticle.do?champCode='
        +kh[k]+'&champNumArticle=\$3$4">\$1</a>'
      );
      nodds[j].innerHTML = nodds[j].innerHTML.replace(
        re,'<a style="color: red" href="http://www.legifrance.gouv.fr/rechCodeArticle.do?champCode='
        +kh[k]+'&champNumArticle=\$2">\$1</a>'
      );  
    }
  }
}
<noxx.snapshotlength; i++)="" {<br=""><options.length; i++)="" {<br=""><nodds.length; j++)="" {<br="">

 

c.cgi

Vu qu’il y a de la demande pour ça, voici la source.

#!/usr/bin/perl
use CGI;
use WWW::Mechanize;
use Encode;
$q = new CGI;
$c = $q->param(‘code’);
$a = $q->param(‘article’);
print « Content-type: text/html\n\n »;
$www = new WWW::Mechanize(agent => $ENV{‘HTTP_USER_AGENT’});
$www->get(‘http://www.legifrance.gouv.fr/initRechCodeArticle.do’);
$www->form_number(2);
($opt) = grep { $_ =~ /$c/i } $www->content =~ /(<option value=.*?<\/option>)/g;
unless ($opt) {
  $c = decode(‘utf-8’,$c);
  ($opt) = grep { $_ =~ /$c/i } $www->content =~ /(<option value=.*?<\/option>)/g;
}
$opt =~ /value= »(.*?) »/;
$www->field(‘champCode’,$1);
$www->field(‘champNumArticle’,$a);
$www->submit();
$url = $www->uri();
if ($www->title() =~ /Article/) {
  $url =~ s/^\.\/\./http:\/\/legifrance.gouv.fr/;
  print « Content-type: text/html\n\n<meta http-equiv=\ »Refresh\ » content=\ »0;url=$url\ » /> »;
} else {
  print « Content-type: text/plain\n\nNo Result »;
}

Regexp so legit

Remue méninge sur un extracteur générateur de liens pour des Codes en code.

Voilà donc un petit chunck de mon affreux linker par regex:
foreach $natty (‘décret’,’loi’,’ordonnance’) {
     (@possibles) = $text =~ /($natty[^<>]{1,21}\d\d\-\d+)/g;
     foreach $possible (@possibles) {
        $text =~ s/([^=>])$possible([^<« ])/$1<a href= »http:\/\/perlpot.net\/cgi-bin\/r.cgi\?what=$possible » shape= »exxo$natty »>$possible<\/a>$2/;
     }
}
 
Reste donc à écrire le r.cgi qui répond.
#!/usr/bin/perl
use CGI;
use WWW::Mechanize;
$q = new CGI;
($natty,$numb) = $q->param(‘what’) =~ /(.*?)\s.*?(\d\d\-\d+)/;
$natty =~ s/\W/./g;
$www = new WWW::Mechanize;
$www->get(‘http://www.legifrance.gouv.fr/initRechTexte.do’);
$www->form_number(1);
($opt) = grep { $_ =~ /$natty/i } $www->content =~ /<option value= »(.*?) »/g;
$www->field(‘champNatureTexte’,$opt);
$www->field(‘champNumTexte’,$numb);
$www->submit();
($url) = $www->content =~ /<li class= »resultat1″>.*?href= »(.*?) »/s;
$url =~ s/^\.\/\./http:\/\/legifrance.gouv.fr/;
print « Location: $url\n\n »;
OK ?

Propédeutique

Résolution de l’été, ouvrir un blog pro histoire d’avoir une stack où poster mon code réutilisable.

Installation donc de Movable Type sous Ubuntu linux, histoire de ne pas,contribuer à la néfaste propagation du PHP mais continuer la valorisation du gospel perl.
Reste que je concentre mes lectures actuelles sur Pro jQuery chez Apress meme si le code n’y est pas très beau, surtout dans le dernier chapitre que j’ai lu hier soir sur le templating,, avec l’excuse au demeurant que le plugin en question est du à un vendeur d’OS qui a plus d’avocats que de programmeurs.
Je suis un peu en panne au milieu de Higher Order Perl de Mark Jason Dominus chez Elsevier mais c’est dans l’ardeur de ce genre de concepts que l’on forge les déploiements à venir.
L’un dans l’autre, je cherche.
Enfin, dans le domaine du scrap, fil rouge de ce blog, à noter le tracing des requêtes XHR de HuDoc pour la CEDH, sous FireBug, afin de comprendre le mode de fonctionnement de leur webservices, afin d’obtenir une récupération correcte en l’absence de documentation utilisable. Bref, use JSON; avec mes réserves habituellement quand au défaut de mention explicite d’encodage de ce format de datas.
It’s more fun to compute.