• PYTHON > fichier ODT

      use relatorio to be able to produce odt. You can have a look at the doc here

      Générer un fichier ODT avec Python

      Il y a quelques semaines je me suis retrouvé devant un problème particulier: générer un fichier ODT depuis un programme python pour le site WEB de la suite CilAPPS. A noter que j’utilise le framework Django et que certains des aspects de mon code y sont liés.

      Un fichier ODT est un fichier ZIP contenant des médias et des fichiers XML, il est donc parfaitement possible d’en créer un. Le problème qui peut se poser c’est comment le réaliser simplement.

       

      La solution qui a finalement été choisis est d’utiliser la bibliothèque python py3o dans un exemple est disponible sur la page du projet. Elle est simple d’utilisation mais il y manque certaines fonctionnalités. Je vais décrire dans cette article les différents problème que j’ai rencontré et la solution mise en place.

      Pour faciliter la mise en place des points qui seront listé ci-dessous j’ai créé une classe nommée ODTParser(). J’explique à la fin de cette article comment installer ODTParser() et patcher py3o.

      Le fonctionnement de py3o

      Je ne vais pas détailler ici le fonctionnement de la bibliothèque, uniquement revenir sur la base. Pour plus de détails vous pouvez vous rendre sur le site officiel.

      Le principe de base consiste à:

      • créer un document ODT qui servira de template. Dans ce document vous pouvez utiliser des variables personnalisées et des lien hypertexte pour insérer des blocs if et for.
      • dans votre programme Python, vous faites la fusion entre le template et un dictionnaire Python pour obtenir le document final. Les variables du template font référence aux du dictionnaire Python.

      Premier problème: le caractère n ne fonctionne pas

      Lorsque vous insérer du texte multi-lignes (composé de n), les n sont affichés tels quelle et non interprétés.

      La solution pour qu’ils soient interprétées consiste à utiliser la méthode Markup() de genshi.core. tonthon tonthon a mis en ligne un exemple dans ce ticket. Le principal défaut de cette solution c’est que vous ne pourrez plus insérer de caractères composées de ‘<>’ car ils seront tous interprété par LibreOffice ce qui provoquera des erreurs.

       

      La méthode xstr(): prend en paramètre un texte et le transforme selon:

      • Si il s’agit d’un booléen elle retourne ‘Yes’ ou ‘False’
      • Si il s’agit d’une date elle retourne strftime(“%d/%m/%y %H:%M”)
      • Si il faut échapper le texte elle retourne sax_escape()
      • Si le texte est “safe” elle retourne Markup()

      Second problème: Insérer un texte HTML

      Ce problème est complexe, il est nécessaire de convertir les balises HTML dans le format ODT. J’ai écrit un morceau de code qui permet de convertir certains caractère HTML, en particulier les style gras, italique.

       

      La méthode html(): prend en paramètre un texte et retourne un texte dans le format ODT. Toute les balises HTML sont supprimée, certains sont remplacé par l’équivalent ODT.

      Les balises et styles css gérés sont:

      • les balises: p, li, ul, ol, h1, h2, h3, br, strong, em, span et a
      • les styles: text-decoration: underline, text-align: justify, color:, background-color

      Il est indispensable que le nom de la variable personnalisée finisse par __ISHTML et que dans le template la variable n’ait pas de style (dans le menu style sélectionner “Effacer le formattage”), ce qui permettra à py3o d’insérer la valeur correctement dans l’ODT.

       

      Cette méthode crée une liste de style ODT qu’il est ensuite nécessaire d’insérer dans py3o.

      t = Template("template.odt", output)
      
      infos.conclusion__ISHTML = odt_parser.html(conclusion)
      
      t.styles = odt_parser.get_styles()
      
      data = dict(infos = infos)
      
      t.render(data)

       

      Le code est sensible, si vous ne mettez pas __HTML ou si dans le template la variable personnalisée a un style, vous aurez une erreur lors de l’ouverture du document final.

      Pour éviter une maximum les erreurs toute les balises HTML non converties sont supprimées, le formatage est donc perdu mais le contenu est gardé.

      lire du contenu saisis par un utilisateur dans un document ODT

      CilAPPS est une application permettant à des questions dans le cadre d’audit, de diagnostic ou de questionnaire. L’utilisateur lit une question, y répond puis est redirigé vers la question suivante. Il était nécessaire que l’utilisateur puisse générer un document ODT contenant les questions, répondre à chaque question puis charger les réponses dans CilAPPS.

      Maintenant qu’on utilise la classe ODTParser avec py3o il est possible de générer le document ODT. mais comment récupérer les réponses ensuite ? il est nécessaire pour cela d’utiliser les contrôleurs de formulaires de LibreOffice. Le principe est le même qu’avec les contrôleurs de formulaires HTML, on insérer des contrôleurs textarea, checkbox ou radiobutton en leur associant à chacun un nom unique.

       

      Pour réaliser ceci 4 méthodes ont été ajoutés dans ODTParser:

      • pour créer le fichier ODT
        • form_textarea()
        • form_radio()
        • form_checkbox()
      • pour lire le fichier ODT
        • get_controls_values()

      Il est indispensable que le nom de la variable personnalisée finisse par __ISFORM et que dans le template la variable n’ait pas de style (dans le menu style sélectionner “Effacer le formattage”), ce qui permettra à py3o d’insérer la valeur correctement dans l’ODT.

       

      Exemple pour générer le fichier ODT

      t = Template("referentiel_export.odt", output)
      
      comment__ISFORM = odt_parser.form_textarea('comment','default value', '14cm', '4cm')
      
      t.forms = odt_parser.get_forms()
      
      data = dict(comment__ISFORM=comment__ISFORM)
      
      t.render(data)

       

      Exemple pour lire le fichier une fois complété par l’utilisateur

      inline = zipfile.ZipFile(fichier, 'r')
      
      content_xml = lxml.etree.parse(StringIO(inline.read('content.xml')))
      
      odt_parser = ODTParser()
      
      controls = odt_parser.get_controls_values(content_xml)
      
      for control incontrols['textarea']:
      
      print '%s: %s' % (control['name'], control['value'])

      Installer ODTParser()

      Vous pouvez télécharger ODTParser() ici.

       

      Il est également nécessaire de patcher la bibliothèque py3o pour qu’elle gère les styles et les formulaires.

      Dans le fichier template/main.py

      class Template(object):
         templated_files = ['content.xml', 'styles.xml', 'META-INF/manifest.xml']
      +  styles = []
      +  forms = []

       

      """Replace user-type text fields that start with "py3o." with genshi
      instructions.
      """
      
      field_expr = "//text:user-field-get[starts-with(@text:name, 'py3o.')]"
      
      for content_tree in self.content_trees:
      
      +    ##ajouts des styles pour les zones HTML
      +    for userfield in content_tree.xpath(
      +       "//office:automatic-styles",
      +       namespaces=self.namespaces
      +    ):
      +       for style in self.styles:
      +          userfield.append(lxml.etree.fromstring(style))
      +    ##ajouts des forms
      +    for userfield in content_tree.xpath(
      +       "//office:forms",
      +       namespaces=self.namespaces
      +    ):
      +       if self.forms and self.forms != '':
      +          userfield.append(lxml.etree.fromstring(self.forms))
      
      for userfield in content_tree.xpath(

       

      if value_type == 'percentage':
         del npar.attrib[value_attr]
         value = "format_percentage(%s)" % value
         npar.attrib[value_type_attr] = "string"
      
      + #HTML FORM
      + if value.endswith('__ISHTML') or value.endswith('__ISFORM'):
      +    parent_parent = parent.getparent()
      +    parent_parent.insert(parent_parent.index(parent), userfield)
      +    parent_parent.remove(parent)
      +    parent = parent_parent
      
      attribs = dict()
      attribs['{%s}strip' % GENSHI_URI] = 'True'
      attribs['{%s}content' % GENSHI_URI] = value

 

Aucun commentaire

 

Laissez un commentaire