generation/planning.py
class PlanningHoraireModifications(PlanningModifications):
    def __init__(self, site, debut):
        PlanningModifications.__init__(self, site, debut)
        self.template = 'Planning horaire.ods'
        self.default_output = "Planning horaire %s.ods" % str(debut)

    def execute(self, filename, dom):
        if filename == 'meta.xml':
            self.GetMetas(dom)
            return None
        elif filename != 'content.xml':
            return None

        date_fin = self.debut + datetime.timedelta(self.metas["Periodicite"])
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table")[0]
        lines = table.getElementsByTagName("table:table-row")
        templates = lines[18:20]

        # Les titres des pages
        ReplaceFields(lines, [('date-debut', self.debut),
                              ('date-fin', date_fin),
                              ('numero-semaine', self.debut.isocalendar()[1])])

        inscriptions = database.creche.select_inscriptions(self.debut, date_fin)
        lignes = [Ligne(inscription) for inscription in inscriptions]
        lignes = GetEnfantsTriesParGroupe(lignes)

        for index, ligne in enumerate(lignes):
            # if index != 0 and index % 24 == 0:
            #     for i, row in enumerate(lignes_entete):
            #         clone = row.cloneNode(1)
            #         table.insertBefore(clone, total_template)
            #         if i == 4:
            #             table.removeChild(template)
            #             template = clone

            inscrit = ligne.inscrit
            inscription = ligne.inscription
            if inscription.groupe is None or len(templates) <= inscription.groupe.ordre:
                template = templates[0]
            else:
                template = templates[inscription.groupe.ordre]
            line = template.cloneNode(1)

            # Les infos
            ReplaceFields(line, GetInscritFields(inscrit) + GetInscriptionFields(inscription))

            # Les présences
            for jour in range(5):
                date = self.debut + datetime.timedelta(jour)
                journee = inscrit.GetJournee(date)
                fields = [("arrivee", journee.GetHeureArrivee() if journee else ""),
                          ("depart", journee.GetHeureDepart() if journee else "")]
                ReplaceFields(GetCell(line, 2 + 2 * jour), fields)
                ReplaceFields(GetCell(line, 3 + 2 * jour), fields)

            table.insertBefore(line, templates[0])

        for template in templates:
            table.removeChild(template)

        self.FillPermanences(lines)

    def FillPermanences(self, lines):
        for jour in range(5):
            liste = get_liste_permanences(self.debut + datetime.timedelta(jour))
            for start, end, inscrit in liste:
                if start <= 12.5 * 12:
                    line = lines[3 + int(float(start) / 12 - 7.5)]
                elif start <= 17.75 * 12:
                    line = lines[10 + int(float(start) / 12 - 14.75)]
                else:
                    continue
                cell = GetCell(line, 7 + jour)
                cell.setAttribute("table:number-rows-spanned", str((end - start) / 12))
                ReplaceFields(cell, [("a", GetPrenom(inscrit)),
                                     ("b", GetPrenom(inscrit))])
        for line in lines[3:14]:
            ReplaceFields(line, [("a", ""),
                                 ("b", "")])
generation/commande_repas.py
class CommandeRepasModifications(object):
    title = "Commande de repas"
    template = "Commande repas.odt"

    def __init__(self, site, debut):
        self.multi = False
        self.default_output = "Commande repas %s.odt" % str(debut)
        self.debut = debut
        self.metas = {"Format": "KangourouKids"}
        self.email = None
        self.site = site

    def activite(self, jour, label):
        # print("activite", jour, label)
        date = self.debut + datetime.timedelta(jour)
        result = 0
        for inscrit in database.creche.select_inscrits(date, date, site=self.site):
            journee = inscrit.GetJournee(date)
            for slot in journee.timeslots:
                if slot.activity.label == label:
                    result += 1
        return str(result)

    def repas(self, jour, categories=None):
        # print("repas")
        try:
            return self.presents(jour, categories, 12 * 12, 14 * 12, True)
        except Exception as e:
            print(e)

    def gouters(self, jour, categories=None):
        return self.presents(jour, categories, 16 * 12, 17 * 12, False)

    def presents(self, jour, categories, debut, fin, allergies):
        if categories is None:
            categories = []
        elif isinstance(categories, str):
            categories = [categories]
        total = 0
        normal = 0
        allergiques = {}
        date = self.debut + datetime.timedelta(jour)
        for inscrit in database.creche.select_inscrits(date, date, site=self.site):
            if not categories or (inscrit.categorie and inscrit.categorie.nom in categories):
                journee = inscrit.GetJournee(date)
                if journee and IsPresentDuringTranche(journee, debut, fin):
                    total += 1
                    if allergies and inscrit.allergies:
                        if inscrit.allergies in allergiques:
                            allergiques[inscrit.allergies] += 1
                        else:
                            allergiques[inscrit.allergies] = 1
                    else:
                        normal += 1
        if total > 0 and normal == 0:
            return " ".join(["%dx%s" % (allergiques[allergie], allergie) for allergie in allergiques])
        elif allergiques:
            return "%d dont\n" % total + " ".join(["%dx%s" % (allergiques[allergie], allergie) for allergie in allergiques])
        elif total:
            return "%d" % total
        else:
            return ""
            
    def execute(self, filename, dom):
        if filename == 'meta.xml':
            metas = dom.getElementsByTagName('meta:user-defined')
            for meta in metas:
                # print meta.toprettyxml()
                name = meta.getAttribute('meta:name')
                value = meta.childNodes[0].wholeText
                if meta.getAttribute('meta:value-type') == 'float':
                    self.metas[name] = float(value)
                else:
                    self.metas[name] = value
            return None
        elif filename != 'content.xml':
            return None

        # print(dom.toprettyxml())

        fields = GetCrecheFields(database.creche) + GetSiteFields(self.site)
        fields.append(('numero-semaine', self.debut.isocalendar()[1]))
        fields.append(('debut-semaine', date2str(self.debut)))
        fields.append(('fin-semaine', date2str(self.debut+datetime.timedelta(4))))
        fields.append(("repas", self.repas))
        fields.append(("gouters", self.gouters))
        fields.append(("activite", self.activite))

        ReplaceTextFields(dom, fields)
        
        return None
generation/rapport_frequentation.py
class RapportFrequentationModifications(object):
    title = "Rapport de fréquentation"
    template = "Rapport frequentation.ods"

    def __init__(self, site, annee):
        self.multi = False
        self.default_output = "Rapport frequentation %d.ods" % annee
        self.annee = annee
        self.factures = {}
        self.errors = {}
        self.email = None
        self.site = site
        self.metas = {"colonne-jour": 2,
                      "colonne-mois": -1,
                      "colonne-annee": 4,
                      "colonne-jours-ouverture": 6,
                      "ligne-taux-occupation": 5,
                      "colonne-total-jours-ouverture": 7,
                      "ligne-total-heures-presence": 3,
                      "ligne-total-jours-ouverture": -1,
                      "ligne-recap-jours-ouverture": -1,
                      "colonne-annee-heures-facturees": -1
                      }
    
    def execute(self, filename, dom):
        fields = GetCrecheFields(database.creche)
                      
        if filename == 'styles.xml':
            ReplaceTextFields(dom, fields)
            return []
        
        elif filename == 'meta.xml':
            metas = dom.getElementsByTagName('meta:user-defined')
            for meta in metas:
                # print meta.toprettyxml()
                name = meta.getAttribute('meta:name')
                value = meta.childNodes[0].wholeText
                if meta.getAttribute('meta:value-type') == 'float':
                    self.metas[name] = int(value)
                else:
                    self.metas[name] = value
            return None        

        elif filename == 'content.xml':
            if "Format" in self.metas and self.metas["Format"] == 1:
                ReplaceFields(dom, fields)
                spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
                feuille = spreadsheet.getElementsByTagName("table:table").item(0)
                lines = feuille.getElementsByTagName("table:table-row")
                template = lines[0:13]
                for line in template:
                    feuille.removeChild(line)
                after = lines[13]
                for mois in range(1, 13):
                    debut = datetime.date(self.annee, mois, 1)
                    fin = GetMonthEnd(debut)
                    if fin > datetime.date.today():
                        break

                    heures_accueil = GetHeuresAccueil(self.annee, mois, self.site)
                    heures_realisees = {}
                    heures_facturees = {}
                    for categorie in database.creche.categories:
                        nom = categorie.nom.lower()
                        heures_realisees[nom] = 0.0
                        heures_facturees[nom] = 0.0
                    total_heures_realisees = 0.0
                    total_heures_facturees = 0.0
                    inscrits = database.creche.select_inscrits(debut, fin, self.site)
                    for inscrit in inscrits:
                        inscriptions = inscrit.get_inscriptions(debut, fin)
                        if inscriptions and (self.site is None or inscriptions[0].site == self.site):
                            try:
                                facture = Facture(inscrit, self.annee, mois, NO_NUMERO)
                                facture_heures_realisees = facture.heures_realisees
                                if config.options & HEURES_CONTRAT:
                                    facture_heures_facturees = facture.heures_facture
                                else:
                                    facture_heures_facturees = facture.heures_facturees
                                if inscrit.categorie:
                                    nom = inscrit.categorie.nom.lower()
                                    heures_realisees[nom] += facture_heures_realisees 
                                    heures_facturees[nom] += facture_heures_facturees 
                                total_heures_realisees += facture_heures_realisees
                                total_heures_facturees += facture_heures_facturees
                            except CotisationException as e:
                                self.errors[GetPrenomNom(inscrit)] = e.errors

                    taux_remplissage = 0.0
                    if heures_accueil:
                        taux_remplissage = (100.0 * total_heures_facturees) / heures_accueil
                    
                    fields = [('mois', months[mois - 1]),
                              ('annee', self.annee),
                              ('heures-realisees', total_heures_realisees),
                              ('heures-facturees', total_heures_facturees),
                              ('taux-remplissage', taux_remplissage)
                              ]
                    
                    fields += [('heures-realisees-%s' % tmp, heures_realisees[tmp]) for tmp in heures_realisees]
                    fields += [('heures-facturees-%s' % tmp, heures_facturees[tmp]) for tmp in heures_facturees]
                    
                    # print fields
                    
                    for line in template:
                        clone = line.cloneNode(1)   
                        ReplaceFields(clone, fields)
                        feuille.insertBefore(clone, after)
                    
            else:
                ReplaceFields(dom, fields)
                spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
                tables = spreadsheet.getElementsByTagName("table:table")
                
                inscrits_annee = {}
                inscrits_annee_details = {}
                jours_mois = []
                total_heures_facturees = {}
    
                # les 12 mois
                colonne_jour = self.metas['colonne-jour']
                template = tables.item(0)
                for mois in range(1, 13):
                    debut = datetime.date(self.annee, mois, 1)
                    fin = GetMonthEnd(debut)
                    if fin > datetime.date.today():
                        break
        
                    inscrits = list(database.creche.select_inscrits(debut, fin, self.site))
                    inscrits.sort(key=lambda x: x.nom.lower())
                    if not inscrits:
                        continue
                    
                    jours = [jour for jour in range(debut.day, fin.day + 1) if datetime.date(debut.year, debut.month, jour) not in database.creche.jours_fermeture]
                    
                    table = template.cloneNode(1)
                    spreadsheet.insertBefore(table, template)
                    table.setAttribute("table:name", months[mois - 1])
                    columns = table.getElementsByTagName("table:table-column")
                    column_template = columns[colonne_jour]
                    lines = table.getElementsByTagName("table:table-row")
                    first_line = lines[0]
                    ReplaceTextFields(first_line, [('mois', months[mois - 1])])
                    line_template = lines[1]
                    sum_line = lines[2]
                    cell_template = line_template.getElementsByTagName("table:table-cell")[colonne_jour]
                    first_line_cell_template = first_line.getElementsByTagName("table:table-cell")[colonne_jour]
                    sum_line_cell_template = sum_line.getElementsByTagName("table:table-cell")[1]
                    for jour in jours:
                        column = column_template.cloneNode(1)
                        table.insertBefore(column, column_template)
                        cell = first_line_cell_template.cloneNode(1)
                        first_line.insertBefore(cell, first_line_cell_template)
                        ReplaceFields(cell, [('jour', jour)])
                        cell = cell_template.cloneNode(1)
                        line_template.insertBefore(cell, cell_template)
                        cell = sum_line_cell_template.cloneNode(1)
                        sum_line.insertBefore(cell, sum_line_cell_template)
                    table.removeChild(column_template)
                    first_line.removeChild(first_line_cell_template)
                    line_template.removeChild(cell_template)
                    sum_line.removeChild(sum_line_cell_template)
                    
                    for i, inscrit in enumerate(inscrits):
                        line = line_template.cloneNode(1)
                        table.insertBefore(line, line_template)
                        ReplaceFields(line, GetInscritFields(inscrit))
                        cells = line.getElementsByTagName("table:table-cell")
                        for j, jour in enumerate(jours):
                            date = datetime.date(debut.year, debut.month, jour)
                            state = inscrit.GetState(date)
                            # TODO hack to move to GetState ...
                            if database.creche.facturation_periode_adaptation == PERIODE_ADAPTATION_HORAIRES_REELS and state.state != ABSENCE_NON_PREVENUE:
                                inscription = inscrit.get_inscription(date)
                                if inscription and inscription.IsInPeriodeAdaptation(date):
                                    if state.heures_facturees != state.heures_realisees:
                                        state.heures_facturees = state.heures_realisees
                            if inscrit in total_heures_facturees:
                                total_heures_facturees[inscrit] += state.heures_facturees
                            else:
                                total_heures_facturees[inscrit] = state.heures_facturees
                            if not state.heures_facturees:
                                state.heures_facturees = ""
                            if not state.heures_realisees:
                                state.heures_realisees = ""
                            ReplaceFields(cells[colonne_jour + j], [('heures-realisees', state.heures_realisees),
                                                                    ('heures-facturees', state.heures_facturees)])
                        total_cell = cells[len(jours) + colonne_jour]
                        total_cell.setAttribute("table:formula", "of:=SUM([.%s%d:.%s%d])" % (GetColumnName(colonne_jour), i + 2, GetColumnName(colonne_jour - 1 + len(jours)), i + 2))
                        if i == 0:
                            IncrementFormulas(cells[len(jours) + colonne_jour + 2:], column=len(jours) - 1)
                            index = len(jours) + self.metas['colonne-jours-ouverture'] - 1
                            if index < len(cells):
                                cells[index].setAttribute("table:formula", "of:=COUNT([.%s1:.%s1])" % (GetColumnName(colonne_jour), GetColumnName(colonne_jour - 1 + len(jours))))
                                jours_mois.append("%s.%s2" % (months[mois - 1], GetColumnName(self.metas['colonne-jours-ouverture'] - 1 + len(jours))))
                                for cell in line_template.getElementsByTagName("table:table-cell")[len(jours) + 3:]:
                                    line_template.removeChild(cell)
                        key = GetPrenomNom(inscrit)
                        if key not in inscrits_annee:
                            inscrits_annee[key] = inscrit
                            inscrits_annee_details[key] = {}
                        inscrits_annee_details[key][mois] = "%s.%s%d" % (months[mois - 1], GetColumnName(colonne_jour + len(jours)), i + 2)
                    table.removeChild(line_template)
                    last_line = 1 + len(inscrits)
                    for i, cell in enumerate(sum_line.getElementsByTagName("table:table-cell")[1:2 + len(jours)]):
                        column = GetColumnName(colonne_jour + i)
                        cell.setAttribute("table:formula", "of:=SUM([.%s2:.%s%d])" % (column, column, last_line))
                    
                spreadsheet.removeChild(template)
                
                # La recap de l'annee
                colonne_mois = self.metas['colonne-mois']
                colonne_annee = self.metas['colonne-annee']
                table = tables[-1]
                lines = table.getElementsByTagName("table:table-row")
                
                if colonne_mois >= 0:
                    line = lines[0]
                    cells = line.getElementsByTagName("table:table-cell")
                    cell_template = cells[colonne_mois]
                    for m in range(12):
                        cell = cell_template.cloneNode(1)
                        ReplaceFields([cell], [('mois', months[m])])
                        line.insertBefore(cell, cell_template)
                    line.removeChild(cell_template)
                        
                template = lines[1]
                keys = list(inscrits_annee.keys())
                keys.sort()
                for i, key in enumerate(keys):
                    inscrit = inscrits_annee[key] 
                    line = template.cloneNode(1)
                    table.insertBefore(line, template)
                    ReplaceFields(line, GetInscritFields(inscrit) + [("total-heures-facturees", total_heures_facturees[inscrit])])
                    cells = line.getElementsByTagName("table:table-cell")
                    cells[colonne_annee].setAttribute("table:formula", "of:=SUM(%s)" % ';'.join(["[%s]" % c for c in inscrits_annee_details[key].values()]))
                    if i == 0 and self.metas['colonne-total-jours-ouverture'] >= 0:
                        index = self.metas['colonne-total-jours-ouverture']
                        if index < len(cells):
                            cells[index].setAttribute("table:formula", "of:=SUM(%s)" % ';'.join(["[%s]" % m for m in jours_mois]))
                            for cell in template.getElementsByTagName("table:table-cell")[colonne_annee + 1:]:
                                template.removeChild(cell)
                    if colonne_mois >= 0:
                        cell_template = cells[colonne_mois]
                        for m in range(1, 13):
                            cell = cell_template.cloneNode(1)
                            if m in inscrits_annee_details[key]:
                                cell.setAttribute("table:formula", "of:=%s" % inscrits_annee_details[key][m])
                            else:
                                cell.setAttribute("table:formula", "of:=0")
                            line.insertBefore(cell, cell_template)
                        line.removeChild(cell_template)
                    
                table.removeChild(template)
                
                ligne_recap = self.metas["ligne-recap-jours-ouverture"]
                if ligne_recap >= 0:
                    line = lines[ligne_recap] 
                    cells = line.getElementsByTagName("table:table-cell")
                    cell_template = cells[colonne_mois]
                    for m in jours_mois:
                        cell = cell_template.cloneNode(1)
                        cell.setAttribute("table:formula", "of:=%s" % m)
                        line.insertBefore(cell, cell_template)
                    line.removeChild(cell_template)
                
                total_cell = lines[self.metas['ligne-total-heures-presence']].getElementsByTagName("table:table-cell")[2]
                if colonne_mois >= 0:
                    colname = GetColumnName(colonne_annee + 11)
                else:
                    colname = 'E'
                total_cell.setAttribute("table:formula", "of:=SUM([.%s2:.%s%d])" % (colname, colname, 1 + len(keys)))
                
                if self.metas["ligne-total-jours-ouverture"] >= 0:
                    total_cell = lines[self.metas['ligne-total-jours-ouverture']].getElementsByTagName("table:table-cell")[2]
                    total_cell.setAttribute("table:formula", "of:=SUM(%s)" % ';'.join(["[%s]" % m for m in jours_mois]))
                
                ligne_taux_occuptation = self.metas['ligne-taux-occupation']
                if ligne_taux_occuptation >= 0:
                    taux_occupation_cell = lines[ligne_taux_occuptation].getElementsByTagName("table:table-cell")[2]
                    taux_occupation_cell.setAttribute("table:formula", "of:=E%d/G2" % (3+len(keys)))
                    demi_journees_reelles_cell = lines[6].getElementsByTagName("table:table-cell")[2]
                    demi_journees_reelles_cell.setAttribute("table:formula", "of:=J2*I2*C%d" % (6+len(keys)))
               
        return self.errors
