Month: November 2008

Piwik 0.2.26

Posted by – November 30, 2008

Ce que j’aime bien chez Piwik par rapport à Google Analytics, en plus de la qualité des graphes, c’est son tableau de bord sous forme de widgets (un peu à la Netvibes) : on en rajoute, on en retire au grès des envies, et ça, c’est top.

En plus, il y a une extension DC2, cela facilite la mise en place. Il en existe également pour tout un ensemble de blogwares et autres moteurs de wiki.

Une légère satisfaction, plus de la moitié des visiteurs du blog utilisent Firefox 3 :

piwik-nav.png

Concernant les résolutions d’écrans, nous sommes sur une utilisation en 1280×1024, suivi de 1280×800, les autres sont au-dessus des 1000x (1 152, 1600, 1920, …) :

piwik-resol.png

Ce qui est également intéressant, c’est l’API qui est mise à disposition, très riche, elle permet de développer des applications AIR d’une grande utilité.

Piwik est à un outil à mettre en place sans attendre !

Linq pour détecter mes strings en double

Posted by – November 27, 2008

Forcément, avec un titre pareil, on sent le gros lourd qui veut attirer les geeks, enfin….bref.

Dans la série

3.5 est vraiment pas mal et j’en découvre tous les avantages,

voilà, comment en 2 lignes, on arrive à détecter des doublons de mails dans un tableau de string.

Imaginons des lignes avec des emails, suivis d’un ensemble d’informations (nom, prénom, …), je veux procéder au traitement du lot uniquement si ce dernier ne contient pas de doublons d’emails. Le lot suivant contient un mail en doublon, sauras-tu le retrouver ?

 od@mydummy.info;DUDU;Ol zorky@domain.info;DUDU;Ol od@mydummy.info;D;O zorky00@fake.com;; 

et oui od@mydummy.info est effectivement en double, ce qui est gênant.

Détectons si le lot contient des emails ayant plus qu’une occurrence. Le test suivant renvoie les lignes ayant plus d’une occurrence sur l’email et les compte, ici, le résultat sera 1 :

[Test] public void ArrayOfEmails_WithDouble_Should_be_detected() {    var myabos = new [] {        "od@mydummy.info;DUDU;Ol",        "zorky@domain.info;DUDU;Ol",        "od@mydummy.info;D;O",        "zorky00@fake.com;;"      };    var cntEmail = from emel            in myabos.Select(x => x.Split(new[] { ';' }).First())            group emel by emel into e            where e.Count() > 1           select new { EMail = e, ECount = e.Count() };      Assert.AreEqual(1,cntEmail.Count()); }

Une gymnastique reste encore à faire, mais à la vue des avantages (disons que cela réduit de façon drastique les boucles et autres tests à l’intérieur), Linq va devenir un réflexe.

NHibernate : HQL et Criteria

Posted by – November 26, 2008

Introduction

Sous NHibernate, il y a 3 manières d’effectuer des requêtes : SQL natif, HQL, et Criteria. La 1ère méthode est à proscrire, sous peine de perdre l’abstraction avec la base, liée à la syntaxe SQL spécifique à celle-ci. Reste HQL et les Criteria, je me demandais quelle méthode utiliser.

Modèle, mapping et requêtes

Soit le modèle suivant :

assoc-resp.png

pour des raisons certainement très justifiées (legacy & co), j’ai besoin d’avoir un objet pour représenter la table de jointure, et ne retenir que l’entité Association et la table de jointure entre Association et Responsable, on aura les tables suivantes : ASSOC et ASSOCRESP. Donc point de many-to-many ici, mais juste une (sous-)requête à réaliser entre ASSOC et ASSOCRESP.

Au niveau base de données, on trouve le schéma de tables suivant (généré avec la syntaxe SQLite par NHibernate) :

CREATE TABLE ASSOC (  Id  integer,   assoc_code TEXT NOT NULL,   assoc_libelle TEXT NOT NULL,   PRIMARY KEY (Id)) CREATE TABLE ASSOCRESP (  resp_id INTEGER NOT NULL,   assoc_id INTEGER NOT NULL,   PRIMARY KEY (resp_id, assoc_id))

et le mapping suivant :

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" >   <class name="AssocConnector.Assoc, AssocConnector" table="ASSOC">     <id name="Id" column="Id" type="System.Int32" access="property">       <generator class="identity"></generator>     </id>     <property name="Code" column="assoc_code" not-null="true" type="System.String" access="property"/>     <property name="Libelle" column="assoc_libelle" not-null="true" type="System.String" access="property"/>     </class> </hibernate-mapping>

une clé composite pour la table de jointure :

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" >   <class name="AssocConnector.ResponsableAssoc, AssocConnector" table="ASSOCRESP">     <composite-id>       <key-property name="Responsable" column="resp_id" type="System.Int32" access="property"/>       <key-property name="Assoc" column="assoc_id" type="System.Int32" access="property"/>     </composite-id>   </class> </hibernate-mapping>

Un test (la méthode GetAssocForAResponsable_ShouldReturn_2Assoc nous intéresse) qui utilise les 2 méthodes : HQL et Criteria : renvoie pour une responsable (id) la liste des associations dont il est responsable. Pour l’occasion (et parce qu’on est à fond), on va aussi utiliser les indicateurs (stats.) que permet la version 2 de NHibernate, pour les tests, ça pourrait servir, le code ici est à titre de démonstration des stats (stats + le Delete).

