[Résolu] XSLT : grouper et sommer

XSLT : grouper et sommer [Résolu] - XML/XSL - Programmation

Marsh Posté le 14-07-2008 à 15:38:51    

Bonjour,
 
Je suis nouveau dans le monde merveilleux du XSLT. Pour remettre dans le contexte, je fais un front-end pour une base Domino (Lotus Notes) en utilisant leur système de génération de XML (?ReadViewEntries pour ceux qui connaissent). Le problème est que la structure obtenue représente une succession de lignes (les <viewentry> ) avec des colonnes qu'elles ont toutes en commun (<entrydata> ), mais que je veux les grouper par la valeur de l'une de ces colonnes (en l'occurence la première, "auteur" ).
 
Fichier exemple :

<viewentries>
  <viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuPremierAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDuProjet</text>
    </entrydata>
    <entrydata columnnumber="2" name="nombre1">
      <text>12</text>
    </entrydata>
    <entrydata columnnumber="3" name="nombre2">
      <text>0</text>
    </entrydata>
    ... (il y a une dizaine d'autres valeurs numeriques similaires)
  </viewentry>
  <viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuPremierAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDunAutreProjet</text>
    </entrydata>
    ...
  </viewentry>
  <viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuSecondAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDEncoreUnAutreProjet</text>
    </entrydata>
    ...  
  </viewentry>
  ...
</viewentries>


 
La structure est un peu étrange (mais on peut pas avoir mieux en sortie de Lotus Notes hélas)
Les projets forment les "lignes", les enregistrements sont groupés par auteur.
 
Maintenant mes questions :  
1. Est-il possible, en XSL, d'aboutir à une structure XML où les <viewentry> seraient regroupés par auteur avec des balises <auteur nom=""> insérées entre <viewentries> et les <viewentry>, pour ensuite transformer facilement cette structure intermédiaire en HTML ?
 
2. Si la réponse à la question 1. est non, sachant que les <viewentry> sont triés par l'auteur (contenu dans le <text> contenu dans <entrydata columnnumber="0"> ) et que ce fichier XML est transformé en un tableau html montrant les valeurs numériques de chaque projet (les viewentry aux numéros de colonne >= 2), est-il possible d'afficher un tableau par auteur, pour mettre son nom en en-tête plutôt que dans chaque ligne ?
 
3. S'il y a une solution à la question 2, est-ce qu'il est possible, pour chaque tableau d'auteur, de rajouter une dernière ligne "total" faisant la somme des valeurs numériques des différents projets ?
 
Ce que je veux obtenir, c'est ça :

--- NomDuPremierAuteur ---
Projet           |donnee1|donnee2|....
NomDuProjet      |     12|      0|....
NomDunAutreProjet|     15|      5|....
Total            |     27|      5|....
 
--- NomDuSecondAuteur ---
Projet                 |donnee1|donnee2|....
NomDEncoreUnAutreProjet|      3|     10|....
Total                  |      3|     10|....
 
.......


 
Tout ça se ferait facilement avec un langage de script ou alors avec un fichier XML mieux structuré, mais j'ai que le XSL, qui m'a l'air suffisamment puissant pour faire ça.
 
 
J'ai tenté différentes choses :

<xsl:template match="viewentries">
<xsl:for-each select="viewentry/entrydata[@columnnumber='0']/text">


Le problème est qu'il me sélectionnait le même nom plusieurs fois, ce qui est logique.
 
J'ai essayé de mettre le nom de l'auteur dans une variable pour ensuite dé. Les "variables" d'XSL ont l'air d'être des constantes et les param ont l'air d'être le seul moyen de pouvoir stocker des valeurs... à condition d'appeler un template, ce qui oblige la récursivité, que je ne peux pas utiliser à cause de la génération de HTML dans ma template.
 
 
Enfin bref.
 
Des experts du XSL par ici ? :D
 
Merci beaucoup


Message édité par Lhalfelin le 18-07-2008 à 15:12:58
Reply

Marsh Posté le 14-07-2008 à 15:38:51   

Reply

Marsh Posté le 17-07-2008 à 09:22:37    

Si tu pouvais donner un exemple de ce que tu veux pour chaque question ce serait plus précis.  
 
Je vois rien d'impossible dans le cahier des charges. Regarde la fonction Xpath sum(), xsl:sort, le XSLT c'est fait pour triturer du XML dans tout les sens de toute façon...


Message édité par avander le 17-07-2008 à 09:26:42
Reply

Marsh Posté le 17-07-2008 à 15:50:29    

Bonjour avander
 
Pour la question 1, j'aimerais obtenir un XML intermédiaire plus commode

<viewentries>  
  <auteur nom="NomDuPremierAuteur">
    <viewentry>
      <entrydata columnnumber="0" name="auteur">
        <text>NomDuPremierAuteur</text>
      </entrydata>
      <entrydata columnnumber="1" name="projet">
        <text>NomDuProjet</text>
      </entrydata>
      <entrydata columnnumber="2" name="nombre1">
        <text>12</text>
      </entrydata>
      <entrydata columnnumber="3" name="nombre2">
        <text>0</text>
      </entrydata>
      ...  
    </viewentry>
    <viewentry>
      ...
    </viewentry>  
  </auteur>
 
  <auteur nom="NomDuSecondAuteur">
    <viewentry>
    ...  
    </viewentry>  
  </auteur>
</viewentries>


 
Evidemment si j'ai la possibilité de faire déjà ça je mettrai des balises plus explicites, mais ma question était surtout : est-il possible de faire un "group by" en xslt. J'ai continué à cherché hier sur le web mais les tutos et exemples en xlt sur le web cassent rarement des briques.  

Spoiler :

Heureusement les ressources sur MS Sharepoint (où est intégré mon XML/XSL) sont pires  :D


 
Pour les questions 2 et 3, le tableau façon ascii-art montre ce que je cherche à avoir avec la source donnée au début du post.
 
 
___________________________________
 
 
Pour faire ce que je veux, il faudrait dans mon "algo" :  
1) trouver la liste des auteurs rencontrés dans le fichier XML, sans doublon.
2) balayer le fichier pour chacun de ces auteurs et récupérer leurs projets respectifs
3) rajouter une ligne de somme pour chacun de ces auteurs
 