generation/releve_siej.py
class ReleveSIEJModifications(object):
    def __init__(self, site, annee):
        self.multi = False
        self.template = 'Releve SIEJ.odt'
        self.default_output = "Releve SIEJ %d.odt" % annee
        self.site = site
        self.annee = annee
        self.debut, self.fin = datetime.date(annee, 1, 1), datetime.date(annee, 12, 31)
        self.errors = {}
        self.email = None
        self.metas = {}
        self.regimes = ["général et fonctionnaire", "agricole", "maritime", "autres"]

    def GetRegime(self, inscrit, date):
        regime = inscrit.GetRegime(date)
        if regime == 1 or regime == 2:
            return 0
        elif regime == 3:
            return 1
        elif regime == 6 or regime == 7:
            return 2
        else:
            return 3
        
    def calculeTable(self):
        self.table = [Regime(), Regime(), Regime(), Regime()]
        self.reel, self.previsionnel, self.facture, self.realise = 0, 0, 0, 0
        for inscrit in database.creche.select_inscrits(self.debut, self.fin):
            date = self.debut
            for mois in range(12):
                trimestreEnd = GetTrimestreEnd(date)
                try:
                    facture = Facture(inscrit, self.annee, mois+1, NO_NUMERO)
                    regime = self.GetRegime(inscrit, date)
                    if config.options & HEURES_CONTRAT:
                        facture_heures_facturees = facture.heures_facture
                    else:
                        facture_heures_facturees = facture.heures_facturees
                    if trimestreEnd <= datetime.date.today():
                        self.table[regime].reel_facture += facture_heures_facturees
                        self.table[regime].reel_realise += facture.heures_realisees
                        self.reel += facture_heures_facturees
                        self.facture += facture_heures_facturees
                        self.realise += facture.heures_realisees
                        
                    else:
                        self.table[regime].previsionnel_facture += facture_heures_facturees
                        self.table[regime].previsionnel_realise += facture.heures_realisees
                        self.previsionnel += facture_heures_facturees
                except CotisationException as e:
                    self.errors[GetPrenomNom(inscrit)] = e.errors                            
                date = GetNextMonthStart(date)

    def execute(self, filename, dom):
        if filename == 'meta.xml':
            metas = dom.getElementsByTagName('meta:user-defined')
            for meta in metas:
                # print meta.toprettyxml()
                name = meta.getAttribute('meta:name')
                value = meta.childNodes[0].wholeText
                if meta.getAttribute('meta:value-type') == 'float':
                    self.metas[name] = float(value)
                else:
                    self.metas[name] = value
            return None
                
        elif filename == 'styles.xml':
            ReplaceTextFields(dom, GetCrecheFields(database.creche))
            return []

        elif filename == 'content.xml':
            self.calculeTable()
            doc = dom.getElementsByTagName("office:text")[0]
            
            fields = GetCrecheFields(database.creche) + [('annee', self.annee),
                                                ('date-debut-reel', self.debut),
                                                ('date-fin-reel', GetTrimestreStart(datetime.date.today()) - datetime.timedelta(1)),
                                                ('date-debut-previsionnel', GetTrimestreStart(datetime.date.today())),
                                                ('date-fin-previsionnel', self.fin),
                                                ]
            
            trimestre = (datetime.date.today().month-1) / 3
            if trimestre > 0:
                fields.append(('trimestre', "%s trimestre" % ordinaux[trimestre-1]))
            
            ReplaceTextFields(doc, fields)
            
            # print doc.toprettyxml()
            
            for section in doc.getElementsByTagName('text:section'):
                    section_name = section.getAttribute('text:name')
                    if section_name == 'Regime':
                        section_regime = section
                        
            for i, regime in enumerate(self.table):
                section = section_regime.cloneNode(1)
                doc.insertBefore(section, section_regime)
                fields = [('regime', 'Régime %s' % self.regimes[i]),
                          ('reel-facture', regime.reel_facture),
                          ('reel-realise', regime.reel_realise),
                          ('previsionnel-facture', regime.previsionnel_facture),
                          ('previsionnel-realise', regime.previsionnel_realise),
                          ('total-facture', regime.reel_facture + regime.previsionnel_facture),
                          ('total-realise', regime.reel_realise + regime.previsionnel_realise),
                          ('reel', self.reel),
                          ('previsionnel', self.previsionnel),
                          ('total', self.reel + self.previsionnel),
                          ]
                    
                for table in section.getElementsByTagName("table:table"):
                    table_name = table.getAttribute("table:name")
                    if (i > 0 and table_name == 'Recap') or table_name == 'Facture' or table_name == 'Realise':
                        section.removeChild(table)                                   
                                         
                ReplaceTextFields(section, fields)
            
            # le tableau recap
            section = section_regime.cloneNode(1)
            doc.insertBefore(section, section_regime)
            for table in section.getElementsByTagName("table:table"):
                table_name = table.getAttribute("table:name")
                if table_name == 'Regime':
                    section.removeChild(table)
            fields = [('facture', self.facture),
                      ('realise', self.realise),
                      ('reel', self.reel),
                      ('previsionnel', self.previsionnel),
                      ('total', self.reel + self.previsionnel),
                      ]
            for i, regime in enumerate(self.table):
                taux = 100.0 * regime.reel_facture / self.facture
                fields.append(('taux-%d' % i, "%.2f %%" % taux))
            ReplaceTextFields(section, fields)
            
            doc.removeChild(section_regime)
            
            return self.errors
generation/etat_places.py
class EtatPlacesModifications(object):
    def __init__(self, site, annee):
        self.multi = False
        self.template = 'Etats places.ods'
        self.default_output = "Etats places %d.ods" % annee
        self.site = site
        self.annee = annee
        self.factures = {}
        self.errors = {}
        self.email = None
        self.site = None

    def execute(self, filename, dom):
        if filename == 'styles.xml':
            fields = GetCrecheFields(database.creche)
            ReplaceTextFields(dom, fields)
            return []

        elif filename == 'content.xml':
            spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
            tables = spreadsheet.getElementsByTagName("table:table")
            table = tables.item(0)
            lines = table.getElementsByTagName("table:table-row")
            # line_heures_ouvrees = lines[2]
    
            fields = GetCrecheFields(database.creche) + GetTarifsHorairesFields(database.creche, datetime.date(self.annee, 1, 1))
            fields.append(("annee", self.annee))
            ReplaceFields(lines, fields)
            
            date = datetime.date(self.annee, 1, 1)
            while date.year == self.annee:
                count = 0
                for inscrit in database.creche.inscrits:
                    inscription = inscrit.get_inscription(date)
                    if inscription and (not self.site or self.site == inscription.site):
                        state = inscrit.get_state(date)
                        if state > 0 and state & PRESENT:
                            count += 1
                cell = GetCell(GetRow(table, date.month+3), date.day)
                #print cell.toprettyxml()
                SetValue(cell, count)
                #print cell.toprettyxml()
                
                date += datetime.timedelta(1)
 
        return self.errors
generation/etat_presences.py
class EtatsPresenceModifications(object):
    def __init__(self, debut, fin, site, professeur, inscrit, selection):
        self.multi = False
        self.template = 'Etats presence.ods'
        self.default_output = "Etats presence"
        if site:
            self.default_output += " %s" % site.nom
        if professeur:
            self.default_output += " %s" % GetPrenomNom(professeur)
        if inscrit:
            self.default_output += " %s" % GetPrenomNom(inscrit)
        self.default_output += ".ods"
        self.debut, self.fin, self.site, self.professeur, self.inscrit, self.selection = debut, fin, site, professeur, inscrit, selection
        self.gauge = None
        self.email = None
        self.site = None
        
    def execute(self, filename, dom):
        if filename != 'content.xml':
            return None
        
        errors = {}
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table").item(0)
        lignes = table.getElementsByTagName("table:table-row")

        titres = GetValues(lignes.item(7))
        if database.creche.type != TYPE_GARDERIE_PERISCOLAIRE:
            for i in range(7):
                if 'Professeur : <critere-professeur>' in GetValues(lignes.item(i)):
                    table.removeChild(lignes.item(i))
                    break                
            if titres[2] == "Professeur":
                RemoveColumn(lignes, 2)
        if len(database.creche.sites) < 2:
            for i in range(7):
                if 'Site : <critere-site>' in GetValues(lignes.item(i)):
                    table.removeChild(lignes.item(i))
                    break
            if titres[1] == "Site":
                RemoveColumn(lignes, 1)
               
        # Les champs de l'entête
        ReplaceFields(lignes, [('debut', self.debut),
                               ('fin', self.fin),
                               ('critere-site', GetNom(self.site)),
                               ('critere-professeur', GetPrenomNom(self.professeur)),
                               ('critere-inscrit', GetPrenomNom(self.inscrit))])
        
        # Les lignes
        template = lignes.item(8)
        dates = self.selection.keys()
        dates.sort()
        for date in dates:
            for site, professeur, inscrit, heure_arrivee, heure_depart, heures, commentaire in self.selection[date]:
                ligne = template.cloneNode(1)
                fields = GetInscritFields(inscrit)
                fields.extend([('date', date),
                               ('debut-contrat', inscrit.get_inscription(date).debut),
                               ('fin-contrat', inscrit.get_inscription(date).fin),
                               ('heure-arrivee', GetHeureString(heure_arrivee)),
                               ('heure-depart', GetHeureString(heure_depart)),
                               ('site', GetNom(site)),
                               ('professeur-prenom', GetPrenom(professeur)),
                               ('professeur-nom', GetNom(professeur)),
                               ('heures', GetHeureString(heures)),
                               ('commentaire', commentaire)])
                ReplaceFields(ligne, fields)
                table.insertBefore(ligne, template)

        table.removeChild(template)
        if self.gauge:
            self.gauge.SetValue(90)
        return errors
generation/etats_inscriptions.py
class EtatsInscriptionsModifications(object):
    def __init__(self, site, date):
        self.multi = False
        self.template = 'Etats inscriptions.ods'
        self.site = site
        if date is None:
            self.date = datetime.date.today()
        else:
            self.date = date
        self.default_output = "Etats inscriptions %s.ods" % GetDateString(self.date, weekday=False)
        self.gauge = None
        self.email = None
        self.site = None
        
    def execute(self, filename, dom):
        if filename != 'content.xml':
            return None
        
        errors = {}
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table").item(0)
        lignes = table.getElementsByTagName("table:table-row")
              
        # Les champs de l'entête
        ReplaceFields(lignes, [('date', self.date)])
        
        # Les lignes
        template = lignes.item(7)

        inscrits = sorted(database.creche.inscrits, key=lambda inscrit: (inscrit.get_groupe_order(self.date), inscrit.nom, inscrit.prenom))
        for inscrit in inscrits:
            inscription = inscrit.get_inscription(self.date)
            if inscription and (not self.site or inscription.site == self.site):
                ligne = template.cloneNode(1)                        
                fields = GetInscritFields(inscrit) + GetInscriptionFields(inscription)
                ReplaceFields(ligne, fields)
                table.insertBefore(ligne, template)

        table.removeChild(template)
        if self.gauge:
            self.gauge.SetValue(90)
        return errors
