Écrire un rapport

From Gramps
(Redirected from Report-writing tutorial/fr)
Jump to: navigation, search
Gramps-notes.png
This page needs a cleanup. Please help keep Gramps Wiki a useful resource by refactoring this page. Think also on screenshots.

Introduction

Ce tutoriel décrit les bases pour l'écriture d'un simple rapport utilisant l'infrastructure de GRAMPS. Il couvre les processus de gestion d'options, la construction d'un document et de création d'un rapport.

Les objectifs de ce rapport est de créer un rapport de synthèse. Il comportera les informations suivantes :

    * Le nombre de personnes dans la base de données
    * Le nombre de garçons et de filles
    * Le nombre de noms de famille uniques.
    * Le prénom le plus commun.

À partir de la version 3.2, sont également disponibles : un accès simple aux API de base de données, accompagné de Rapports rapides, Gramplets et Addons.

Aperçu

Avant d'entrer dans les détails, il est utile de noter que le rapport doit comporter trois éléments fondamentaux. Comme expliqué sur la page Addons, les éléments sont dans deux fichiers différents : le fichier code source (càd : report.py) et le fichier d'enregistrement du greffon Gramps (càd : report.gpr.py).

report.py

Rapport de classe

C'est ce code qui contient les données de la base de données GRAMPS et les structure dans le document.

Cette structure peut ensuite être imprimée, visualisée, ou écrite dans une variété de formats.

Cette classe utilise l'interface docgen, fournissant les détails pour le format de sortie.

Options de la classe

C'est le code qui fournit les moyens nécessaires à l'obtention des options pour le rapport, utilisant une variété de mécanismes disponibles.

report.gpr.py

Déclaration d'enregistrement

Ceci initialise le report par un simple appel de register() dans le module.

Il est trivial, mais sans ceci votre rapport ne sera pas disponible dans GRAMPS, même si il est par ailleurs parfaitement écrit.

Un rapport peut être généré potentiellement comme un rapport unique, comme un item de livre GRAMPS, et comme un rapport en ligne de commande. L'enregistrement détermine quels sont les modes activés pour un rapport donné. Le rapport classe ne nécessite aucune connaissance du mode. La classe d'options est là pour fournir des options d'interface pour tous les modes disponibles.

L'interface du document

GRAMPS essaye d'ignorer la sortie du document quel que soit le rapport. En codant la classe docgen, le rapport peut générer le document selon le format désiré par l'utilisateur final. Le document transmis au rapport (self.doc) peut être une page HTML, OpenDocument, PDF ou d'autres formats supportés par l'utilisateur. Le rapport n'a pas à se préoccuper des détails du format de sortie, puisque tous les détails sont pris en charge par l'objet document.

Un document est composé de paragraphes, des tableaux, des graphiques et d'objets. Les tableaux et les graphiques des objets ne seront pas développés dans ce tutoriel.

Le rapport définit une série de paragraphe et styles, ainsi que leur application par défaut. L'utilisateur peut passer outre la définition de chaque modèle, lui permettant de personnaliser le rapport. Chaque style de paragraphe doit être nommé spécifiquement, pour prévenir les collisions lors de l'impression dans un format livre. Il est recommandé de préfixer chaque style de paragraphe avec un code à trois lettres uniques au rapport.

Paragraphe et styles sont définis dans make_default_style() : la fonction de la classe d'options. Les paragraphes sont regroupés dans StyleSheet, qui est défini dans make_default_style(). Pour l'exemple du rapport (DbSummary), les styles de paragraphe sont définies comme suit:

    def make_default_style(self, default_style):

        # Define the title paragraph, named 'DBS-Title', which uses a
        # 18 point, bold Sans Serif font with a paragraph that is centered

        font = docgen.FontStyle()
        font.set_size(18)
        font.set_type_face(docgen.FONT_SANS_SERIF)
        font.set_bold(True)

        para = docgen.ParagraphStyle()
        para.set_header_level(1)
        para.set_alignment(docgen.PARA_ALIGN_CENTER)
        para.set_font(font)
        para.set_description(_('The style used for the title of the page.'))

        default_style.add_style('DBS-Title',para)

        # Define the normal paragraph, named 'DBS-Normal', which uses a
        # 12 point, Serif font.

        font = docgen.FontStyle()
        font.set_size(12)
        font.set_type_face(docgen.FONT_SERIF)

        para = docgen.ParagraphStyle()
        para.set_font(font)
        para.set_description(_('The style used for normal text'))

        default_style.add_style('DBS-Normal',para)