[Test] public void GetAssocForAResponsable_ShouldReturn_2Assoc() { // requête API Criteria var assocCriteria = DetachedCriteria.For<ResponsableAssoc>()        .SetProjection(Projections.Distinct(Projections.Property("Assoc")))        .Add(Restrictions.Eq("Responsable", 1972)); var lassoc1 = session.CreateCriteria(typeof(Assoc), "assoc")        .Add(Subqueries.PropertyIn("Id", assocCriteria)).List();   // requete HQL IQuery q = session.CreateQuery(@"FROM Assoc assoc WHERE assoc.Id IN     (SELECT ra.Assoc FROM ResponsableAssoc ra       WHERE ra.Responsable = :respid)"); var lassoc2 = q.SetParameter("respid", 1972).List();   _displayStats();   Assert.AreEqual(2, lassoc1.Count); Assert.AreEqual(2, lassoc2.Count);   GlobalStats.Clear();   session.BeginTransaction(); session.Delete("from ResponsableAssoc ra where ra.Responsable = :respid", 1972, NHibernateUtil.Int32); session.Transaction.Commit();   _displayStats(); }   [TestFixtureSetUp] public void initall() {     InitalizeSessionFactory(new List<string> {"AssocImpl"}); }   [SetUp] public void init() {     session = CreateSession();       _create_3Assoc();     _create_3Responsables_for_1Assoc(2);     _create_3Responsables_for_1Assoc(3); }   [TearDown] public void end() {     session.Dispose(); }   private IStatistics GlobalStats {     get { return session.SessionFactory.Statistics; } }   private void _displayStats() {     Console.WriteLine("Statistiques NHibernate");     Console.WriteLine("Insert : {0}", GlobalStats.EntityInsertCount);     Console.WriteLine("Delete : {0}", GlobalStats.EntityDeleteCount);     Console.WriteLine("Fetch : {0}", GlobalStats.EntityFetchCount);     Console.WriteLine("Load : {0}", GlobalStats.EntityLoadCount);     Console.WriteLine("Update : {0}", GlobalStats.EntityUpdateCount); } private List<Assoc> _create_3Assoc() {     var assocs = new List<Assoc>                    {                        new Assoc {Code = "A01", Libelle = "Association pour la liberté du NET"},                        new Assoc {Code = "A9078", Libelle = "Association des chiens errants"},                        new Assoc {Code = "A4503", Libelle = "Association des geeks"}                    };     foreach (var asso in assocs)         session.Save(asso);     session.Flush();     foreach (var asso in assocs)         session.Evict(asso);       return assocs; }   private List<ResponsableAssoc> _create_3Responsables_for_1Assoc(int associd) {     var responsableAssocs = new List<ResponsableAssoc>                    {                        new ResponsableAssoc {Assoc = associd, Responsable = 1972},                        new ResponsableAssoc {Assoc = associd, Responsable = 1976},                        new ResponsableAssoc {Assoc = associd, Responsable = 1999}                    };     foreach (var resp in responsableAssocs)         session.Save(resp);     session.Flush();     foreach (var resp in responsableAssocs)         session.Evict(resp);       return responsableAssocs; }

La requête Criteria sera traduite par NHibernate par :

SELECT this_.Id AS Id0_0_, this_.assoc_code AS assoc2_0_0_, this_.assoc_libelle AS assoc3_0_0_ FROM ASSOC this_ WHERE this_.Id IN (SELECT DISTINCT this_0_.assoc_id AS y0_ FROM ASSOCRESP this_0_ WHERE this_0_.resp_id = @p0); @p0 = '1972'

La requête HQL par :

SELECT assoc0_.Id AS Id0_, assoc0_.assoc_code AS assoc2_0_, assoc0_.assoc_libelle AS assoc3_0_ FROM ASSOC assoc0_ WHERE (assoc0_.Id IN(SELECT responsabl1_.assoc_id FROM ASSOCRESP responsabl1_ WHERE (responsabl1_.resp_id=@p0 ))); @p0 = '1972'

Trace complet du test :

 NHibernate: INSERT INTO ASSOC (assoc_code, assoc_libelle) VALUES (@p0, @p1); select last_insert_rowid(); @p0 = 'A01', @p1 = 'Association pour la liberté du NET' NHibernate: INSERT INTO ASSOC (assoc_code, assoc_libelle) VALUES (@p0, @p1); select last_insert_rowid(); @p0 = 'A9078', @p1 = 'Association des chiens errants' NHibernate: INSERT INTO ASSOC (assoc_code, assoc_libelle) VALUES (@p0, @p1); select last_insert_rowid(); @p0 = 'A4503', @p1 = 'Association des geeks' NHibernate: INSERT INTO ASSOCRESP (resp_id, assoc_id) VALUES (@p0, @p1); @p0 = '1972', @p1 = '2' NHibernate: INSERT INTO ASSOCRESP (resp_id, assoc_id) VALUES (@p0, @p1); @p0 = '1976', @p1 = '2' NHibernate: INSERT INTO ASSOCRESP (resp_id, assoc_id) VALUES (@p0, @p1); @p0 = '1999', @p1 = '2' NHibernate: INSERT INTO ASSOCRESP (resp_id, assoc_id) VALUES (@p0, @p1); @p0 = '1972', @p1 = '3' NHibernate: INSERT INTO ASSOCRESP (resp_id, assoc_id) VALUES (@p0, @p1); @p0 = '1976', @p1 = '3' NHibernate: INSERT INTO ASSOCRESP (resp_id, assoc_id) VALUES (@p0, @p1); @p0 = '1999', @p1 = '3' NHibernate: SELECT this_.Id as Id0_0_, this_.assoc_code as assoc2_0_0_, this_.assoc_libelle as assoc3_0_0_ FROM ASSOC this_ WHERE this_.Id in (SELECT distinct this_0_.assoc_id as y0_ FROM ASSOCRESP this_0_ WHERE this_0_.resp_id = @p0); @p0 = '1972' NHibernate: select assoc0_.Id as Id0_, assoc0_.assoc_code as assoc2_0_, assoc0_.assoc_libelle as assoc3_0_ from ASSOC assoc0_ where (assoc0_.Id IN(select responsabl1_.assoc_id from ASSOCRESP responsabl1_ where (responsabl1_.resp_id=@p0 ))); @p0 = '1972' Statistiques NHibernate Insert : 9 Delete : 0 Fetch : 0 Load : 2 Update : 0 NHibernate: select responsabl0_.resp_id as resp1_1_, responsabl0_.assoc_id as assoc2_1_ from ASSOCRESP responsabl0_ where (responsabl0_.resp_id=@p0 ); @p0 = '1972' NHibernate: DELETE FROM ASSOCRESP WHERE resp_id = @p0 AND assoc_id = @p1; @p0 = '1972', @p1 = '2' NHibernate: DELETE FROM ASSOCRESP WHERE resp_id = @p0 AND assoc_id = @p1; @p0 = '1972', @p1 = '3' Statistiques NHibernate Insert : 0 Delete : 2 Fetch : 0 Load : 2 Update : 0 

Conclusion

On peut voir que les 2 méthodes génèrent environ la même requête à Epsilon près.

Mon avis sur les Criteria :

  • API, donc vérifiée à la compilation au niveau de sa construction, mais négligeable, n’utilisons-nous pas le TDD pour le développement ? ;-) ,
  • pour les allergiques du SQL, les Criteria paraissent être un bon compromis,
  • cela reste assez verbeux, beaucoup de lignes pour pas grand chose,