generation/compte_exploitation.py
class CompteExploitationModifications(object):
    def __init__(self, site, annee):
        self.multi = False
        self.template = 'Compte exploitation.ods'
        self.default_output = "Compte exploitation %d.ods" % annee
        self.site = site
        self.annee = annee
        self.errors = {}
        self.email = None
        self.row_total_semestre = [0, 0]

    def FillSemestre(self, template, semestre, handicap):
        debut = datetime.date(self.annee, semestre * 6 + 1, 1)
        if debut > today:
            return None, None
        fin = GetMonthEnd(datetime.date(self.annee, semestre * 6 + 6, 1))
        
        inscrits = list(database.creche.select_inscrits(debut, fin, self.site, handicap=handicap))
        if not inscrits:
            return None, None
        
        inscrits = GetEnfantsTriesParNom(inscrits)

        str_semestre = "%s semestre %d" % (ordinaux[semestre], self.annee)
        table = template.cloneNode(1)
        table_name = table.getAttribute("table:name").replace("<semestre>", str_semestre)
        table.setAttribute("table:name", table_name)
        lines = table.getElementsByTagName("table:table-row")
        
        # Les champs simples
        fields = [('annee', self.annee),
                  ('semestre', str_semestre)]
        if self.site:
            fields.append(('site', self.site.nom))
        for i in range(6):
            fields.append(('mois[%d]' % i, months[semestre * 6 + i]))
        ReplaceFields(lines, fields)
        
        template_header = lines[7]
        template_report = lines[8]
        template_enfant = lines[9]
        template_sous_total = lines[10]
        template_saut_page = lines[11]
        template_total = lines[12]
        
        shift = 0
        index_in_page = 0
        page = 0

        line = template_report.cloneNode(1)
        HideLine(line)
        table.insertBefore(line, template_report)
        table.removeChild(template_report)
        
        for inscrit in inscrits:
            if index_in_page == 10:
                line_sous_total = template_sous_total.cloneNode(1)
                IncrementFormulas(line_sous_total, row=-10+shift)
                table.insertBefore(line_sous_total, template_enfant)
                table.insertBefore(template_saut_page.cloneNode(1), template_enfant)
                table.insertBefore(template_header.cloneNode(1), template_enfant)
                line_report = template_report.cloneNode(1)
                IncrementFormulas(line_report, row=3+shift)
                table.insertBefore(line_report, template_enfant)
                shift += 4
                page += 1
                index_in_page = 0
            
            line = template_enfant.cloneNode(1)
            fields = GetInscritFields(inscrit)
            for i in range(6):
                mois = semestre * 6 + i + 1
                debut = datetime.date(self.annee, mois, 1)
                if inscrit.is_present(debut, GetMonthEnd(debut), self.site, handicap):
                    try:
                        facture = Facture(inscrit, self.annee, mois, NO_NUMERO)
                        heures = facture.heures_realisees
                    except Exception as e:
                        self.errors[GetPrenomNom(inscrit)] = e.errors
                        heures = 0
                else:
                    heures = 0
                fields.append(('reel[%d]' % i, heures))
            ReplaceFields(line, fields)
            IncrementFormulas(line, row=shift)
            table.insertBefore(line, template_enfant)
            shift += 1
            index_in_page += 1
        
        for i in range(index_in_page, 10):
            line = template_enfant.cloneNode(1)
            fields = [('reel[%d]' % i, 0) for i in range(6)]
            ReplaceFields(line, fields)
            IncrementFormulas(line, row=shift)
            HideLine(line)
            table.insertBefore(line, template_enfant)
            shift += 1
            
        table.removeChild(template_enfant)
        table.removeChild(template_sous_total)
        table.removeChild(template_saut_page)
        
        IncrementFormulas(template_total, row=shift-10)
        
        return table, shift+10
    
    def FillRecap(self, table, tables_normaux, tables_handicapes):
        debut = datetime.date(self.annee, 1, 1)
        fin = datetime.date(self.annee, 12, 31)
        
        nombre_enfants_non_handicapes = len(list(database.creche.select_inscrits(debut, fin, self.site, handicap=False)))
        nombre_enfants_handicapes = len(list(database.creche.select_inscrits(debut, fin, self.site, handicap=True)))
        fields = [("annee", self.annee),
                  ("date", today),
                  ("nombre-enfants-non-handicapes", nombre_enfants_non_handicapes),
                  ("nombre-enfants-handicapes", nombre_enfants_handicapes),
                  ("nombre-enfants", nombre_enfants_handicapes+nombre_enfants_non_handicapes)
                  ]
        if self.site:
            fields.append(('site', self.site.nom))
        ReplaceFields(table, fields)
        
        lines = table.getElementsByTagName("table:table-row")
        for i in range(12):
            semestre, mois = i / 6, i % 6
            line = lines[i + 10]
            if tables_normaux[semestre][1]:
                SetCellFormulaReference(GetCell(line, 1), tables_normaux[semestre][0], '%c%d' % (chr(ord('G') + mois), tables_normaux[semestre][1]))
            if tables_handicapes[semestre][1]:
                SetCellFormulaReference(GetCell(line, 5), tables_handicapes[semestre][0], '%c%d' % (chr(ord('G') + mois), tables_handicapes[semestre][1]))
    
    def execute(self, filename, dom):
        if filename == 'styles.xml':
            ReplaceTextFields(dom, GetCrecheFields(database.creche))
            return []

        elif filename == 'content.xml':
            spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
            tables = spreadsheet.getElementsByTagName("table:table")
            tables_normaux = []
            tables_handicapes = []
            
            # Les 2 semestres
            template = tables.item(1)
            spreadsheet.removeChild(template)
            for semestre in range(2):
                table, total = self.FillSemestre(template, semestre, handicap=False)
                tables_normaux.append((table, total))
                if table is not None:
                    spreadsheet.insertBefore(table, tables[3])

            # Les 2 semestres pour enfants handicapes
            template = tables.item(2)
            spreadsheet.removeChild(template)
            for semestre in range(2):
                table, total = self.FillSemestre(template, semestre, handicap=True)
                tables_handicapes.append((table, total))
                if table is not None:
                    spreadsheet.insertBefore(table, tables[3])
                    
            # Le recapitulatif
            template = tables.item(0)
            self.FillRecap(template, tables_normaux, tables_handicapes)

        return self.errors
generation/export_tablette.py
class ExportTabletteModifications(object):
    title = "Export tablette"
    template = "Export tablette.ods"

    def __init__(self, site, date):
        self.multi = False
        self.default_output = "Export tablette.ods"
        self.site, self.date = site, date
        self.gauge = None
        self.email = None
        self.site = None
        self.array_enfants = {}
        self.array_salaries = {}

    def FetchJournal(self):
        journal = config.connection.LoadJournal()
        self.array_enfants = {}
        self.array_salaries = {}
        if not journal:
            return
        lines = journal.split("\n")
        for line in lines:
            try:
                salarie, label, idx, date, heure = SplitLineTablette(line)
                if date.year != self.date.year or date.month != self.date.month:
                    continue
                if salarie:
                    array = self.array_salaries
                else:
                    array = self.array_enfants
                if idx not in array:
                    array[idx] = { }
                if date not in array[idx]:
                    array[idx][date] = []
                if label == "arrivee":
                    array[idx][date].append(PeriodePresence(date, heure))
                elif label == "depart":
                    if len(array[idx][date]):
                        last = array[idx][date][-1]
                        if last.date == date and last.arrivee:
                            last.depart = heure
                        else:
                            array[idx][date].append(PeriodePresence(date, None, heure))
                    else:
                        array[idx][date].append(PeriodePresence(date, None, heure))
                elif label == "absent":
                    array[idx][date].append(PeriodePresence(date, absent=True))
                elif label == "malade":
                    array[idx][date].append(PeriodePresence(date, malade=True))
                else:
                    print("Ligne %s inconnue" % label)
            except Exception as e:
                print(e)
                pass
    
    def GetHeureString(self, value):
        if value is None:
            return ""
        heures = value / 60
        minutes = value % 60
        return "%dh%02d" % (heures, minutes)

    def AddSheet(self, who, array, fields):
        table = self.template.cloneNode(1)
        table.setAttribute("table:name", GetPrenomNom(who))
        self.spreadsheet.insertBefore(table, self.template)
        lignes = table.getElementsByTagName("table:table-row")
        ReplaceFields(lignes, fields)
        lineTemplate = lignes.item(3)
        dates = list(array.keys())
        dates.sort()
        for date in dates:
            for jour in array[date]:
                ligne = lineTemplate.cloneNode(1)
                lineFields = fields + [('date', date),
                                       ('heure-arrivee', self.GetHeureString(jour.arrivee)),
                                       ('heure-depart', self.GetHeureString(jour.depart))]
                ReplaceFields(ligne, lineFields)
                table.insertBefore(ligne, lineTemplate)
        table.removeChild(lineTemplate)

    def SortedKeys(self, array, function):
        keys = list(array.keys())
        keys.sort(key=lambda key: GetPrenomNom(function(key)))
        return keys
            
    def execute(self, filename, dom):
        if filename != 'content.xml':
            return None
        
        self.FetchJournal()    
        errors = {}
        self.spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        self.template = self.spreadsheet.getElementsByTagName("table:table").item(0)

        for key in self.SortedKeys(self.array_enfants, database.creche.GetInscrit):
            inscrit = database.creche.GetInscrit(key)
            if inscrit:
                self.AddSheet(inscrit, self.array_enfants[key], GetInscritFields(inscrit))
            else:
                print("Inscrit inconnu")
                
        for key in self.SortedKeys(self.array_salaries, database.creche.GetSalarie):
            salarie = database.creche.GetSalarie(key)
            if salarie:
                self.AddSheet(salarie, self.array_salaries[key], GetSalarieFields(salarie))
            else:
                print("Salarié inconnu")
                        
        self.spreadsheet.removeChild(self.template)
        
        if self.gauge:
            self.gauge.SetValue(90)

        return errors
generation/releve_salaries.py
class ReleveSalariesModifications(object):
    title = "Relevés salariés"
    template = "Releve salaries.ods"

    def __init__(self, salaries, periode):
        self.salaries = salaries
        self.periode = periode
        self.email = None
        self.site = None
        self.multi = False
        if len(salaries) > 1:
            self.default_output = "Releve salaries %s %d.ods" % (months[periode.month-1], periode.year)
        else:
            who = salaries[0]
            self.default_output = "Releve salaries %s - %s %d.ods" % (GetPrenomNom(who), months[periode.month - 1], periode.year)
    
    def GetFields(self, salarie):
        fields = [('nom-creche', database.creche.nom),
                  ('adresse-creche', database.creche.adresse),
                  ('code-postal-creche', str(database.creche.code_postal)),
                  ('departement-creche', GetDepartement(database.creche.code_postal)),
                  ('ville-creche', database.creche.ville),
                  ('telephone-creche', database.creche.telephone),
                  ('email-creche', database.creche.email),
                  ('prenom', salarie.prenom),
                  ('nom', salarie.nom),
                  ('mois', months[self.periode.month-1]),
                  ('annee', self.periode.year)
                  ]
        day = self.periode
        weekday = day.weekday()
        if weekday >= 5:
            day += datetime.timedelta(7 - weekday)
        elif weekday >= 1 or weekday <= 4:
            day -= datetime.timedelta(weekday)
        semaines = 0
        while day.year < self.periode.year or (day.year == self.periode.year and day.month <= self.periode.month):
            semaines += 1
            for i in range(7):
                if day.month == self.periode.month:
                    contrat = salarie.get_contrat(day)
                    if contrat is not None:
                        heures = salarie.days.get(day, salarie.GetJourneeReference(day)).get_duration()
                    else:
                        heures = 0
                else:
                    heures = ""
                fields.append(("%s-%d" % (days[i].lower(), semaines), heures))                    
                day += datetime.timedelta(1)
        _, contrat, realise, _, _ = salarie.GetDecompteHeuresEtConges(GetMonthStart(self.periode), GetMonthEnd(self.periode))
        fields.append(('delta', realise - contrat))
        return semaines, fields

    def execute(self, filename, dom):
        if filename != 'content.xml':
            return None
        
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table").item(0)       
        lignes = table.getElementsByTagName("table:table-row")
        
        LINES = 16
        template = lignes[1:LINES + 1]
        last_line = lignes[LINES + 1]
        for line in template:
            table.removeChild(line)

        inc = 0
        for salarie in self.salaries:
            if salarie.get_contrat(datetime.date.today()):
                semaines, fields = self.GetFields(salarie)
                lignes = []
                for l, line in enumerate(template):
                    if l == LINES-1 or l < LINES-1-(5-semaines)*2:
                        clone = line.cloneNode(1)
                        lignes.append(clone)
                        if l == LINES - 1:
                            ReplaceFormulas(clone, "SUM([.G8:.G16])", "SUM([.G%d:.G%d])" % (8+inc, 8+inc+2*(semaines-1)))
                        else:
                            IncrementFormulas(clone, row=+inc)
                        table.insertBefore(clone, last_line)
                ReplaceFields(lignes, fields)
                inc += LINES - (5-semaines) * 2
generation/etat_presence_mensuel.py
class EtatPresenceMensuelModifications(object):
    def __init__(self, site, date):
        self.multi = False
        self.template = "Etat presence mensuel.ods"
        self.default_output = "Etat presence mensuel %s %d.ods" % (months[date.month - 1], date.year)
        self.date = date
        self.site = site
        self.gauge = None
        self.email = None
        
    def execute(self, filename, dom):
        if filename != 'content.xml':
            return None
        
        errors = {}
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table").item(0)
        lignes = table.getElementsByTagName("table:table-row")
              
        # Les champs de l'entête
        ReplaceFields(lignes, [('mois', months[self.date.month-1]),
                               ('annee', self.date.year),
                               ])
        
        # Les lignes
        #for i, ligne in enumerate(lignes):
        #    print i, ligne.toprettyxml()
            
        #return errors

        inscrits = list(database.creche.select_inscrits(self.date, GetMonthEnd(self.date)))
        inscrits.sort(cmp=lambda x, y: cmp(GetPrenomNom(x), GetPrenomNom(y)))
        
        template = lignes.item(5)
        for inscrit in inscrits:
            fields = GetInscritFields(inscrit)
            cantine, garderie = 0, 0
            if database.creche.mode_saisie_planning == SAISIE_HORAIRE:
                date = self.date
                while date.month == self.date.month:
                    journee = inscrit.GetJournee(date)
                    if journee:
                        if journee.GetActivity(8.0):
                            garderie += 1
                        if journee.GetActivity(12.5):
                            cantine += 1
                        if journee.GetActivity(13.5):
                            garderie += 1
                        if journee.GetActivity(18.0):
                            garderie += 1
                    date += datetime.timedelta(days=1)
            else:
                try:
                    facture = Facture(inscrit, self.date.year, self.date.month, NO_NUMERO)
                    fields.extend(GetFactureFields(facture))
                    for key in facture.heures_supplement_activites:
                        fields.append((key, facture.heures_supplement_activites[key]))
                        fields.append(("montant-%s" % key, facture.detail_supplement_activites[key], FIELD_EUROS))
                except CotisationException as e:
                    errors[GetPrenomNom(inscrit)] = e.errors
                    continue

            fields.extend([('cantine', cantine),
                           ('garderie', garderie),
                           ])
            ligne = template.cloneNode(1)
            ReplaceFields(ligne, fields)
            table.insertBefore(ligne, template)

        table.removeChild(template)
        
        totaux = lignes.item(6)
        if totaux:
            IncrementFormulas(totaux, row=+len(inscrits)-1, flags=FLAG_SUM_MAX)
        
        if self.gauge:
            self.gauge.SetValue(90)
            
        return errors