Définir les classes

La classe du rapport

La classe du rapport doit hériter des contenus du rapport. Le développeur doit définir deux arguments (hors de la classe elle-même, le plus souvent signalés par un 'self'):

    * La base de données GRAMPS
    * Classe options

Le premier est la base de données de travail. La deuxième est l'instance de la classe d'options définie dans le même rapport, voir la section suivante. Voici l'exemple d'une définition de classe pour un rapport:

  from ReportBase import Report, ReportUtils, ReportOptions

  class ReportClassName(Report):
      def __init__(self,database,person,options_class):
          Report.__init__(self,database,person,options_class)

La classe du rapport va initialiser plusieurs variables pour l'utilisateur, basées sur les valeurs définies. Il s'agit de :

self.doc 
le document ouvert, prêt pour la sortie. Du type docgen, ceci n'est pas un fichier objet normal.
self.database 
l'objet GrampsDbBase de base de données.
self.options_class 
la classe ReportOptions.

Tout le reste du rapport devant définir les besoins afin de produire le rapport sera obtenu auprès de la classe options_class. Par exemple, vous pouvez avoir besoin d'inclure du code supplémentaires dans la construction de la classe afin d'obtenir les options que vous avez définies pour le rapport.

La classe du rapport doit fournir write_report(). Cette méthode doit copier le contenu du rapport dans le document déjà ouvert.

      def write_report(self):
          self.doc.start_paragraph("ABC-Title")
          self.doc.write_text(_("Some text"))
          self.doc.end_paragraph()

Le reste de la classe est plus l'affaire du développeur. Selon les objectifs et la portée du rapport, il y a une taille du code. Lorsque l'utilisateur génère le rapport dans n'importe quel mode, la construction de la classe va être exécutée, puis la méthode write_report() sera appelée. Donc, si vous avez écrit quelque chose de vraiment important, assurez-vous que ceci est appelée depuis write_report(). Sinon personne ne le verra, à part en regardant dans le code.

La classe Options

  • La classe Options doit dériver de la classe ReportOptions. En général dans un simple rapport, la classe MenuReportOptions en est dérivée. MenuReportOptions va fournir l'essentiel de la gestion du widget de bas niveau.

Utilisation de ReportOptions

  class OptionsClassName(ReportOptions):
      def __init__(self,name,person_id=None):
          ReportOptions.__init__(self,name,person_id)

Elle doit fixer les nouvelles options spécifiques au rapport, avec set_new_options() qui définit les dictionnaires options_dict et options_help:

      def set_new_options(self):
          # Options specific for this report
          self.options_dict = {
              'my_fist_option'    : 0,
              'my_second_option'  : '',
          }
          self.options_help = {
              'my_fist_option'    : ("=num","Number of something",
                                     [ "First value", "Second value" ],
                                     True),
              'my_second_option'  : ("=str","Some necessary string for the report",
                                     "Whatever String You Wish"),
        }
  • Elle doit également permettre les options "semi-common" utilisées dans le rapport, en passant par la méthode enable_options qui définit le dictionnaire enable_dict. Les semi-commons sont les options connues par GRAMPS, mais qui ne sont pas nécessairement présentes dans tous les rapports:
      def enable_options(self):
          # Semi-common options that should be enabled for this report
          self.enable_dict = {
              'filter'    : 0,
          }