sur les HQL :

  • pour les fin connaisseurs de SQL, l’apprentissage s’en trouve réduit,
  • comme c’est une chaîne, la (mauvaise) habitude de concaténer les arguments peut vite arriver, au lieu d’utiliser les paramètres (SetParameter), et ça, c’est mal,
  • la syntaxe étant une chaîne, cela peut être pratique de mettre en place un système d’indicateurs où il y aurait les requêtes à exécuter dans un fichier par exemple (ie: requêtes statiques),

Au niveau performances, il peut y avoir une légère différence, mais pour le Web, lorsqu’il s’agit d’afficher une page, ce n’est pas un critère discriminant.

Personnellement, je penche pour HQL, bien plus proche du SQL, et NH permet d’avoir un langage d’interrogation objet, autant l’exploiter.

Ressources

On pourra essayer cet intéressant projet, plugin NHibernate pour R#.

Links #10 : MS Web platforms

Posted by – November 22, 2008

  • Packaging MS : IIS + VStudio Express + SQL Express
  • MS (ASP.NET) + Opensource (PHP, Perl, MySQL) = WAI

Links #9 : jQuery, WCF / POCO, GMail, semantic Web

Posted by – November 22, 2008

Ce type de billet est de mettre en avant des billets qui ont retenu toute mon attention.

  • avec l’intellisense jQuery intégré à Visual Studio 2008, cela en devient presque magique,
  • lorsque l’on parle de vulnérabilités JSON, je ne peux que repenser à ma triste expérience de spoofing sur mon compte GMail. Ce lien apporte-t-il une explication ? en attendant, pour l’instant j’ai été exclu du groupe ALT.NET, premier dommage du SPAM envoyé contre ma volonté ?,
  • WCF sans attributs : de mon propre avis, il est toujours étonnant de fournir des frameworks où l’on doive utiliser des attributs à apposer sur les classes ou propriétés. Cela contribue à polluer le code et à générer une adhérence forte avec le framework utilisé, bonne nouvelle donc avec le SP1.
  • le Web sémantique résumé par David

GMail j’ai mal

Posted by – November 19, 2008

Fin d’après-midi, je reçois un mail en anglais de … DUVAL Olivier, avec un from: sur mon email gmail…1 mn après je réalise qu’on vient de me hacker mon compte et donc que ce mail, SPAM pour un site de vente en ligne, est en train d’arroser tous mes contacts, par lots de mails, ordonnés sur le nom.

Quand on sait que sous gmail, dès qu’on écrit à quelqu’un, ce dernier est automatiquement ajouté aux contacts, on imagine vite le désastre.

Aussi, je présente mes sincères excuses pour la gêne occasionnée à ceux qui auraient reçu ce mail indélicat (qui, au passage, passe les anti-spams).

Mot de passe changé, désactivation des add-on GMail (RTM, AideRSS), et malgré tout passablement énervé.

