Difference between revisions of "Écrire un rapport"

From Gramps
Jump to: navigation, search
m (Rapport de classe)
m (La classe du rapport)
Line 104: Line 104:
 
La classe du rapport va initialiser plusieurs variables pour l'utilisateur, basées sur les valeurs définies. Il s'agit de :
 
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 exemple prêt pour la sortie. Il s'agit du type [http://www.gramps-project.org/docs/ docgen], et elle est '''pas''' un fichier normal objet.
+
; self.doc : le document ouvert, prêt pour la sortie. Du type [http://www.gramps-project.org/docs/ docgen], ceci n'est '''pas''' un fichier objet normal.
; self.database: Le [http://www.gramps-project.org/docs/gen/gen_lib.html#module-gen.lib GrampsDbBase] objet de base de données
+
; self.database : l'objet [http://www.gramps-project.org/docs/gen/gen_lib.html#module-gen.lib GrampsDbBase] de base de données.
; self.options_class: Le [http://www.gramps-project.org/devdoc/api/2.2/private/ReportBase._ReportOptions.ReportOptions-class.html ReportOptions] classe
+
; self.options_class : la classe [http://www.gramps-project.org/devdoc/api/2.2/private/ReportBase._ReportOptions.ReportOptions-class.html ReportOptions].
  
Tout le reste du rapport devant définir les besoins afin de produire le rapport sera obtenu auprès de la <tt>options_class</tt>. 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.
+
Tout le reste du rapport devant définir les besoins afin de produire le rapport sera obtenu auprès de la classe <tt>options_class</tt>. 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.
  
Rapport de la classe '''doit''' fournir <tt>write_report()</tt>. Cette méthode devrait copier le rapport contenu dans le document déjà ouvert.
+
La classe du rapport '''doit''' fournir <tt>write_report()</tt>. Cette méthode doit copier le contenu du rapport dans le document déjà ouvert.
 
<pre>
 
<pre>
 
       def write_report(self):
 
       def write_report(self):
Line 117: Line 117:
 
           self.doc.end_paragraph()
 
           self.doc.end_paragraph()
 
</pre>
 
</pre>
Le reste du rapport est quasiment classe donnée au rapport écrivain. Selon les objectifs et la portée du rapport, il ya un montant de code en cause. Lorsque l'utilisateur génère le rapport dans n'importe quel mode, la construction de la classe va être exécuté, et ensuite la méthode <tt>write_report()</tt> sera appelée. Donc, si vous avez écrit que la méthode de cotation belle chose vraiment importante, assurez-vous qu'il est finalement appelée depuis <tt>write_report()</tt>. Sinon personne ne la verrez que si en regardant le code.
+
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 <tt>write_report()</tt> sera appelée. Donc, si vous avez écrit quelque chose de vraiment important, assurez-vous que ceci est appelée depuis <tt>write_report()</tt>. Sinon personne ne le verra à part en regardant dans le code.
  
 
=== Options de la classe ===
 
=== Options de la classe ===

Revision as of 14:25, 6 May 2010

Gramps-notes.png
This page has good information but needs a cleanup. Please make the Gramps Wiki more useful by re‑organizing this page and linking introductory material. Consider also updating the 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ée 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 constructeur 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éfinies 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.

Options de la classe

    * Options de la classe devrait découler de ReportOptions classe. 

Souvent 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)

Il devrait fixer de nouvelles options qui sont spécifiques aux fins du présent rapport, par des raisons de la set_new_options() qui définit options_dict et options_help dictionnaires:

      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"),
        }
  • Il devrait également permettre à la "semi-common" les options qui sont utilisées dans le présent rapport, par des raisons de la enable_options qui définit enable_dict dictionnaire. Les semi-commons sont les options que GRAMPS connaît, 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,
          }

Tous les options sont déjà prises en charge par la base de GRAMPS.

  • Pour tout mettre en place de nouvelles options dans la classe d'options, il doit être défini widgets pour fournir 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 la classe variable dictionnaire:
      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 pour l'utilisateur réglable paragraphe style doit être défini ici, pour former un 'défaut' feuille de style:
      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

The MenuReportOptions can be used in place of ReportOptions to present the user with a standard interface for running the report. Instead of parsing options, you generate a menu using one or more of the classes available in gen.plug.menu. All these are initialzed in the add_menu_options function (which is a required function when you inherit from MenuReportOptions). For example:

    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)

In this example a BooleanListOption object is created that presents the user with a group of check boxes, one is created for each call to add_button. Finally the object is added to the menu with menu.add_option(). The category name is used to generate tabs on the report dialog.

Then to access the selected values once the user runs the report, you make a call the the menu object from within the report's __init__ function. For example, to access the "what_types" that are selected from the menu above you would add the following code:

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()

In the example, the option class is retrieved by the get_option_by_name() function. The string must match the name you passed as the second argument to menu.add_option() when you created the menu. Then a list of the selected item titles is retrieved with get_selected() and stored as a class member for later use.

Implémentation

Définition de la classe Rapport Options

Dans cet exemple, aucune des options spéciales sont requises. 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 le rapport de classe

La mise en place effective de la DbSummary rapport est assez simple. Pas de travail supplémentaire doit être fait pour initialiser la classe, de sorte que les parents __init__ routine est appelée.

Tout le travail se fait dans le write_report(). Cette fonction utilise un GrampsCursor à itérer à travers la carte de la Person qui rassemble les objets et de simples statistiques.

La seule chose de toute complication la détermination de la plupart des communes patronyme. 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é. 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 devrait définir nom interne du rapport (de préférence, seule chaîne avec des caractères spéciaux, utilisable pour rendre l'identification de la ligne de commande et dans les options de stockage, ainsi que pour former le nom du fichier sain pour stocker ses propres modèles). Il 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 deux options de rapport classe et la classe doivent être passées à l'enregistrement.

Voici par exemple la déclaration d'enregistrement: Finalement, les classes options et report doivent passer 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 = ["[email protected]"]
category = CATEGORY_TEXT
require_active = False
reportclass = 'DbSummaryReport'
optionclass = 'DbSummaryOptions'
report_modes = [REPORT_MODE_GUI, REPORT_MODE_CLI]

Deux chaînes définissent le rapport de classe et d'options classe. L'argument report_modes argument est fixé à la somme de bits sage (le OU) de chacun des trois modes possibles: GUI (autonome rapport produit d'GRAMPS cours d'exécution dans une fenêtre), BKI (Livre en question), et CLI (interface de ligne de commande). Cela signifie que le rapport sera disponible dans les trois modes. Le reste devrait être d'explication.

Un exemplaire complet du rapport peut être téléchargé (1.66 KB) pour les essais et l'expérimentation.