Toutes les options sont déjà prises en charge par le coeur de GRAMPS.

  • Pour mettre en place de nouvelles options dans la classe d'options, on doit définir des widgets d'interface, fournissant les moyens de changer ces options par le biais du dialogue. En outre, il faut définir des méthodes pour extraire les valeurs de ces options dans le widgets et de les mettre ainsi dans le dictionnaire de classe variable :
      def add_user_options(self,dialog):
          option_menu = gtk.OptionMenu()
          self.the_menu = gtk.Menu()

          for item_index in range(10):
              item = _("Item numer %d") % item_index
              menuitem = gtk.MenuItem(item)
              menuitem.show()
              self.the_menu.append(menuitem)

          option_menu.set_menu(self.the_menu)
          option_menu.set_history(self.options_dict['my_first_option'])

          dialog.add_option(_('My first option'),option_menu)

          self.the_string_entry = gtk.Entry()
          if self.options_dict['my_second_option']:
              self.the_string_entry.set_text(self.options_dict['my_second_option'])
          else:
              self.the_string_entry.set_text(_("Empty string"))
          self.the_string_entry.show()
          dialog.add_option(_('My second option'),self.the_string_entry)

      def parse_user_options(self,dialog):
          self.options_dict['my_second_option'] = unicode(self.the_string_entry.get_text())
          self.options_dict['my_first_option'] = self.the_menu.get_history()
  • Enfin, les définitions par défaut réglables par l'utilisateur. Le paragraphe et le style doivent être définis ici, formant une feuille de style par 'défaut' :
      def make_default_style(self,default_style):
          f = docgen.FontStyle()
          f.set_size(10)
          f.set_type_face(docgen.FONT_SANS_SERIF)
          p = docgen.ParagraphStyle()
          p.set_font(f)
          p.set_description(_("The style used for the person's name."))
          default_style.add_style("ABC-Name",p)

Utilisation de MenuReportOptions

MenuReportOptions peut être utilisé à la place de ReportOptions, fournissant à l'utilisateur une interface standard pour le rapport. Au lieu de passer par des options, vous générez un menu parmi une ou plusieurs classes disponibles dans gen.plug.menu. Elles sont initialisées dans la fonction add_menu_options (qui est une fonction requise quand vous héritez depuis MenuReportOptions). Par exemple:

    def add_menu_options(self, menu):
        """
        Add options to the menu for this report.
        """
        category_name = _("Report Options")
        what_types = BooleanListOption(_('Select From:'))
        what_types.add_button(_('People'), True)
        what_types.add_button(_('Families'), False)
        what_types.add_button(_('Places'), False)
        what_types.add_button(_('Events'), False)
        menu.add_option(category_name, "what_types", what_types)

Dans cet exemple un objet BooleanListOption est créé qui fournit à l'utilisateur un groupe de case à cocher, créées par add_button. Enfin l'objet est ajouté au menu avec menu.add_option(). Le nom de la catégorie est utilisé pour générer des tabulations dans le dialogue du rapport.

Puis pour accéder aux valeurs sélectionnées une fois que l'utilisateur utilise le rapport, vous appelez l'objet menu depuis la fonction __init__ du rapport. Par exemple, pour accéder à "what_types" sélectionné depuis le menu ci-dessus, vous ajouterez le code suivant :

class ExampleReport(Report):

    def __init__(self, database, options_class):

        Report.__init__(self, database, options_class)

	menu_option = options_class.menu.get_option_by_name('what_types')
        self.what_types = menu_option.get_selected()

Dans cet exemple, la classe option est rapportée par la fonction get_option_by_name(). La chaîne doit correspondre au nom qui vous avez saisi comme second argument dans menu.add_option() lorsque que vous avez créé le menu. Enfin une liste des items sélectionnés est rapportée avec get_selected() et stockée comme faisant partie d'une classe pour une utilisation future.

Implémentation

Définition de la classe Options du rapport

Dans cet exemple, aucune option particulière n'est requise. Cela rend la classe d'options très simple. Tout ce qui est nécessaire pour définir les styles par défaut.

class DbSummaryOptions(ReportOptions):

    def __init__(self, name, person_id=None):

        ReportOptions.__init__(self, name, person_id)

    def make_default_style(self, default_style):

        # Define the title paragraph, named 'DBS-Title', which uses a
        # 18 point, bold Sans Serif font with a paragraph that is centered

        font = docgen.FontStyle()
        font.set_size(18)
        font.set_type_face(docgen.FONT_SANS_SERIF)
        font.set_bold(True)

        para = docgen.ParagraphStyle()
        para.set_header_level(1)
        para.set_alignment(docgen.PARA_ALIGN_CENTER)
        para.set_font(font)
        para.set_description(_('The style used for the title of the page.'))

        default_style.add_style('DBS-Title',para)

        # Define the normal paragraph, named 'DBS-Normal', which uses a
        # 12 point, Serif font.

        font = docgen.FontStyle()
        font.set_size(12)
        font.set_type_face(docgen.FONT_SERIF)

        para = docgen.ParagraphStyle()
        para.set_font(font)
        para.set_description(_('The style used for normal text'))

        default_style.add_style('DBS-Normal',para)