Le souci de ce type d’intrusion étant de trouver la faille : mot de passe (très probable, trop simple : sans chiffre par ex.), add-on, autre ?

Bon, le côté positif c’est que je reçois les notifications d’erreurs (bounces) sur les emails devenus obsolètes.

Déjà que j’avais pas d’amis (en tout bon geek), alors si en plus, ils me taguent de spammeur…faut espérer maintenant que cela n’ira pas plus loin qu’un mail…

ALT.NET : rencontre de décembre

Posted by – November 19, 2008

La prochaine rencontre aura lieu le mardi 2 décembre, de 19h30 à 21h30. Le sujet du jour sera le DDD.

Le talentueux Rob nous a trouvé un lieu pour accueillir la réunion : au 148 rue de l’Université, chez…Microsoft.

Pour s’inscrire, directement sur la page Wiki.

Microformats vs. RDFa

Posted by – November 16, 2008

Introduction

Samedi, j’ai assisté à un exposé sur le Web sémantique, où du moins sur les formats pour y répondre : µformats et les RDFa. La présentation avait lieu dans les locaux de l’EFREI (qui ne m’a pas du tout rappelé mes années universitaires, tant les locaux étaient bien entretenus, les salles accueillantes, bref, pas du tout comme Jussieu avec son amiante – vous remarquerez également que cette parenthèse n’apporte rien au compte rendu).

Frédéric, un peu malade, était accompagné de David pour nous parler de ces 2 formats.

Le Web sémantique, c’est quoi : la capacité de donner du sens aux pages Web à l’aide de méta-données incluses dans du code (X)HTML. Les microformats et RDFa répondent à un manque de HTML 4. La version 5 de HTML y répondra certainement.

Microformats

Mise en œuvre simple : utiliser les attributs rel, rev et class pour y inclure du sens. Ce sens peut englober plusieurs formats : un calendrier à l’aide de hCalendar, une carte de visite avec hCard, des mots clés avec rel-tag, rel-nofollow, un CV avec hResume, des nouveautés avec hAtom. On l’aura compris, il doit exister une spécification (hCard, hCalendar, …) pour décrire un modèle dans la page Web affichée.

Les µformats se veulent simples, et utilisables immédiatement.

Composition des différents formats

rel-tag : lancé par technorati en 2005 – permet d’étiqueter des contenus non indexables – aujourd’hui : il n’existe plus de moteurs de recherche qui supportent le rel-tag. Cela se représentera de la façon suivante :

<a href=”toto.html” rel=”tag”>Tag</a>

Une statistique intéressante : les nuages de tags sont très peu utilisés par les humains (- de 2%), mais bien entendu ce n’est pas le cas pour le référencement des moteurs.

rel-nofollow : <a href=”toto.html” rel=’external nofollow’>Lien</a> rel : relation vers un doc extérieur (cf. <link rel=…>). rev : relation de mon doc par rapport au doc pointé. Il y a eu des abus du no-follow par Wikio pour vampiriser les contenus des blogs : Wikio était classé avant le blog qui produisait le billet.

hCard : transposition à l’identique du format vCard : permet de saisir une carte de visite – support de la géo-localisation, prend en compte les adresses privées / professionnelles

hCalendar : transposition à l’identique de iCalendar – format décrié car il y a une utilisation abusive de l’élément abbr : utilise le title pour stocker la date

hAtom : transposition à l’identique du format Atom : destiné aux publications périodiques (journal, blog, …) – utilisé par le thème Scribbish (j’adore ce thème, à quand pour DC ?) comme Proof of concept de hAtom.

hResume : macroformat, permet d’écriture un CV en ligne en appliquant un ensemble de µformats : hCard, hCalendar, rel-tag (pour indiquer les compétences)

Il existe l’extension Operator de FF pour l’utilisation des µformats : détecte sur une page les µformats contenus dans une page. Pour être un utilisateur de cette extension, elle répond bien à l’ensemble des µformats, et permet de pousser sa recherche sur ce que trouve Operator. La prochaine version IE 8 implémenterait la lecture des µformats en natif.

Les slides de la présentation.

RDFa

RDF : Ressource Description Framework

Objectif : stocker des relations entre des personnes ou des choses. Par exemple : David Larlet connait Alexandre Passant : http://david.larlet.fr connaît http://apassant.net Il existe un vocabulaire (ontologie) FOAF pour décrire ce type de relation : http://david.larlet.fr foaf:knows http://apassant.net Voir les spécifications des différents vocabulaires.

RDFa : standard du W3C – ce format permet de donner une sémantique dans du code (X)HTML, comme pourraient le faire les microformats.

Exemples :

 <div xmlns:dc="http://purl.org/dc/elements/1.1"> <!-- la partie xmlns peut ê déclarée une seule fois dans la balise <html>, elle s'appliquera à ttes. les balises du doc. -->   <h2 property="dc:title">Mon titre</h2>   <h3 property="dc:creator">Olivier</h2> </div> 

Drupal 7 inclut du RDFa : avec un DOCTYPE particulier pour l’utilisation du RDFa

Utilisation soit des attributs rel ou property dans le code (X)HTML :

<div typeof=”foaf:Person” xmlns:foaf=”http://xmlns.com/foaf/0.1/”>

 <p property="foaf:name"     Alice  </p>  <p> Email : <a rel="foaf:mbox" href="mailto:alice@mail.fr">alice@mail.fr</a></p>  <p> Phone : <a rel="foaf:phone" href="tel:+33-01-40-69-29-44">+33-01-40-69-29-44</a>

</div>

Social network : blogroll en FOAF (voir FOAF explorer pour effectuer des interrogations FOAF)

 <div xmlns:foaf="http://xmlns.com/foaf/0.1/" about="#me" rel="foaf:knows">   <ul>    <li typeof="foaf:Person"> <a property="foaf:name" rel="foaf:homepage" href="http://monsite.fr">Bob</a> </li>    <li typeof="foaf:Person"> <a property="foaf:name" rel="foaf:homepage" href="http://monsite2.fr">Joe</a> </li>   </ul> </div> 

Operator permet de récupérer les informations RDFa.

Google et Yahoo semblent prendre en compte les RDFa pour l’indexation mais on ne sait pas si cela joue sur le Pagerank.

Ressources

searchMonkey de Yahoo.

Sindice est un moteur de recherche sémantique qui permet d’extraire les microformats ou RDFa des pages Web indexées.

Conclusion

Je m’intéresse à ces formats depuis pas mal de temps déjà. De mon propre avis, il reste un gros soucis d’exploitation des données (au niveau client), peu de moteurs (notamment Google) pour le moment les exploitent. Le salut viendra des navigateurs s’ils implémentent un jour en natif la lecture des microformats ou RDFa afin de pouvoir les utiliser simplement.

Je suis resté un peu sur ma faim quant à la présentation : quid de XFN, SIOC, APML, … qui forment le futur Web 3 (il parait) ? heureusement qu’il existe des petits génies en .NET pour développer un blogware nommé BlogEngine.NET qui prend en compte toutes ces petites choses, et en l’expliquant.

Microsoft Techdays 2009

Posted by – November 14, 2008

L’évènement est annoncé : il aura lieu du 10 au 12 février 2009 au Palais des Congrès de Paris.

Programme et abonnement à l’alerte pour être informé de l’ouverture des inscriptions sur le site des Techdays.

Paris Web 2008 : ateliers techniques

Posted by – November 14, 2008

Et n’oubliez pas, si, comme votre serviteur, vous cherchez absolument à avoir des amis, alors dès demain (samedi), participez aux ateliers techniques de Paris Web 2008.

Le programme de la journée est en ligne.

J’irai très certainement uniquement l’après-midi, aux ateliers jQuery et l’accessibilité et surtout à RDFa vs Microformats. Ce dernier est présenté par Frédéric et David.