generation/etats_trimestriels.py
class EtatsTrimestrielsModifications(object):
    def __init__(self, site, annee):
        self.multi = False
        self.template = 'Etats trimestriels.ods'
        self.default_output = "Etats trimestriels %d.ods" % annee
        self.site = site
        self.annee = annee
        self.factures = {}
        self.errors = {}
        self.email = None

    def GetPrintableValue(self, value):
        if value == 0:
            return ''
        else:
            return value

    def execute(self, filename, dom):
        if filename == 'styles.xml':
            ReplaceTextFields(dom, GetCrecheFields(database.creche))
            return []

        elif filename == 'content.xml':
            global_indexes = GetTriParCommuneEtNomIndexes(range(len(database.creche.inscrits)))
    
            spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
            tables = spreadsheet.getElementsByTagName("table:table")
    
            # LES 4 TRIMESTRES
            template = tables.item(1)
            spreadsheet.removeChild(template)
            for trimestre in range(4):
                debut = datetime.date(self.annee, trimestre * 3 + 1, 1)
                if debut > datetime.date.today():
                    break
    
                if trimestre == 3:
                    fin = datetime.date(self.annee, 12, 31)
                else:
                    fin = datetime.date(self.annee, trimestre * 3 + 4, 1) - datetime.timedelta(1)
    
                # On retire ceux qui ne sont pas inscrits pendant la periode qui nous interesse
                indexes = GetPresentsIndexes(global_indexes, (debut, fin), self.site)
    
                table = template.cloneNode(1)
                spreadsheet.appendChild(table)
                table.setAttribute("table:name", "%s tr %d" % (ordinaux[trimestre], self.annee))
                lines = table.getElementsByTagName("table:table-row")
                page_template = lines[:template_total_lines_count]
                last_line = lines[template_total_lines_count]
                for line in page_template:
                    table.removeChild(line)
    
                nb_pages = (len(indexes) // template_lines_count) + (len(indexes) % template_lines_count > 0)
                total = [[0, 0, 0], [0, 0, 0]]

                for page in range(nb_pages):
                    lines = []
                    for l, line in enumerate(page_template):
                        clone = line.cloneNode(1)
                        lines.append(clone)
                        table.insertBefore(clone, last_line)
                    
                    # Le titre de la page
                    ReplaceFields(lines[0], [('annee', self.annee),
                                             ('trimestre', ordinaux[trimestre].upper())])
                    # Les mois
                    ReplaceFields(lines[2], [('mois(1)', months[trimestre * 3].upper()),
                                             ('mois(2)', months[(trimestre * 3) + 1].upper()),
                                             ('mois(3)', months[(trimestre * 3) + 2].upper())])

                    sous_total = [[0, 0, 0], [0, 0, 0]]
                    
                    for l, line in enumerate(lines[template_first_line:template_first_line+template_lines_count]):
                        index = page * template_lines_count + l
                        heures = [[0] * 3, [0] * 3]
                        previsionnel = [0] * 3
    
                        if index < len(indexes):
                            inscrit = database.creche.inscrits[indexes[index]]
    
                            # Calcul du nombre d'heures pour chaque mois
                            for i in range(3):
                                mois = trimestre * 3 + i + 1
                                try:
                                    facture = self.get_facture(inscrit, mois)
                                except:
                                    continue
                                previsionnel[i] = facture.previsionnel
                                if config.options & HEURES_CONTRAT:
                                    heures[0][i] = facture.heures_facture - facture.heures_facture_par_mode[MODE_HALTE_GARDERIE]
                                    heures[1][i] = facture.heures_facture_par_mode[MODE_HALTE_GARDERIE]
                                else:
                                    heures[0][i] = facture.heures_facturees - facture.heures_facturees_par_mode[MODE_HALTE_GARDERIE]
                                    heures[1][i] = facture.heures_facturees_par_mode[MODE_HALTE_GARDERIE]
    
                            fields = GetInscritFields(inscrit)
    
                            for m, mode in enumerate(["creche", "halte"]):
                                for i in range(3):
                                    fields.append(('%s(%d)' % (mode, i+1), self.GetPrintableValue(heures[m][i])))
                                    sous_total[m][i] += heures[m][i]
                                    total[m][i] += heures[m][i]
                        else:
                            fields = [(tmp, '') for tmp in ('nom', 'prenom', 'adresse', 'ville', 'code-postal', 'naissance', 'entree', 'sortie')]
                            for mode in ["creche", "halte"]:
                                for i in range(3):
                                    fields.append(('%s(%d)' % (mode, i+1), ''))
    
                        ReplaceFields(line, fields)
                    
                    # La ligne de sous-total
                    line = lines[template_first_line+template_lines_count]
                    if "sous-total" in line.toprettyxml():
                        fields = []
                        for m, mode in enumerate(["creche", "halte"]):
                            for i in range(3):
                                fields.append(('sous-total-%s(%d)' % (mode, i+1), self.GetPrintableValue(sous_total[m][i])))
                        ReplaceFields(line, fields)
                    
                    # La ligne de total
                    line = lines[template_first_line+template_lines_count+1]
                    if "total" in line.toprettyxml():
                        if page == nb_pages-1:
                            fields = []
                            for m, mode in enumerate(["creche", "halte"]):
                                for i in range(3):
                                    fields.append(('total-%s(%d)' % (mode, i+1), self.GetPrintableValue(total[m][i])))
                            ReplaceFields(line, fields)
                        else:
                            table.removeChild(line)
                        
            # LA SYNTHESE ANNUELLE
            table = tables.item(0)
            if datetime.date(self.annee, 9, 1) < datetime.date.today():
                debut = datetime.date(self.annee, 1, 1)
                fin = datetime.date(self.annee, 12, 31)
                if debut < datetime.date.today():
                    lignes = table.getElementsByTagName("table:table-row")
                    # Les inscrits en creche
                    indexes = GetInscritsByMode(debut, fin, MODE_TEMPS_PLEIN | MODE_TEMPS_PARTIEL | MODE_FORFAIT_MENSUEL | MODE_FORFAIT_MENSUEL | MODE_FORFAIT_GLOBAL_CONTRAT, self.site)
                    self.Synthese(table, lignes, indexes, MODE_CRECHE, 'creche', 0)
                    # Les inscrits en halte-garderie
                    indexes = GetInscritsByMode(debut, fin, MODE_HALTE_GARDERIE, self.site)
                    self.Synthese(table, lignes, indexes, MODE_HALTE_GARDERIE, 'halte', 6)
            else:
                spreadsheet.removeChild(table)

        return self.errors

    def get_facture(self, inscrit, mois):
        if (inscrit.idx, mois) not in self.factures:
            try:
                self.factures[inscrit.idx, mois] = Facture(inscrit, self.annee, mois, options=NO_REVENUS|NO_NUMERO)
            except CotisationException as e:
                label = "%s %s" % (inscrit.prenom, inscrit.nom)
                if not label in self.errors:
                    self.errors[label] = set(e.errors)
                else:
                    self.errors[label].update(e.errors)
                raise
        return self.factures[inscrit.idx, mois]

    def Synthese(self, table, lignes, indexes, mode, str_mode, premiere_ligne):
        indexes = GetTriParNomIndexes(indexes)

        # Le titre
        ReplaceFields(lignes.item(premiere_ligne), [('annee', self.annee)])

        # Les mois
        fields = [('mois(%d)' % (mois + 1), months_abbrev[mois].upper()) for mois in range(12)]
        ReplaceFields(lignes.item(premiere_ligne + 2), fields)

        # Les valeurs
        template = lignes.item(premiere_ligne+3)
        #print template.toprettyxml()
        total = [0] * 12
        total_previsionnel = [0] * 12
        for i in range(len(indexes)):
            inscrit = database.creche.inscrits[indexes[i]]
            ligne = template.cloneNode(1)
            table.insertBefore(ligne, template)

            heures = [0] * 12
            previsionnel = [0] * 12

            # Calcul du nombre d'heures pour chaque mois
            for mois in range(12):
                try:
                    facture = self.get_facture(inscrit, mois + 1)
                except:
                    continue
                
                if config.options & HEURES_CONTRAT:
                    if mode == 0:
                        heures[mois] = facture.heures_facture - facture.heures_facture_par_mode[MODE_HALTE_GARDERIE]
                    else:
                        heures[mois] = facture.heures_facture_par_mode[MODE_HALTE_GARDERIE]
                else:
                    if mode == 0:
                        heures[mois] = facture.heures_facturees - facture.heures_facturees_par_mode[MODE_HALTE_GARDERIE]
                    else:
                        heures[mois] = facture.heures_facturees_par_mode[MODE_HALTE_GARDERIE]
                previsionnel[mois] = facture.previsionnel
                total[mois] += heures[mois]
                total_previsionnel[mois] += previsionnel[mois]

            fields = GetInscritFields(inscrit) + GetFamilleFields(inscrit.famille)

            for mois in range(12):
                if heures[mois] == 0:
                    fields.append(('%s(%d)' % (str_mode, mois+1), ''))
                elif previsionnel[mois]:
                    fields.append(('%s(%d)' % (str_mode, mois+1), '(%d)' % heures[mois]))
                else:
                    fields.append(('%s(%d)' % (str_mode, mois+1), heures[mois]))

            if sum(previsionnel):
                fields.append(('total_enfant', '(%d)' % sum(heures)))
            else:
                fields.append(('total_enfant', sum(heures)))
            ReplaceFields(ligne, fields)
        table.removeChild(template)

        # Les totaux des mois
        ligne = lignes.item(premiere_ligne+4)
        fields = []
        for mois in range(12):
            if total_previsionnel[mois]:
                fields.append(('total(%d)' % (mois+1), '(%s)' % total[mois]))
            else:
                fields.append(('total(%d)' % (mois+1), total[mois]))
            if sum(total_previsionnel):
                fields.append(('total', '(%s)' % sum(total)))
            else:
                fields.append(('total', sum(total)))
        ReplaceFields(ligne, fields)
generation/planning.py
class PlanningModifications(object):
    title = "Planning"
    template = "Planning.ods"

    def __init__(self, site, debut):
        self.multi = False
        self.default_output = "Planning %s.ods" % str(debut)
        self.site = site
        self.debut = debut
        self.metas = {"Format": 1, "Periodicite": 11}
        self.email = None
        self.site = None

    def GetMetas(self, dom):
        metas = dom.getElementsByTagName('meta:user-defined')
        for meta in metas:
            # print meta.toprettyxml()
            name = meta.getAttribute('meta:name')
            value = meta.childNodes[0].wholeText
            if meta.getAttribute('meta:value-type') == 'float':
                self.metas[name] = float(value)
            else:
                self.metas[name] = value

    def execute(self, filename, dom):
        if filename == 'meta.xml':
            self.GetMetas(dom)
            return None
        elif filename != 'content.xml':
            return None
              
        date_fin = self.debut + datetime.timedelta(self.metas["Periodicite"])
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table")[0]
        lignes = table.getElementsByTagName("table:table-row")

        # Les titres des pages
        ReplaceFields(lignes, [('date-debut', self.debut),
                               ('date-fin', date_fin)])

        if self.metas["Format"] == 1:
            # Le format utilisé par Les petits potes (séparation adaptation / halte-garderie / mi-temps / plein-temps
            # Changé en format utilisé par les petits lutins (sans la séparation)
            table.setAttribute("table:name", '%d %s %d - %d %s %d' % (self.debut.day, months[self.debut.month - 1], date_fin.year, date_fin.day, months[date_fin.month - 1], date_fin.year))

            # Les jours
            ligne = lignes.item(1)
            cellules = ligne.getElementsByTagName("table:table-cell")
            for semaine in range(2):
                for jour in range(5):
                    date = self.debut + datetime.timedelta(semaine * 7 + jour)
                    cellule = cellules.item(1 + semaine * 6 + jour)
                    ReplaceFields([cellule], [('date', date)])
    
            ligne_total = lignes.item(7)

            # Les lignes
            inscrits = list(database.creche.select_inscrits(self.debut, date_fin))
            inscrits.sort(key=lambda x: GetPrenomNom(x))
            self.printPresences(table, inscrits, 3)
            nb_55 = 1 + max(2, len(inscrits))
    
            cellules = ligne_total.getElementsByTagName("table:table-cell")
            for i in range(cellules.length):
                cellule = cellules.item(i)
                if cellule.hasAttribute('table:formula'):
                    formule = cellule.getAttribute('table:formula')
                    formule = formule.replace('6', '%d' % (3 + nb_55))
                    cellule.setAttribute('table:formula', formule)
        elif self.metas["Format"] == 2:
            # Le format utilisé par une garderie périscolaire (tri par professeur)
            inscriptions = database.creche.select_inscriptions(self.debut, date_fin)
            tableau = {}  # par professeur
            for inscription in inscriptions:
                if inscription.professeur in tableau:
                    tableau[inscription.professeur].append(inscription)
                else:
                    tableau[inscription.professeur] = [inscription]

            def GetState(local_inscription, delta):
                state = local_inscription.inscrit.get_state(self.debut + datetime.timedelta(delta))
                if state <= 0:
                    return 0
                else:
                    return state & PRESENT

            template = table
            for professeur in tableau:
                table = template.cloneNode(1)
                table.setAttribute("table:name", GetPrenomNom(professeur))
                template.parentNode.insertBefore(table, template)
                ligne_template = GetRow(table, 10)
                for inscription in tableau[professeur]:
                    ligne = ligne_template.cloneNode(1)
                    fields = [('prenom', GetPrenom(inscription.inscrit)),
                              ('nom', GetNom(inscription.inscrit)),
                              ('professeur-prenom', GetPrenom(inscription.professeur)),
                              ('professeur-nom', GetNom(inscription.professeur))] 
                    for i, day in enumerate(days):
                        fields.append((day.lower(), GetState(inscription, i)))
                    ReplaceFields(ligne, fields)
                    table.insertBefore(ligne, ligne_template)
                table.removeChild(ligne_template)
            template.parentNode.removeChild(template)
        elif self.metas["Format"] == 3:
            # Mon petit bijou
            # lecture des couleurs
            couleurs = {}
            for i, inscrit in enumerate(database.creche.inscrits):
                line = lignes[5 + (i % 40)]
                couleurs[GetPrenomNom(inscrit)] = [GetCell(line, j).getAttribute("table:style-name") for j in range(1, 5)]
            for line in lignes[5:45]:
                table.removeChild(line)
            del lignes[5:45]

            date = self.debut
            fin = self.debut + datetime.timedelta(5)
            jour = 0
            while date < fin:
                template = lignes[4 + 3 * jour]
                lignes_presence = GetLines(date, database.creche.inscrits, presence=True, site=self.site)
                for i, presence in enumerate(lignes_presence):
                    if i == 0:
                        line = lignes[3 + 3 * jour]
                        GetCell(line, 0).setAttribute("table:number-rows-spanned", str(len(lignes_presence)))
                    else:
                        line = template.cloneNode(1)
                        table.insertBefore(line, template)
                    nom_ecrit = False
                    for c in range(24):  # 12h
                        cell = GetCell(line, c + 1)
                        if IsPresentDuringTranche(presence, (7.0 + c * 0.5) * 12, (7.0 + c * 0.5 + 0.5) * 12):
                            if not nom_ecrit:
                                cell.setAttribute("office:value-type", "string")
                                text_node = GetCell(line, 3).childNodes[0]
                                GetCell(line, 3).removeChild(text_node)
                                ReplaceTextFields(text_node, [("nom", GetPrenomNom(presence))])
                                cell.appendChild(text_node)
                                nom_ecrit = True
                            cell.setAttribute("table:style-name", couleurs[presence.label][2 + (c & 1)])
                        else:
                            cell.setAttribute("table:style-name", couleurs[presence.label][c & 1])
                table.removeChild(template)               
                date += datetime.timedelta(1)
                jour += 1                
        elif self.metas["Format"] == 4:
            # Garderie Ribambelle
            fin = self.debut + datetime.timedelta(5)
            lignes_entete = lignes[0:6]
            template = lignes[4]
            total_template = lignes[6]
            inscriptions = database.creche.select_inscriptions(self.debut, fin)
            for index, inscription in enumerate(inscriptions):
                if index != 0 and index % 24 == 0:
                    for i, line in enumerate(lignes_entete):
                        clone = line.cloneNode(1)
                        table.insertBefore(clone, total_template)
                        if i == 4:
                            table.removeChild(template)
                            template = clone
                    
                inscrit = inscription.inscrit
                line = template.cloneNode(1)
                date = self.debut
                cell = 0
                tranches = [(database.creche.ouverture, 12), (14, database.creche.fermeture)]
                while date < fin:
                    journee = inscrit.GetJournee(date)
                    for t in range(2):
                        cell += 1
                        if journee and IsPresentDuringTranche(journee, tranches[t][0] * 12, tranches[t][1] * 12):
                            ReplaceFields(GetCell(line, cell), [('p', 1)])
                        else:
                            ReplaceFields(GetCell(line, cell), [('p', '')])
                    date += datetime.timedelta(1)    
                table.insertBefore(line, template)
                ReplaceTextFields(GetCell(line, 0), GetInscritFields(inscription.inscrit))

            table.removeChild(template)
            
            cellules = total_template.getElementsByTagName("table:table-cell")
            for i in range(cellules.length):
                cellule = cellules.item(i)
                if cellule.hasAttribute('table:formula'):
                    formule = cellule.getAttribute('table:formula')
                    formule = formule.replace('6', '%d' % (5 + len(inscriptions) + 5 * (len(inscriptions) / 24)))
                    cellule.setAttribute('table:formula', formule)
        elif self.metas["Format"] == 5:
            # Au petit Saturnin
            fin = self.debut + datetime.timedelta(5)
            lignes_entete = lignes[0:2]
            templates = lignes[2:4]
            total_template = lignes[5]

            # Les jours
            ligne = lignes.item(1)
            cellules = ligne.getElementsByTagName("table:table-cell")
            for jour in range(5):
                date = self.debut + datetime.timedelta(jour)
                cellule = cellules.item(2 + jour)
                ReplaceFields([cellule], [('date', date)])

            inscriptions = database.creche.select_inscriptions(self.debut, fin)
            lines = [Ligne(inscription) for inscription in inscriptions]
            lines = GetEnfantsTriesParGroupe(lines)

            for index, ligne in enumerate(lines):
                # if index != 0 and index % 24 == 0:
                #     for i, row in enumerate(lignes_entete):
                #         clone = row.cloneNode(1)
                #         table.insertBefore(clone, total_template)
                #         if i == 4:
                #             table.removeChild(template)
                #             template = clone

                inscrit = ligne.inscrit
                inscription = ligne.inscription
                if inscription.groupe is None or len(templates) <= inscription.groupe.ordre:
                    line = templates[0].cloneNode(1)
                else:
                    line = templates[inscription.groupe.ordre].cloneNode(1)
                cell = 0
                tranches = [(database.creche.ouverture, 12), (14, database.creche.fermeture)]
                date = self.debut
                while date <= fin:
                    journee = inscrit.GetJournee(date)
                    for t in range(2):
                        cell += 1
                        if journee and IsPresentDuringTranche(journee, tranches[t][0] * 12, tranches[t][1] * 12):
                            ReplaceFields(GetCell(line, cell), [('p', 1)])
                        else:
                            ReplaceFields(GetCell(line, cell), [('p', '')])
                    date += datetime.timedelta(1)
                table.insertBefore(line, templates[0])
                ReplaceTextFields(line, GetInscritFields(inscription.inscrit) + GetInscriptionFields(inscription))

            for template in templates:
                table.removeChild(template)

            IncrementFormulas(total_template, row=+len(lines), flags=FLAG_SUM_MAX)

        #print dom.toprettyxml()
        return None

    def printPresences(self, dom, inscrits, ligne_depart):
        lignes = dom.getElementsByTagName("table:table-row")
        if len(inscrits) == 0:
            dom.removeChild(lignes.item(ligne_depart + 2))
            dom.removeChild(lignes.item(ligne_depart + 1))
            dom.removeChild(lignes.item(ligne_depart))
            dom.removeChild(lignes.item(ligne_depart - 1))
            return
        nb_lignes = 3
        if len(inscrits) > 3:
            for i in range(3, len(inscrits)):
                dom.insertBefore(lignes.item(ligne_depart + 1).cloneNode(1), lignes.item(ligne_depart + 2))
            nb_lignes = len(inscrits)
        elif len(inscrits) < 3:
            dom.removeChild(lignes.item(ligne_depart + 1))
            nb_lignes = 2
        lignes = dom.getElementsByTagName("table:table-row")
        for i in range(nb_lignes):
            if i < len(inscrits):
                inscrit = inscrits[i]
            else:
                inscrit = None
            ligne = lignes.item(ligne_depart + i)
            cellules = ligne.getElementsByTagName("table:table-cell")
            for semaine in range(2):
                # Les infos
                cellule = cellules.item(semaine * 12)
                ReplaceFields([cellule], GetInscritFields(inscrit))
                # Les présences
                for jour in range(5):
                    date = self.debut + datetime.timedelta(semaine * 7 + jour)
                    if inscrit:
                        journee = inscrit.GetJournee(date)
                        print(inscrit.prenom, date, journee, journee.timeslots if journee else None)
                    for t in range(2):
                        cellule = cellules.item(1 + semaine * 12 + jour * 2 + t)
                        if inscrit and journee:
                            tranches = [(database.creche.ouverture, 12.30), (13.30, database.creche.fermeture)]
                            ReplaceFields([cellule], [('p', int(IsPresentDuringTranche(journee, tranches[t][0] * 12, tranches[t][1] * 12)))])
                        else:
                            ReplaceFields([cellule], [('p', '')])
generation/releve_detaille.py
class ReleveDetailleModifications(object):
    def __init__(self, site, annee):
        self.multi = False
        self.template = 'Releve detaille.ods'
        self.default_output = "Releve detaille %d.ods" % annee
        self.site = site
        self.annee = annee
        self.errors = {}
        self.email = None
        self.metas = {}

    def GenerateOptimomesTable(self, inscrits, template, agemin=0, agemax=0):
        feuille, lines = template
        template_first, template, after = lines[0], lines[1], lines[2]
        feuille.removeChild(template)

        count = 0
        for inscrit in inscrits:
            if (agemin or agemax) and not isinstance(inscrit.naissance, datetime.date):
                self.errors[GetPrenomNom(inscrit)] = ["Date de naissance incorrecte"]
                continue
            if agemax:
                anniversaireMax = GetDateAnniversaire(inscrit.naissance, agemax)
                if anniversaireMax <= datetime.date(self.annee, 1, 1):
                    continue
            if agemin:
                anniversaireMin = GetDateAnniversaire(inscrit.naissance, agemin)
                if anniversaireMin > datetime.date(self.annee, 12, 1):
                    continue
            
            if count == 0:
                clone = template_first
            else:
                clone = template.cloneNode(1)
                feuille.insertBefore(clone, after)

            fields = GetInscritFields(inscrit)
            ReplaceFields(clone, fields)
            if count > 1:
                IncrementFormulas(clone, row=+count-1)
            
            for mois in range(12):
                fields = [('heures-facturees', 0), ('heures-realisees', 0), ('total', 0)]
                date = datetime.date(self.annee, mois + 1, 1)
                if date <= datetime.date.today() and (not agemin or date >= anniversaireMin) and (not agemax or date < anniversaireMax):
                    try:
                        facture = Facture(inscrit, self.annee, mois+1, NO_NUMERO)
                        fields = [('heures-facturees', facture.heures_facture), ('heures-realisees', facture.heures_realisees), ('total', facture.total)]
                    except CotisationException as e:
                        self.errors[GetPrenomNom(inscrit)] = e.errors
                if count == 0:
                    cells = clone.getElementsByTagName("table:table-cell")[3+mois*3:6+mois*3]
                else:
                    cells = clone.getElementsByTagName("table:table-cell")[2+mois*3:5+mois*3]
                ReplaceFields(cells, fields)
                
            count += 1
        
        first_cell = template_first.getElementsByTagName("table:table-cell").item(0)
        first_cell.setAttribute("table:number-rows-spanned", str(count))
        IncrementFormulas(after, row=+count-2, flags=FLAG_SUM_MAX)
        for line in lines[3:]:
            IncrementFormulas(line, row=+count-2)

    def execute(self, filename, dom):
        if filename == 'meta.xml':
            metas = dom.getElementsByTagName('meta:user-defined')
            for meta in metas:
                # print meta.toprettyxml()
                name = meta.getAttribute('meta:name')
                value = meta.childNodes[0].wholeText
                if meta.getAttribute('meta:value-type') == 'float':
                    self.metas[name] = float(value)
                else:
                    self.metas[name] = value
            return None
                
        elif filename == 'styles.xml':
            ReplaceTextFields(dom, GetCrecheFields(database.creche))
            return []

        elif filename == 'content.xml':
            if 'Format' in self.metas and self.metas['Format'] == "Optimomes":
                spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
                feuille = spreadsheet.getElementsByTagName("table:table").item(0)
                lines = feuille.getElementsByTagName("table:table-row")
                ReplaceTextFields(lines[0], [('date-debut', datetime.date(self.annee, 1, 1)), ('date-fin', datetime.date(self.annee, 12, 31))])
                inscrits = list(database.creche.select_inscrits(datetime.date(self.annee, 1, 1), datetime.date(self.annee, 12, 31)))
                self.GenerateOptimomesTable(inscrits, (feuille, lines[4:]), agemax=4)
                self.GenerateOptimomesTable(inscrits, (feuille, lines[7:]), agemin=4)
            else:
                spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
                feuille = spreadsheet.getElementsByTagName("table:table").item(0)
                lines = feuille.getElementsByTagName("table:table-row")
                template = lines[1:13]
                after = lines[13]
                for line in template:
                    feuille.removeChild(line)
                for inscrit in database.creche.select_inscrits(datetime.date(self.annee, 1, 1), datetime.date(self.annee, 12, 31)):
                    for i, line in enumerate(template):
                        try:
                            facture = Facture(inscrit, self.annee, i+1, NO_NUMERO)
                        except CotisationException as e:
                            facture = None
                            self.errors[GetPrenomNom(inscrit)] = e.errors                            
                        clone = line.cloneNode(1)
                        ReplaceTextFields(clone, GetInscritFields(inscrit))
                        if facture:
                            ReplaceTextFields(clone, GetFactureFields(facture))
                        feuille.insertBefore(clone, after)
            return self.errors
generation/export_filoue.py
class ExportFiloueModifications:
    title = "Export Filoue"
    template = "Export Filoue.csv"

    def __init__(self, site, year):
        self.annee = year
        self.start = datetime.date(year, 1, 1)
        self.end = datetime.date(year, 12, 31)
        self.site = site
        if site:
            self.default_output = u"Export Filoue %s %d.csv" % (site.nom, year)
        else:
            self.default_output = u"Export Filoue %d.csv" % year
        self.email_to = None
        self.multi = False
        self.email = False

    @staticmethod
    def get_regime_caf(inscrit, date):
        regime = inscrit.GetRegime(date)
        if regime == 1:
            return 1
        elif regime == 3:
            return 2
        else:
            return 3

    def execute(self, text):
        errors = {}
        lines = text.splitlines()
        result = [lines[0]]
        template = lines[1]
        for inscrit in database.creche.select_inscrits(self.start, self.end):
            if not inscrit.famille.numero_allocataire_caf:
                errors[GetPrenomNom(inscrit)] = [u"Le numéro d'allocataire CAF n'est pas renseigné"]
            elif not inscrit.famille.numero_allocataire_caf.isdigit():
                errors[GetPrenomNom(inscrit)] = [u"Le numéro d'allocataire CAF ne doit comporter que des chiffres"]
            regime_caf = 3
            heures_realisees = 0
            heures_facturees = 0
            total_facture = 0
            date = self.start
            error = False
            facture = None
            while date < self.end:
                try:
                    facture = Facture(inscrit, self.annee, date.month, NO_NUMERO)
                    regime_caf = self.get_regime_caf(inscrit, date)
                    heures_realisees += facture.heures_realisees
                    heures_facturees += facture.heures_facturees
                    total_facture += facture.total
                except CotisationException as e:
                    errors[GetPrenomNom(inscrit)] = e.errors
                    error = True
                if error:
                    break
                date = GetNextMonthStart(date)
            line = template
            if facture:
                fields = {
                    "numero-caf": database.creche.numero_caf,
                    "numero-dossier-afc": database.creche.numero_dossier_afc,
                    "top-allocataire": 1,
                    "prenom": inscrit.prenom,
                    "nom": inscrit.nom,
                    "matricule-allocataire": inscrit.famille.numero_allocataire_caf,
                    "regime-caf": regime_caf,
                    "date-naissance": inscrit.naissance.strftime("%d/%m/%Y"),
                    "code-postal-famille": inscrit.famille.code_postal,
                    "ville-famille": inscrit.famille.ville,
                    "heures-facturees": "%04.02f" % heures_facturees,
                    "heures-realisees": "%04.02f" % heures_realisees,
                    "total-facture": "%06.02f" % total_facture,
                    "tarif-horaire": "%02.02f" % ((total_facture / heures_facturees) if heures_facturees else 0),
                    "taux-effort": ("%01.03f" % facture.taux_effort) if facture.taux_effort else None,
                    "premier-jour": max(self.start, min([contrat.debut for contrat in inscrit.get_inscriptions() if contrat.debut and not contrat.preinscription])).strftime("%d/%m/%Y"),
                    "dernier-jour": min(self.end, max([(contrat.GetFin() if contrat.GetFin() else self.end) for contrat in inscrit.get_inscriptions() if contrat.debut and not contrat.preinscription])).strftime("%d/%m/%Y"),
                }
                for key, value in fields.items():
                    line = line.replace("<%s>" % key, str(value).replace(";", ",") if value else "")
                result.append(line)

        return "\n".join(result), errors
statistiques.py
def GetStatistiques(start, end, site=None, bargraph=False):
    result = Statistiques()
    if bargraph:
        debut = datetime.date(start.year, 1, 1)
        fin = datetime.date(start.year, 12, 31)
        result.bargraph_year = start.year
    else:
        debut = start
        fin = end
    date = debut
    while date <= fin:
        print("[Statistiques %s %d]" % (months[date.month-1], date.year))
        fin_mois = GetMonthEnd(date)
        if start <= date <= end:
            result.heures_accueil += GetHeuresAccueil(date.year, date.month, site)
        for inscrit in database.creche.inscrits:
            try:
                inscriptions = inscrit.get_inscriptions(date, fin_mois)
                if inscriptions and (site is None or inscriptions[0].site == site):
                    facture = Facture(inscrit, date.year, date.month, NO_NUMERO)
                    if config.options & HEURES_CONTRAT:
                        heures_contrat = facture.heures_contrat
                        heures_facture = facture.heures_facture
                    else:
                        heures_contrat = facture.heures_contractualisees
                        heures_facture = facture.heures_facturees
                    if start <= date <= end:
                        result.heures_contrat += heures_contrat
                        result.heures_reel += facture.heures_realisees
                        result.heures_facture += heures_facture
                        result.jours_contrat += facture.jours_contractualises
                        result.jours_reel += facture.jours_realises
                        result.jours_facture += facture.jours_factures
                        result.cotisations_contrat += facture.total_contractualise
                        result.cotisations_reel += facture.total_realise
                        result.cotisations_facture += facture.total_facture
                        print(GetPrenomNom(inscrit), "au", facture.date)
                        print(' ', "heures contractualisées :", facture.heures_contractualisees, ", heures contrat :", facture.heures_contrat)
                        print(' ', "heures réalisées :", facture.heures_realisees)
                        print(' ', "heures facturées :", facture.heures_facturees, ", heures facture :", facture.heures_facture)
                        print(' ', "jours contractualisés :", facture.jours_contractualises)
                        print(' ', "jours réalisés :", facture.jours_realises)
                        print(' ', "jours facturés :", facture.jours_factures)
                        print(' ', "total contractualisé", facture.total_contractualise)
                        print(' ', "total réalisé :", facture.total_realise)
                        print(' ', "total facturé :", facture.total_facture)
                    result.bargraph[0][date.month-1] += heures_contrat
                    result.bargraph[1][date.month-1] += facture.heures_realisees
                    result.bargraph[2][date.month-1] += heures_facture
            except CotisationException as e:
                if date <= datetime.date.today():
                    result.erreurs[GetPrenomNom(inscrit)] = e.errors
        date = fin_mois + datetime.timedelta(1)

    if result.heures_accueil:
        result.percent_contrat = (100.0 * result.heures_contrat) / result.heures_accueil
        result.percent_reel = (100.0 * result.heures_reel) / result.heures_accueil
        result.percent_facture = (100.0 * result.heures_facture) / result.heures_accueil

    return result
generation/synthese_financiere.py
class SyntheseFinanciereModifications(object):
    def __init__(self, annee):
        self.multi = False
        self.template = 'Synthese financiere.ods'
        self.default_output = "Synthese financiere %d.ods" % annee
        self.annee = annee
        self.factures = {}
        self.errors = {}
        self.email = None
        self.site = None

    def execute(self, filename, dom):
        if filename == 'styles.xml':
            fields = GetCrecheFields(database.creche)
            ReplaceTextFields(dom, fields)
            return []

        elif filename == 'content.xml':
            spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
            tables = spreadsheet.getElementsByTagName("table:table")
            table = tables.item(0)
            lines = table.getElementsByTagName("table:table-row")
            # line_heures_ouvrees = lines[2]
    
            fields = GetCrecheFields(database.creche) + GetTarifsHorairesFields(database.creche, datetime.date(self.annee, 1, 1))
            fields.append(("annee", self.annee))
            for mois in range(1, 13):
                heures_accueil = GetHeuresAccueil(self.annee, mois, self.site)
                heures_facturees = 0.0
                heures_facturees_par_mode = [0.0] * 33
                cotisations_facturees = 0.0
                
                debut = datetime.date(self.annee, mois, 1)
                fin = GetMonthEnd(debut)
                for inscrit in database.creche.inscrits:
                    if inscrit.get_inscriptions(debut, fin):
                        facture = FactureFinMois(inscrit, self.annee, mois)
                        for mode in (MODE_FORFAIT_MENSUEL, MODE_HALTE_GARDERIE):
                            heures_facturees_par_mode[mode] += facture.heures_facturees_par_mode[mode]
                        heures_facturees += facture.heures_facturees
                        cotisations_facturees += facture.total

                fields.append(("heures-accueil[%d]" % (mois), heures_accueil))
                fields.append(("heures-facturees[forfait][%d]" % (mois), heures_facturees_par_mode[MODE_FORFAIT_MENSUEL]))
                fields.append(("heures-facturees[hg][%d]" % (mois), heures_facturees_par_mode[MODE_HALTE_GARDERIE]))
                fields.append(("heures-facturees[%d]" % (mois), heures_facturees))
                fields.append(("cotisations[%d]" % (mois), cotisations_facturees))
                fields.append(("charges[%d]" % (mois), database.creche.charges[datetime.date(self.annee, mois, 1)].charges))

            ReplaceFields(lines, fields)
            
            
#                debut = datetime.date(annee, mois+1, 1)
#                fin = GetMonthEnd(debut)
#                for inscrit in database.creche.inscrits:
#                    try:
#                        if inscrit.get_inscriptions(debut, fin):
#                            facture = FactureFinMois(inscrit, annee, mois+1)
#                            heures_contractualisees += facture.heures_contractualisees
#                            heures_realisees += facture.heures_realisees                       
#                            heures_facturees += sum(facture.heures_facturees)
#                            cotisations_contractualisees += facture.cotisation_mensuelle
#                            cotisations_realisees += facture.total_realise
#                            cotisations_facturees += facture.total
#                    except Exception as e:
#                        erreurs.append((inscrit, e))
                        
            # LES 4 TRIMESTRES
 
        return self.errors
generation/planning_detaille.py
class PlanningDetailleModifications:
    title = "Planning détaillé"
    template = "Planning detaille.odg"

    def __init__(self, periode, site=None, groupe=None):
        self.multi = False
        self.site = site
        self.groupe = groupe
        self.start, self.end = periode
        if IsTemplateFile("Planning detaille.ods"):
            self.template = 'Planning detaille.ods'
            self.default_output = "Planning detaille %s %d.ods" % (months[self.start.month - 1], self.start.year)
        else:
            self.template = 'Planning detaille.odg'
            if self.start == self.end:
                self.default_output = "Planning presences %s.odg" % GetDateString(self.start, weekday=False)
            else:
                self.default_output = "Planning presences %s-%s.odg" % (GetDateString(self.start, weekday=False), GetDateString(self.end, weekday=False))
        self.errors = {}
        self.metas = {"format": 0,
                      "left": 1.5,
                      "right": 2.0,
                      "top": 6.0,
                      "label-length": 20,
                      "labels-width": 3.5,
                      "line-height": 0.75,
                      "lines-max": 25,
                      "lignes-vides": False,
                      "summary": True
                      }
        self.email = None

    def execute(self, filename, dom):
        if filename == 'meta.xml':
            metas = dom.getElementsByTagName('meta:user-defined')
            for meta in metas:
                # print(meta.toprettyxml())
                name = meta.getAttribute('meta:name').lower()
                if len(meta.childNodes) > 0:
                    value = meta.childNodes[0].wholeText
                    value_type = meta.getAttribute('meta:value-type')
                    if value_type == "float":
                        self.metas[name] = float(value)
                    elif value_type == "boolean":
                        self.metas[name] = True if value == "true" else False
                    else:
                        self.metas[name] = value
            # for name in self.metas:
            #     print("Meta", name, type(self.metas[name]), self.metas[name])
            return None
        elif filename != 'content.xml':
            return None
        elif IsTemplateFile("Planning detaille.ods"):
            if self.metas["format"] == 1:
                return self.executeTemplateCalcJulien(filename, dom)
            elif self.metas["format"] == 2:
                return self.executeTemplateCalc123APetitsPas(filename, dom)
            else:
                return self.executeTemplateCalc(filename, dom)
        else:
            if self.metas["format"] == "one-page":
                return self.executeTemplateOnePage(filename, dom)
            else:
                return self.executeTemplateDraw(filename, dom)

    @staticmethod
    def get_timeslot_shape(shapes, timeslot, salarie=False):
        key1 = "activite-%s-%s" % ("salarie" if salarie else "enfant", timeslot.activity.label)
        key2 = "activite-%s" % timeslot.activity.label
        key3 = "activite-%d" % (timeslot.activity.mode if timeslot.activity.mode <= 0 else database.creche.activites.index(timeslot.activity) + 1)
        if key1 in shapes:
            return shapes[key1]
        elif key2 in shapes:
            return shapes[key2]
        elif key3 in shapes:
            return shapes[key3]
        else:
            print("Pas de forme pour l'activité %s: %s, %s, %s" % (timeslot.activity.label, key1, key2, key3))
            return None

    def executeTemplateDraw(self, filename, dom):
        affichage_min = int(database.creche.affichage_min * (60 // BASE_GRANULARITY))
        affichage_max = int(database.creche.affichage_max * (60 // BASE_GRANULARITY))
        step = (21.0-self.metas["left"]-self.metas["right"]-self.metas["labels-width"]) / (affichage_max - affichage_min)

        drawing = dom.getElementsByTagName('office:drawing').item(0)
        if not drawing:
            drawing = dom.getElementsByTagName('office:presentation').item(0)

        template = drawing.getElementsByTagName("draw:page").item(0)
        # print(template.toprettyxml())
        shapes = getNamedShapes(template)
        # print(shapes)
        for shape in shapes:
            if shape in ["legende-heure", "ligne-heure", "ligne-quart-heure", "libelle", "separateur", "ligne-cahier", "category"] or shape.startswith("activite-"):
                template.removeChild(shapes[shape])
        drawing.removeChild(template)
        if not "activite-%d" % PRESENCE_SALARIE in shapes:
            shapes["activite-%d" % PRESENCE_SALARIE] = shapes["activite-%d" % 0]

        def AddCategoryShape(page, text, y):
            if "category" in shapes:
                node = shapes["category"].cloneNode(1)
                node.setAttribute('svg:y', '%fcm' % y)
                ReplaceTextFields(node, [('category', text)])
                page.appendChild(node)

        date = self.start
        while date <= self.end:
            if date in database.creche.jours_fermeture:
                date += datetime.timedelta(1)
                continue

            lines_enfants = ChildPlanningLine.select(date, self.site, self.groupe)
            lines_salaries = SalariePlanningLine.select(date, self.site)
            if not self.metas["lignes-vides"]:
                lines_enfants = [line for line in lines_enfants if line.state > 0]
                lines_salaries = [line for line in lines_salaries if line.state > 0]

            lines = lines_enfants
            if lines_salaries:
                lines += [BasePlanningSeparator("Salariés")] + lines_salaries

            pages_count = 1 + (len(lines)) // self.metas["lines-max"]
            for page_index in range(pages_count):
                lines_count = min(self.metas["lines-max"], len(lines)-page_index*self.metas["lines-max"])
                page = template.cloneNode(1)
                page.setAttribute("draw:name", GetDateString(date))
                drawing.appendChild(page)

                # le quadrillage et l'echelle
                h = affichage_min
                while h <= affichage_max:
                    if h % (60 // BASE_GRANULARITY) == 0:
                        node = shapes["legende-heure"].cloneNode(1)
                        node.setAttribute('svg:x', '%fcm' % (self.metas["left"] + self.metas["labels-width"] - 0.5 + (float(h)-affichage_min) * step))
                        # node.setAttribute('svg:y', '1cm')
                        node.setAttribute('svg:width', '1cm')
                        node.firstChild.firstChild.firstChild.firstChild.replaceWholeText('%dh' % int(round(h/(60 // BASE_GRANULARITY))))
                        page.appendChild(node)
                        node = shapes["ligne-heure"].cloneNode(1)
                    else:
                        node = shapes["ligne-quart-heure"].cloneNode(1)
                    node.setAttribute('svg:x1', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + (h-affichage_min) * step))
                    # node.setAttribute('svg:y1', '2cm')
                    node.setAttribute('svg:x2', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + (h-affichage_min) * step))
                    # node.setAttribute('svg:y2', '29cm')
                    page.appendChild(node)
                    h += database.creche.granularite // BASE_GRANULARITY

                if "ligne-cahier" in shapes:
                    ligne_cahier = shapes["ligne-cahier"].cloneNode(1)
                    ligne_cahier.setAttribute('svg:x1', '%fcm' % self.metas["left"])
                    ligne_cahier.setAttribute('svg:x2', '%fcm' % (21.0 - self.metas["right"]))
                else:
                    ligne_cahier = None

                ajoute_ligne_cahier = False

                # les enfants et salaries
                for i in range(lines_count):
                    line_idx = i + self.metas["lines-max"] * page_index
                    line = lines[line_idx]
                    if isinstance(line, BasePlanningSeparator):
                        AddCategoryShape(page, line.label, 0.20 + self.metas["top"] + self.metas["line-height"] * i)
                        ajoute_ligne_cahier = False
                    else:
                        if ajoute_ligne_cahier and ligne_cahier and database.creche.tri_planning & TRI_LIGNES_CAHIER:
                            node = ligne_cahier.cloneNode(1)
                            node.setAttribute('svg:y1', '%fcm' % (self.metas["top"] + self.metas["line-height"] * i))
                            node.setAttribute('svg:y2', '%fcm' % (self.metas["top"] + self.metas["line-height"] * i))
                            page.appendChild(node)
                        else:
                            ajoute_ligne_cahier = True
                        node = shapes["libelle"].cloneNode(1)
                        node.setAttribute('svg:x', '%fcm' % self.metas["left"])
                        node.setAttribute('svg:y', '%fcm' % (self.metas["top"] + self.metas["line-height"] * i))
                        node.setAttribute('svg:width', '%fcm' % self.metas["labels-width"])
                        fields = [('nom', line.who.nom),
                                  ('prenom', line.who.prenom),
                                  ('label', line.label)]
                        ReplaceTextFields(node, fields)
                        page.appendChild(node)
                        for timeslot in line.timeslots:
                            if timeslot.activity.has_horaires():
                                shape = self.get_timeslot_shape(shapes, timeslot)
                                if shape:
                                    node = shape.cloneNode(1)
                                    node.setAttribute('svg:x', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + float(timeslot.debut - affichage_min) * step))
                                    node.setAttribute('svg:y', '%fcm' % (0.10 + self.metas["top"] + self.metas["line-height"] * i))
                                    node.setAttribute('svg:width', '%fcm' % ((timeslot.fin - timeslot.debut) * step))
                                    allergies = line.who.get_allergies() if isinstance(line.who, Inscrit) else []
                                    ReplaceTextFields(node, [("texte", ""), ("allergies", ", ".join(allergies))])
                                    page.appendChild(node)

                if self.metas["summary"] and page_index + 1 == pages_count:
                    AddCategoryShape(page, "Totaux", 0.20 + self.metas["top"] + self.metas["line-height"] * lines_count)

                    # le récapitulatif par activité
                    i = lines_count
                    summary = get_lines_summary(lines)[0]
                    for activity in summary:
                        i += 1
                        node = shapes["libelle"].cloneNode(1)
                        node.setAttribute('svg:x', '%fcm' % self.metas["left"])
                        node.setAttribute('svg:y', '%fcm' % (self.metas["top"] + self.metas["line-height"] * i))
                        node.setAttribute('svg:width', '%fcm' % self.metas["labels-width"])
                        fields = [('nom', ''),
                                  ('prenom', activity.label),
                                  ('label', activity.label)]
                        ReplaceTextFields(node, fields)
                        page.appendChild(node)
                        line = summary[activity]
                        for timeslot in line:
                            shape = self.get_timeslot_shape(shapes, timeslot)
                            if shape:
                                node = shape.cloneNode(1)
                                node.setAttribute('svg:x', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + (float(timeslot.debut - affichage_min) * step)))
                                node.setAttribute('svg:y', '%fcm' % (0.10 + self.metas["top"] + self.metas["line-height"] * i))
                                node.setAttribute('svg:width', '%fcm' % (float(timeslot.fin - timeslot.debut) * step))
                                ReplaceTextFields(node, [('texte', str(timeslot.value))])
                                page.appendChild(node)
                fields = GetCrecheFields(database.creche) + GetSiteFields(self.site)
                if pages_count > 1:
                    fields.append(('date', GetDateString(date) + " (%d/%d)" % (page_index + 1, pages_count)))
                else:
                    fields.append(('date', GetDateString(date)))

                ReplaceTextFields(page, fields)
            date += datetime.timedelta(1)
        return None

    def executeTemplateOnePage(self, filename, dom):
        affichage_min = int(database.creche.affichage_min * (60 // BASE_GRANULARITY))
        affichage_max = int(database.creche.affichage_max * (60 // BASE_GRANULARITY))
        step = (21.0 - self.metas["left"] - self.metas["right"] - self.metas["labels-width"]) / (affichage_max - affichage_min)

        drawing = dom.getElementsByTagName('office:drawing').item(0)
        if not drawing:
            drawing = dom.getElementsByTagName('office:presentation').item(0)

        template = drawing.getElementsByTagName("draw:page").item(0)
        # print(template.toprettyxml())
        shapes = getNamedShapes(template)
        # print(shapes)
        for shape in shapes:
            if shape in ["legende-heure", "ligne-heure", "ligne-quart-heure", "libelle", "jour", "separateur", "ligne-cahier", "category"] or shape.startswith("activite-"):
                template.removeChild(shapes[shape])
        drawing.removeChild(template)
        if not "activite-%d" % PRESENCE_SALARIE in shapes:
            shapes["activite-%d" % PRESENCE_SALARIE] = shapes["activite-%d" % 0]

        def drawPage(people, days, salaries=False):
            page = template.cloneNode(1)
            page.setAttribute("draw:name", GetDateString(self.start))
            drawing.appendChild(page)

            # le quadrillage
            h = affichage_min
            while h <= affichage_max:
                if h % (60 // BASE_GRANULARITY) == 0:
                    node = shapes["ligne-heure"].cloneNode(1)
                else:
                    node = shapes["ligne-quart-heure"].cloneNode(1)
                node.setAttribute('svg:x1', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + (h - affichage_min) * step))
                node.setAttribute('svg:x2', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + (h - affichage_min) * step))
                page.appendChild(node)
                h += database.creche.granularite // BASE_GRANULARITY

            current_top = self.metas["top"] - 0.5

            for day in days:
                if day in database.creche.jours_fermeture:
                    continue

                # l'échelle
                h = affichage_min
                while h <= affichage_max:
                    if h % (60 // BASE_GRANULARITY) == 0:
                        node = shapes["legende-heure"].cloneNode(1)
                        node.setAttribute('svg:y', '%fcm' % current_top)
                        node.setAttribute('svg:x', '%fcm' % (self.metas["left"] + self.metas["labels-width"] - 0.5 + (float(h) - affichage_min) * step))
                        node.setAttribute('svg:width', '1cm')
                        node.firstChild.firstChild.firstChild.firstChild.replaceWholeText('%dh' % int(round(h / (60 // BASE_GRANULARITY))))
                        page.appendChild(node)
                    h += database.creche.granularite // BASE_GRANULARITY

                # le nom du jour
                node = shapes["jour"].cloneNode(1)
                node.setAttribute('svg:y', '%fcm' % current_top)
                fields = [('jour', GetDateString(day)),
                          ('jour-sans-annee', GetDateString(day, annee=False))]
                ReplaceTextFields(node, fields)
                page.appendChild(node)
                current_top += self.metas["line-height"]

                lines = GetLines(day, people, presence=not self.metas["lignes-vides"], site=self.site, groupe=self.groupe, summary=SUMMARY_ENFANT)
                lines = GetEnfantsTriesSelonParametreTriPlanning(lines)
                for line in lines:
                    node = shapes["libelle"].cloneNode(1)
                    # node.setAttribute('svg:x', '%fcm' % self.metas["left"])
                    node.setAttribute('svg:y', '%fcm' % current_top)
                    # node.setAttribute('svg:width', '%fcm' % self.metas["labels-width"])
                    fields = [('nom', line.nom),
                              ('prenom', line.prenom),
                              ('label', truncate(line.label, self.metas["label-length"]))]
                    ReplaceTextFields(node, fields)
                    page.appendChild(node)
                    timeslots = line.timeslots[:]
                    timeslots.sort(key=lambda timeslot: timeslot.activity.idx)
                    for timeslot in timeslots:
                        if timeslot.activity.mode >= 0:
                            shape = self.get_timeslot_shape(shapes, timeslot, salaries)
                            if shape:
                                node = shape.cloneNode(1)
                                node.setAttribute('svg:x', '%fcm' % (self.metas["left"] + self.metas["labels-width"] + float(timeslot.debut - affichage_min) * step))
                                node.setAttribute('svg:y', '%fcm' % (0.20 + current_top))
                                node.setAttribute('svg:width', '%fcm' % ((timeslot.fin - timeslot.debut) * step))
                                page.appendChild(node)

                    fields = GetCrecheFields(database.creche) + GetSiteFields(self.site)
                    ReplaceTextFields(page, fields)
                    current_top += self.metas["line-height"]

                if "line-per-day" in self.metas:
                    current_top += self.metas["line-height"] * (self.metas["line-per-day"] - len(lines))

        working_days = [(self.start + datetime.timedelta(i)) for i in range(5)]
        drawPage(database.creche.inscrits, working_days)
        drawPage(database.creche.salaries, working_days, salaries=True)
        if "split-saturdays" in self.metas:
            saturdays = [(self.start + datetime.timedelta(5 + 7 * i)) for i in range(5)]
            drawPage(database.creche.inscrits, saturdays)

        return None

    def executeTemplateCalc(self, filename, dom):
        # Garderie Ribambelle, planning detaillé

        debut, fin = GetMonthStart(self.start), GetMonthEnd(self.start)
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table")[0]
        lignes = table.getElementsByTagName("table:table-row")

        # Les titres des pages
        ReplaceFields(lignes, [('mois', months[self.start.month-1]),
                               ('annee', self.start.year)])

        template = lignes[6]
        semaines_template = lignes[3]
        total_template = lignes[7]
        montant_template = lignes[9]
        
        c = 0
        numero = debut.isocalendar()[1]
        while 1:
            cell = GetCell(semaines_template, c)
            if cell is None:
                break
            c += 1
            if ReplaceFields(cell, [('numero-semaine', numero)]):
                numero += 1
            
        inscriptions = database.creche.select_inscriptions(debut, fin)
        for index, inscription in enumerate(inscriptions):
            inscrit = inscription.inscrit
            row = template.cloneNode(1)
            # print(row.toprettyxml())
            date = debut
            cell = 0
            weekday = date.weekday()
            if weekday == 6:
                date += datetime.timedelta(1)
            elif weekday == 5:
                date += datetime.timedelta(2)
            elif weekday > 0:
                cell = weekday * 2 + 1
            tranches = [(database.creche.ouverture, 12), (14, database.creche.fermeture)]
            while date <= fin:
                weekday = date.weekday()
                if weekday == 2 or weekday >= 5:
                    date += datetime.timedelta(1)
                    continue
                if weekday == 0:
                    cell += 1
                journee = inscrit.GetJournee(date)
                for t in range(2):
                    if journee and IsPresentDuringTranche(journee, tranches[t][0]*12, tranches[t][1]*12):
                        heures = HeuresTranche(journee, tranches[t][0] * 12, tranches[t][1]*12)
                        ReplaceFields(GetCell(row, cell), [('p', heures)])
                    cell += 1
                date += datetime.timedelta(1)    
            table.insertBefore(row, template)
            ReplaceTextFields(GetCell(row, 0), GetInscritFields(inscription.inscrit))
            ReplaceFields(row, [('p', '')])
            cellules = row.getElementsByTagName("table:table-cell")
            for i in range(cellules.length):
                cellule = cellules.item(i)
                if cellule.hasAttribute('table:formula'):
                    formule = cellule.getAttribute('table:formula')
                    formule = formule.replace('7', '%d' % (7+index))
                    cellule.setAttribute('table:formula', formule)

        table.removeChild(template)

        for template in (total_template, montant_template):        
            cellules = template.getElementsByTagName("table:table-cell")
            for i in range(cellules.length):
                cellule = cellules.item(i)
                if cellule.hasAttribute('table:formula'):
                    formule = cellule.getAttribute('table:formula')
                    formule = formule.replace('9', '%d' % (7 + len(inscriptions)))
                    formule = formule.replace('8', '%d' % (6 + len(inscriptions)))
                    cellule.setAttribute('table:formula', formule)
                    
        return None

    def executeTemplateCalcJulien(self, filename, dom):
        spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
        table = spreadsheet.getElementsByTagName("table:table")[0]
        lignes = table.getElementsByTagName("table:table-row")
        
        HEADER_LINE_COUNT = 5
        BODY_LINE_COUNT = 2
        FOOTER_LINE_COUNT = 1
        TEMPLATE_LINE_COUNT = HEADER_LINE_COUNT+BODY_LINE_COUNT+FOOTER_LINE_COUNT
        
        templateHeader = lignes[:HEADER_LINE_COUNT]
        templateLines = lignes[HEADER_LINE_COUNT:HEADER_LINE_COUNT + BODY_LINE_COUNT]
        templateFooter = lignes[HEADER_LINE_COUNT + BODY_LINE_COUNT:TEMPLATE_LINE_COUNT]

        date = self.start
        while date <= self.end:
            if date in database.creche.jours_fermeture:
                date += datetime.timedelta(1)
                continue

            # Header            
            for line in templateHeader:
                node = line.cloneNode(1)
                ReplaceFields(node, [('semaine', date.isocalendar()[1]),
                                     ('jour', days[date.weekday()]),
                                     ('date', date2str(date))])
                table.insertBefore(node, lignes[TEMPLATE_LINE_COUNT])
            
            # Body
            inscrits = GetLines(date, database.creche.inscrits, site=self.site, groupe=self.groupe)
            linesCount = 0
            for i, inscrit in enumerate(inscrits):
                if not isinstance(inscrit, BasePlanningSeparator):
                    if i % 2:
                        node = templateLines[1].cloneNode(1)
                    else:
                        node = templateLines[0].cloneNode(1)
                    fields = [('nom', inscrit.nom),
                              ('prenom', inscrit.prenom),
                              ('label', inscrit.label),
                              ('arrivee-depart', inscrit.GetHeureArriveeDepart()),
                              ('arrivee', inscrit.GetHeureArrivee()),
                              ('depart', inscrit.GetHeureDepart())]
                    ReplaceFields(node, fields)
                    table.insertBefore(node, lignes[TEMPLATE_LINE_COUNT])
                    linesCount += 1

            # Footer
            for line in templateFooter:
                node = line.cloneNode(1)
                ReplaceFields(node, [('count', linesCount)])
                table.insertBefore(node, lignes[TEMPLATE_LINE_COUNT])
            
            date += datetime.timedelta(1)
        
        for line in templateHeader + templateLines + templateFooter:
            table.removeChild(line)

    def executeTemplateCalc123APetitsPas(self, filename, dom):
        # Basé sur le template de Julien, mais avec un onglet par jour
        spreadsheet_template = dom.getElementsByTagName('office:spreadsheet').item(0)

        HEADER_LINE_COUNT = 5
        BODY_LINE_COUNT = 2
        FOOTER_LINE_COUNT = 0
        TEMPLATE_LINE_COUNT = HEADER_LINE_COUNT + BODY_LINE_COUNT + FOOTER_LINE_COUNT

        date = self.start
        while date <= self.end:
            if date not in database.creche.jours_fermeture:
                spreadsheet = spreadsheet_template.cloneNode(1)
                spreadsheet_template.parentNode.insertBefore(spreadsheet, spreadsheet_template)

                table = spreadsheet.getElementsByTagName("table:table")[0]
                table_name = "%s %s" % (days[date.weekday()], date2str(date))
                table_name = table_name.replace("/", "|")
                table.setAttribute("table:name", table_name)
                lignes = table.getElementsByTagName("table:table-row")

                templateHeader = lignes[:HEADER_LINE_COUNT]
                templateLines = lignes[HEADER_LINE_COUNT:HEADER_LINE_COUNT + BODY_LINE_COUNT]
                templateFooter = lignes[HEADER_LINE_COUNT + BODY_LINE_COUNT:TEMPLATE_LINE_COUNT]

                for groupe in database.creche.groupes:
                    # Header
                    for line in templateHeader:
                        node = line.cloneNode(1)
                        ReplaceFields(node, [('semaine', date.isocalendar()[1]),
                                             ('jour', days[date.weekday()]),
                                             ('date', date2str(date)),
                                             ('groupe', groupe.nom)
                                             ])
                        table.insertBefore(node, lignes[TEMPLATE_LINE_COUNT])

                    # Body
                    inscrits = GetLines(date, database.creche.inscrits, site=self.site, groupe=groupe)
                    linesCount = 0
                    for i, inscrit in enumerate(inscrits):
                        if i % 2:
                            node = templateLines[1].cloneNode(1)
                        else:
                            node = templateLines[0].cloneNode(1)
                        fields = [('nom', inscrit.nom),
                                  ('prenom', inscrit.prenom),
                                  ('label', inscrit.label),
                                  ('arrivee-depart', inscrit.GetHeureArriveeDepart()),
                                  ('arrivee', inscrit.GetHeureArrivee()),
                                  ('depart', inscrit.GetHeureDepart())]
                        ReplaceFields(node, fields)
                        table.insertBefore(node, lignes[TEMPLATE_LINE_COUNT])
                        linesCount += 1

                    # Footer
                    for line in templateFooter:
                        node = line.cloneNode(1)
                        ReplaceFields(node, [('count', linesCount)])
                        table.insertBefore(node, lignes[TEMPLATE_LINE_COUNT])

                # Remove the template lines
                for line in templateHeader + templateLines + templateFooter:
                    table.removeChild(line)

            date += datetime.timedelta(1)

        spreadsheet_template.parentNode.removeChild(spreadsheet_template)
generation/coordonnees_parents.py
class CoordonneesModifications(object):
    title = "Coordonnées des parents"
    template = "Coordonnees parents.odt"

    def __init__(self, site, date, selection=EXPORT_FAMILLES_PRESENTES):
        self.multi = False
        self.site = site
        if date is None:
            self.date = datetime.date.today()
        else:
            self.date = date
        self.selection = selection
        self.email = None
        if IsTemplateFile("Coordonnees parents.ods"):
            self.template = "Coordonnees parents.ods"
            self.default_output = "Coordonnees parents %s.ods" % GetDateString(self.date, weekday=False)
        else:
            self.template = "Coordonnees parents.odt"
            self.default_output = "Coordonnees parents %s.odt" % GetDateString(self.date, weekday=False)

    def get_inscrits(self):
        result = []
        for inscrit in database.creche.inscrits:
            temporalite = 0
            for inscription in inscrit.inscriptions:
                if not inscription.preinscription and (self.site is None or inscription.site == self.site):
                    if inscription.debut and inscription.debut > self.date:
                        temporalite = EXPORT_FAMILLES_FUTURES
                    elif inscription.fin and inscription.fin < self.date:
                        temporalite = EXPORT_FAMILLES_PARTIES
                    else:
                        temporalite = EXPORT_FAMILLES_PRESENTES
                        break
            if temporalite & self.selection:
                result.append(inscrit)
        result.sort(key=lambda x: GetPrenomNom(x))
        return result

    def get_salaries(self):
        result = []
        for salarie in database.creche.salaries:
            temporalite = 0
            for contrat in salarie.contrats:
                if self.site is None or contrat.site == self.site:
                    if contrat.debut and contrat.debut > self.date:
                        temporalite = EXPORT_FAMILLES_FUTURES
                    elif contrat.fin and contrat.fin < self.date:
                        temporalite = EXPORT_FAMILLES_PARTIES
                    else:
                        temporalite = EXPORT_FAMILLES_PRESENTES
                        break
            if temporalite & self.selection:
                result.append(salarie)
        result.sort(key=lambda x: GetPrenomNom(x))
        return result

    def execute(self, filename, dom):
        if self.template == "Coordonnees parents.ods":
            return self.execute_ods(filename, dom)
        else:
            return self.execute_odt(filename, dom)

    def execute_ods(self, filename, dom):
        if filename != 'content.xml':
            return None

        errors = {}
        spreadsheet = dom.getElementsByTagName('office:spreadsheet')[0]
        table = spreadsheet.getElementsByTagName("table:table")[0]
        lignes = table.getElementsByTagName("table:table-row")
        template = lignes[1]

        for inscrit in self.get_inscrits():
            inscrit_fields = GetInscritFields(inscrit)
            for parent in inscrit.famille.parents:
                if parent:
                    fields = inscrit_fields + GetParentFields(parent)
                    line = template.cloneNode(1)
                    ReplaceFields(line, fields)
                    table.insertBefore(line, template)
                    inscrit_fields = [(field[0], "") for field in inscrit_fields]
        table.removeChild(template)
        return errors

    def execute_odt(self, filename, dom):
        # print dom.toprettyxml()
        if filename != 'content.xml':
            return None

        fields = GetCrecheFields(database.creche)
        fields.append(('date', '%.2d/%.2d/%d' % (self.date.day, self.date.month, self.date.year)))
        ReplaceTextFields(dom, fields)

        inscrits = self.get_inscrits()

        for table in dom.getElementsByTagName("table:table"):
            if table.getAttribute("table:name") == "Enfants":
                template = table.getElementsByTagName("table:table-row")[1]
                # print template.toprettyxml()
                for inscrit in inscrits:
                    line = template.cloneNode(1)
                    referents = [GetPrenomNom(referent) for referent in inscrit.famille.referents]
                    parents = [GetPrenomNom(parent) for parent in inscrit.famille.parents if parent is not None]
                    ReplaceTextFields(line, [('prenom', inscrit.prenom),
                                             ('parents', parents),
                                             ('referents', referents),
                                             ('commentaire', None)])
                    phoneCell = line.getElementsByTagName('table:table-cell')[2]
                    phoneTemplate = phoneCell.getElementsByTagName('text:p')[0]
                    phones = {}  # clé: [téléphone, initiales, ?travail]
                    emails = set()
                    for parent in inscrit.famille.parents:
                        if parent:
                            emails.add(parent.email)
                            for phoneType in ["domicile", "portable", "travail"]:
                                phone = getattr(parent, "telephone_" + phoneType)
                                if phone:
                                    if phone in phones.keys():
                                        phones[phone][1] = ""
                                    else:
                                        phones[phone] = [phone, GetInitialesPrenom(parent), phoneType == "travail"]
                    for referent in inscrit.famille.referents:
                        phone = referent.telephone
                        phones[phone] = [phone, GetInitialesPrenom(referent), False]
                    for phone, initiales, phoneType in phones.values():
                        if initiales and phoneType:
                            remark = "(%s travail)" % initiales
                        elif initiales:
                            remark = "(%s)" % initiales
                        elif phoneType:
                            remark = "(travail)"
                        else:
                            remark = ""
                        phoneLine = phoneTemplate.cloneNode(1)
                        ReplaceTextFields(phoneLine, [('telephone', phone),
                                                      ('remarque', remark)])
                        phoneCell.insertBefore(phoneLine, phoneTemplate)
                    phoneCell.removeChild(phoneTemplate)
                    emailCell = line.getElementsByTagName('table:table-cell')[3]
                    emailTemplate = emailCell.getElementsByTagName('text:p')[0]
                    for email in emails:
                        emailLine = emailTemplate.cloneNode(1)
                        ReplaceTextFields(emailLine, [('email', email)])
                        emailCell.insertBefore(emailLine, emailTemplate)
                    emailCell.removeChild(emailTemplate)
                    table.insertBefore(line, template)
                table.removeChild(template)

            if table.getAttribute("table:name") == 'Employes':
                template = table.getElementsByTagName("table:table-row")[0]
                #print template.toprettyxml()
                for salarie in self.get_salaries():
                    if 1:  # TODO
                        line = template.cloneNode(1)
                        ReplaceTextFields(line, [('prenom', salarie.prenom),
                                                 ('nom', salarie.nom),
                                                 ('domicile', salarie.telephone_domicile),
                                                 ('portable', salarie.telephone_portable)])
                        table.insertBefore(line, template)
                table.removeChild(template)

        return None
generation/export_facturation.py
class ExportFacturationModifications(object):
    def __init__(self, annee):
        self.multi = False
        self.template = 'Export facturation.ods'
        self.default_output = "Export facturation %d.ods" % annee
        self.annee = annee
        self.site = None
        self.errors = {}
        self.email = None

    def execute(self, filename, dom):
        if filename == 'styles.xml':
            ReplaceTextFields(dom, GetCrecheFields(database.creche))
            return []

        elif filename == 'content.xml':
            spreadsheet = dom.getElementsByTagName('office:spreadsheet').item(0)
            tables = spreadsheet.getElementsByTagName("table:table")
            template_table = tables.item(0)

            for m in range(0, 12, 2):
                debut = datetime.date(self.annee, m + 1, 1)
                fin = GetNextMonthStart(debut)
                if fin > datetime.date.today():
                    break
                fin = GetMonthEnd(fin)

                table = template_table.cloneNode(1)
                table.setAttribute("table:name", "%s-%s %d" % (months[m], months[m + 1], self.annee))
                spreadsheet.insertBefore(table, template_table)

                lines = table.getElementsByTagName("table:table-row")
                template_line = lines[2]
                total_line = lines[3]
                total_cell = total_line.getElementsByTagName("table:table-cell")[2]

                inscrits = database.creche.select_inscrits(debut, fin)
                index = 0
                for i, inscrit in enumerate(inscrits):
                    try:
                        facture = Facture(inscrit, self.annee, m+1, NO_NUMERO)
                        if facture.total > 0:
                            facture2 = Facture(inscrit, self.annee, m+2, NO_NUMERO)
                            facture.jours_realises += facture2.jours_realises
                            for activity in database.creche.activites:
                                label = activity.label
                                facture.heures_supplement_activites[label] += facture2.heures_supplement_activites[label]
                        else:
                            facture = Facture(inscrit, self.annee, m+2, NO_NUMERO)
                        if facture.total == 0:
                            continue
                        line = template_line.cloneNode(1)
                        fields = GetInscritFields(inscrit) + GetFactureFields(facture)
                    except CotisationException as e:
                        self.errors[GetPrenomNom(inscrit)] = e.errors
                        continue

                    # print fields
                    ReplaceFields(line, fields)
                    IncrementFormulas(line, row=+index)
                    table.insertBefore(line, template_line)
                    index += 1

                table.removeChild(template_line)
                total_cell.setAttribute("table:formula", "of:=SUM([.R3:.R%d])" % (2+index))

            spreadsheet.removeChild(template_table)

        return self.errors


Question: import wx.grid
import wx.html
import wx.lib.expando
import wx.lib.scrolledpanel
from controls import *
from generation.commande_repas import CommandeRepasModifications
from generation.compte_exploitation import CompteExploitationModifications
from generation.coordonnees_parents import CoordonneesModifications
from generation.etat_places import EtatPlacesModifications
from generation.etat_presence_mensuel import EtatPresenceMensuelModifications
from generation.etat_presences import EtatsPresenceModifications
from generation.etats_inscriptions import EtatsInscriptionsModifications
from generation.etats_trimestriels import EtatsTrimestrielsModifications
from generation.export_filoue import ExportFiloueModifications
from generation.export_tablette import ExportTabletteModifications
from generation.planning import PlanningModifications, PlanningHoraireModifications
from generation.planning_detaille import PlanningDetailleModifications
from generation.rapport_frequentation import RapportFrequentationModifications
from generation.releve_detaille import ReleveDetailleModifications
from generation.releve_salaries import ReleveSalariesModifications
from generation.releve_siej import ReleveSIEJModifications
from generation.synthese_financiere import SyntheseFinanciereModifications
from generation.export_facturation import ExportFacturationModifications
from document_dialog import *
from planning import *
from alertes import *
from statistiques import GetStatistiques
        self.releve_salaries_monthchoice = wx.Choice(self)
        self.Bind(wx.EVT_CHOICE, self.EvtReleveSalariesSalarieChoice, self.salaries_choice["releves"])
        self.Bind(wx.EVT_CHOICE, self.EvtReleveSalariesMonthChoice, self.releve_salaries_monthchoice)
        self.releve_salaries_button = wx.Button(self, -1, "Génération")
        self.Bind(wx.EVT_BUTTON, self.OnGenerationReleveSalaries, self.releve_salaries_button)
        box_sizer.AddMany([(self.salaries_choice["releves"], 1, wx.ALL | wx.EXPAND, 5), (self.releve_salaries_monthchoice, 1, wx.ALL | wx.EXPAND, 5), (self.releve_salaries_button, 0, wx.ALL, 5)])
        self.sizer.Add(box_sizer, 0, wx.EXPAND | wx.BOTTOM, 10)

        # Les exports tablette
        if (config.options & TABLETTE) and IsTemplateFile("Export tablette.ods"):
            box_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Export tablette"), wx.HORIZONTAL)
            self.export_tablette_choice = wx.Choice(self)
            AddMonthsToChoice(self.export_tablette_choice)
            button = wx.Button(self, -1, "Génération")
            self.Bind(wx.EVT_BUTTON, self.OnGenerationExportTablette, button)
            box_sizer.AddMany([(self.export_tablette_choice, 1, wx.ALL | wx.EXPAND, 5), (button, 0, wx.ALL, 5)])
            self.sizer.Add(box_sizer, 0, wx.EXPAND | wx.BOTTOM, 10)

        self.SetSizer(self.sizer)
        self.UpdateContents()        

    def UpdateContents(self):
        if len(database.creche.sites) > 1:
            self.site_choice.Show(True)
            site_selected = self.site_choice.GetSelection()
            self.site_choice.Clear()
            for site in database.creche.sites:
                self.site_choice.Append(site.get_name(), site)
            if site_selected < 0 or site_selected >= self.site_choice.GetCount():
                site_selected = 0
            self.site_choice.SetSelection(site_selected)                
        else:
            self.site_choice.Show(False)

        self.UpdateContentsReleveSalaries()
        self.sizer.Layout()
        self.Layout()

    def UpdateContentsReleveSalaries(self):
        for choice in self.salaries_choice.values():
            choice.Clear()
            choice.Append("Tous les salariés", database.creche.salaries)

        salaries = {}
        autres = {}
        for salarie in database.creche.salaries:
            if salarie.get_contrat(datetime.date.today()) is not None:
                salaries[GetPrenomNom(salarie)] = salarie
            else:
                autres[GetPrenomNom(salarie)] = salarie

        keys = salaries.keys()
        keys.sort()
        for key in keys:
            for choice in self.salaries_choice.values():
                choice.Append(key, salaries[key])

        if len(salaries) > 0 and len(autres) > 0:
            for choice in self.salaries_choice.values():
                choice.Append("-" * 20, None)

        keys = autres.keys()
        keys.sort()
        for key in keys:
            for choice in self.salaries_choice.values():
                choice.Append(key, autres[key])

        for choice in self.salaries_choice.values():
            choice.SetSelection(0)

        self.EvtReleveSalariesSalarieChoice(None)

    def GetSelectedSite(self):
        if len(database.creche.sites) > 1:
            current_site = self.site_choice.GetSelection()
            return self.site_choice.GetClientData(current_site)
        else:
            return None

    def EvtReleveSalariesSalarieChoice(self, _):
        self.releve_salaries_monthchoice.Clear()
        salarie = self.salaries_choice["releves"].GetClientData(self.salaries_choice["releves"].GetSelection())
        date = config.get_first_monday()
        while date <= datetime.date.today():
            if isinstance(salarie, list) or salarie.get_contrat(date):
                self.releve_salaries_monthchoice.Append("%s %d" % (months[date.month - 1], date.year), date)
            date = GetNextMonthStart(date)
        self.releve_salaries_monthchoice.SetSelection(self.releve_salaries_monthchoice.GetCount() - 1)
        self.EvtReleveSalariesMonthChoice()

    def EvtReleveSalariesMonthChoice(self, _=None):
        salaries, periode = self.__get_releve_salaries_periode()
        self.releve_salaries_button.Enable(periode is not None and len(salaries) > 0)

    def __get_releve_salaries_periode(self):
        salaries = self.salaries_choice["releves"].GetClientData(self.salaries_choice["releves"].GetSelection())
        periode = self.releve_salaries_monthchoice.GetClientData(self.releve_salaries_monthchoice.GetSelection())
        if isinstance(salaries, list):
            salaries = [salarie for salarie in salaries if salarie.get_contrat(periode)]
        else:
            salaries = [salaries]
        return salaries, periode

    def OnGenerationReleveSalaries(self, _):
        salaries, periode = self.__get_releve_salaries_periode()
        DocumentDialog(self, ReleveSalariesModifications(salaries, periode)).ShowModal()
            
    def OnGenerationCoordonnees(self, _):
        site = self.GetSelectedSite()
        date = str2date(self.coords_date.GetValue())
        DocumentDialog(self, CoordonneesModifications(site, date)).ShowModal()

    def OnGenerationEtatsInscriptions(self, _):
        site = self.GetSelectedSite()
        date = str2date(self.inscriptions_date.GetValue())
        DocumentDialog(self, EtatsInscriptionsModifications(site, date)).ShowModal()

    def OnGenerationReleveSIEJ(self, _):
        site = self.GetSelectedSite()
        annee = self.releves_choice.GetClientData(self.releves_choice.GetSelection())

Answer:         DocumentDialog(self, ReleveSIEJModifications(site, annee)).ShowModal()