Définir la classe du rapport

La mise en place effective du rapport DbSummary est assez simple. Pas de travail supplémentaire pour initialiser la classe, de sorte que la routine parent __init__ est appelée.

Tout le travail se fait dans write_report(). Cette fonction utilise GrampsCursor pour itérer à travers les objects, rassemblant les objets tels de simples statistiques.

La seule chose un peu plus compliqué étant la détermination des patronymes les plus courants. Un dictionnaire Python est utilisé pour stocker le nombre de fois que chaque nom est utilisé. Chaque fois qu'un nom est rencontré, la valeur dans le dictionnaire est incrémentée. Les résultats sont ensuite chargés dans une liste et classés, ce qui nous permet de trouver le nom le plus courant en regardant la dernière inscription dans la liste.

class DbSummaryReport(Report):

    def __init__(self, database, person, options_class):

        Report.__init__(self, database, person, options_class)

    def write_report(self):

        cursor = self.database.get_person_cursor()

        data = cursor.first()

        males = 0
        females = 0
        total = 0
        surname_map = {}
        while data:
            person = RelLib.Person()
            person.unserialize(data[1])

            if person.get_gender() == RelLib.Person.MALE:
                males += 1
            if person.get_gender() == RelLib.Person.FEMALE:
                females += 1
            total += 1

            surname = person.get_primary_name().get_surname()

            if surname_map.has_key(surname):
                surname_map[surname] += 1
            else:
                surname_map[surname] = 1

            data = cursor.next()
        cursor.close()

        slist = []
        for key in surname_map.keys():
            slist.append((surname_map[key],key))
        slist.sort()

        self.doc.start_paragraph("DBS-Title")
        self.doc.write_text(_("Database Summary"))
        self.doc.end_paragraph()

        self.doc.start_paragraph('DBS-Normal')
        self.doc.write_text(_('Number of males : %d') % males)
        self.doc.end_paragraph()

        self.doc.start_paragraph('DBS-Normal')
        self.doc.write_text(_('Number of females : %d') % females)
        self.doc.end_paragraph()

        self.doc.start_paragraph('DBS-Normal')
        self.doc.write_text(_('Total people : %d') % total)
        self.doc.end_paragraph()

        self.doc.start_paragraph('DBS-Normal')
        self.doc.write_text(_('Number of unique surnames : %d') % len(slist))
        self.doc.end_paragraph()

        self.doc.start_paragraph('DBS-Normal')
        self.doc.write_text(_('Most common surname : %s') % (slist[-1][1]))
        self.doc.end_paragraph()

L'enregistrement du rapport

  • L'enregistrement est défini dans un fichier nom.gpr.py.
  • L'enregistrement doit définir un nom interne pour le rapport (de préférence, une seule chaîne sans caractères spéciaux, utilisable pour l'identification en ligne de commande et dans les options de stockage, ainsi que pour fournir un nom de fichier nécessaire au stockage de ses propres modèles). On doit aussi définir :
    • la catégorie du rapport (texte / graphiques / Code)
    • le nom pour la traduction (celui de l'affichage dans les menus)
    • les modes qui doivent être activés pour le rapport (autonome, livre, ligne de commande).
  • Si le rapport a besoin d'une personne active pour fonctionner, alors require_active doit être défini comme True.
  • Enfin, les classes options et report doivent être passées par l'enregistrement.

Voici un exemple d'enregistrement :

register(REPORT,
id    = 'un identifiant unique'
name  = _("Nom du plugin")
description =  _("Produces a ...")
version = '1.0'
gramps_target_version = '3.2'
status = STABLE
fname = 'filename.py'
authors = ["John Doe"]
authors_email = ["jdoe@example.com"]
category = CATEGORY_TEXT
require_active = False
reportclass = 'DbSummaryReport'
optionclass = 'DbSummaryOptions'
report_modes = [REPORT_MODE_GUI, REPORT_MODE_CLI]

Deux chaînes définissent la classe du rapport et la classe d'options. L'argument report_modes est défini pour être une somme de bits (le OR statement) parmi les modes disponibles : GUI (rapport autonome produit depuis GRAMPS dans une fenêtre), BKI (article du livre), et CLI (interface de la ligne de commande). Cela signifie que le rapport sera disponible dans les trois modes.