Je n'ai aucune idée de comment me débarasser des doublons dans le (1)
J'ai des problèmes avec la somme (voir ci-dessous)
 
___________________________________
 
J'ai fais des essais avec sum(), qui m'a systématiquement revoyé "0" ou "". [:lhalfelin]  
 

<x id=1><a>1</a><b>2</b><c>3</c></x>
<x id=2><a>3</a><b>4</b><c>5</c></x>
<x id=3><a>5</a><b>5</b><c>7</c></x>


 
Je sais que sum() est très fort si je veux pour chaque x faire a+b+c
Mais avec le XML dont je dispose, ce que je veux sommer est plutôt a[x id=1] + a[x id=2] + a[x id=3]
Sachant que les <x> sont très nombreux et que je ne souhaite que ceux qui contiennent, disons, une certaine valeur de <c> (dans mon exemple pratique c'est l'auteur).
 
 
Je vais jeter un oeil à xsl:sort

Reply

Marsh Posté le 17-07-2008 à 17:25:06    

Un début de réponse, il faut utiliser ce qu'on appelle une algo de 'munchian grouping', y'a un lien dans le code.
 

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<root>
<viewentries>
  <viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuPremierAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDuProjet</text>
    </entrydata>
    <entrydata columnnumber="2" name="nombre1">
      <text>12</text>
    </entrydata>
    <entrydata columnnumber="3" name="nombre2">
      <text>0</text>
    </entrydata>
    <!-- ... (il y a une dizaine d'autres valeurs numeriques similaires) -->
  </viewentry>
  <viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuPremierAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDunAutreProjet</text>
    </entrydata>
    <!-- ... -->
  </viewentry>
  <viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuSecondAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDEncoreUnAutreProjet</text>
    </entrydata>
    <!-- ... -->
  </viewentry>
  <!-- ... -->
</viewentries>
</root>
<!-- eof -->


 

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
<!-- general settings -->
  <xsl:output method="html" omit-xml-declaration="no" encoding="ISO-8859-1"/>
 
<!-- parameters -->
 
<!-- global variables -->
 
<!-- transformations -->
  <xsl:key name="nom-auteur" match="entrydata[@name='auteur']" use="text" />
 <!-- http://www.jenitennison.com/xslt/grouping/muenchian.xml -->
 
<xsl:template match="/">
  <xsl:text>DBG - Start XSLT
</xsl:text>
  <xsl:apply-templates /><!-- on amorce la tranformation -->
</xsl:template>
 
<xsl:template match="viewentries">
 
  <!-- boucle sur les noms d'auteur uniques ( basé sur muenchian grouping) -->
  <xsl:for-each select="//entrydata[@name='auteur'][generate-id() = generate-id( key('nom-auteur', text)[1])]">
 
    <xsl:variable name="vAuteurId" select="text" />
 
    <!-- relancer le traitement pour tous les viewentry d'un auteur particulier -->
    <auteur nom="{$vAuteurId}">
      <xsl:apply-templates select="//viewentry[entrydata[@name='auteur' and text=$vAuteurId]]" />
    </auteur><xsl:text><!-- retour a la ligne en xslt ! -->
</xsl:text>
 
  </xsl:for-each>
</xsl:template>
 
<xsl:template match="viewentry">
  <!-- le traitement c'est de copier-coller le sous-arbre entierement -->
  <xsl:copy-of select="."/>
</xsl:template>
 
</xsl:stylesheet>
<!-- eof -->


 
L'algo de regroupage est un peu chaude de premier abord, j'en conviens... le résultat est a la hauteur:

DBG - Start XSLT
 
<auteur nom="NomDuPremierAuteur"><viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuPremierAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDuProjet</text>
    </entrydata>
    <entrydata columnnumber="2" name="nombre1">
      <text>12</text>
    </entrydata>
    <entrydata columnnumber="3" name="nombre2">
      <text>0</text>
    </entrydata>
    <!-- ... (il y a une dizaine d'autres valeurs numeriques similaires) -->
  </viewentry><viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuPremierAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDunAutreProjet</text>
    </entrydata>
    <!-- ... -->
  </viewentry></auteur>
<auteur nom="NomDuSecondAuteur"><viewentry>
    <entrydata columnnumber="0" name="auteur">
      <text>NomDuSecondAuteur</text>
    </entrydata>
    <entrydata columnnumber="1" name="projet">
      <text>NomDEncoreUnAutreProjet</text>
    </entrydata>
    <!-- ... -->
  </viewentry></auteur>
 


 
Enfin, je pense...  
 

Reply

Marsh Posté le 18-07-2008 à 11:00:58    

Waouw !!! :love:  
 

<xsl:for-each select="//entrydata[@name='auteur'][generate-id() = generate-id( key('nom-auteur', text)[1])]">

Je pense que j'aurais été incapable de faire ça moi-même à ce stade de connaissance du XSLT.  
 
J'essaye ça et j'update.

Reply

Marsh Posté le 18-07-2008 à 11:58:31    

Juste pour dire que ça marche impeccablement bien, j'en reviens pas =D
 
Je m'attaque maintenant à la somme. Je suppose qu'il va me falloir une template par variable numérique de la viewentry ? Ah zut y'en a 12 !

Reply

Marsh Posté le 18-07-2008 à 12:08:47    

Si c'est possible je ferais deux stylesheets:  
- un qui modifie la structure pour le regroupement par auteur
- un autre qui s'occuppe de la présentation finale

Reply

Marsh Posté le 18-07-2008 à 12:12:30    

Lhalfelin a écrit :

Waouw !!! :love:  
 

<xsl:for-each select="//entrydata[@name='auteur'][generate-id() = generate-id( key('nom-auteur', text)[1])]">

Je pense que j'aurais été incapable de faire ça moi-même à ce stade de connaissance du XSLT.  
 
J'essaye ça et j'update.


 
C'est le muenchian grouping qui fonctionne comme ça, faut bien décomposer l'expression pour comprendre... revoir le fonctionnement de xsl:key ça aide aussi ( faut je m'y remets à chaque fois, je te rassure).  :whistle:  

Reply

Marsh Posté le 18-07-2008 à 13:13:07    

J'ai gardé tout ensemble
 

<xsl:value-of select="sum(//viewentry[entrydata[@name='auteur' and text=$vAuteurId]]/entrydata[@columnnumber='2']/text)" />


 
Copié collé 12 fois, avec column number qui change
 
Merci un million de fois pour ton aide !  :jap:
 
 
/edit : si j'ai le temps je ferai une récursivité pour être plus propre et incrémenter le columnnumber dynamiquement.


Message édité par Lhalfelin le 18-07-2008 à 15:15:26
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed