Exemplo n.º 1
0
def creer_historique_texte(texte, format, dossier, cache, bdd):

    # Connexion à la base de données
    db = legi.utils.connect_db(bdd)

    # Créer le dossier si besoin
    sousdossier = '.'
    cid = texte[1]
    nom = texte[0] or cid

    Path(dossier).mkdir_p()
    entree_texte = db.one("""
        SELECT id, nature, titre, titrefull, etat, date_debut, date_fin
        FROM textes_versions
        WHERE id = '{0}'
    """.format(cid))
    if entree_texte[1].lower() in ('code', 'loi', 'ordonnance'):
        if not os.path.exists(
                os.path.join(dossier, entree_texte[1].lower() + 's')):
            os.makedirs(os.path.join(dossier, entree_texte[1].lower() + 's'))
        sousdossier = entree_texte[1].lower() + 's'
    elif entree_texte[1].lower() == 'decret':
        if not os.path.exists(os.path.join(dossier, u'décrets')):
            os.makedirs(os.path.join(dossier, u'décrets'))
        sousdossier = 'décrets'
    elif entree_texte[1].lower() == 'arrete':
        if not os.path.exists(os.path.join(dossier, u'arrêtés')):
            os.makedirs(os.path.join(dossier, u'arrêtés'))
        sousdossier = 'arrêtés'

    if texte[2]:
        identifiant, nom_fichier = normalisation_code(nom)
        sousdossier = os.path.join('codes', identifiant)
        Path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(cid, True)
    else:
        sousdossier = os.path.join(sousdossier, nom)
        nom_fichier = cid
    dossier = os.path.join(dossier, sousdossier)
    sousdossier = '.'
    if not os.path.exists(dossier):
        os.makedirs(dossier)
    fichier = os.path.join(dossier, nom_fichier + '.md')

    # Créer le dépôt Git
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.call(['git', 'init'], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--', sousdossier], cwd=dossier)

    if os.path.exists(fichier):
        raise Exception(
            'Fichier existant : la mise à jour de fichiers existants n’est pas encore prise en charge.'
        )

    # Vérifier que les articles ont été transformés en Markdown ou les créer le cas échéant
    creer_markdown_texte(texte, db, cache)

    # Sélection des versions du texte
    versions_texte_db = db.all("""
          SELECT debut
          FROM sommaires
          WHERE cid = '{0}'
        UNION
          SELECT fin
          FROM sommaires
          WHERE cid = '{0}'
    """.format(cid, cid))
    dates_texte = []
    versions_texte = []
    for vt in versions_texte_db:
        vt = vt[0]
        if isinstance(vt, basestring):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        dates_texte.append(vt)
    for i in range(0, len(dates_texte) - 1):
        debut = dates_texte[i]
        fin = dates_texte[i + 1]
        versions_texte.append((debut, fin))

    sql_texte = "cid = '{0}'".format(cid)

    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    for (i_version, version_texte) in enumerate(versions_texte):

        # Passer les versions 'nulles'
        #if version_texte.base is None:
        #    continue

        sql = sql_texte + " AND debut <= '{0}' AND ( fin >= '{1}' OR fin == '2999-01-01' )".format(
            version_texte[0], version_texte[1])

        # Créer l’en-tête
        date_fr = '{} {} {}'.format(version_texte[0].day,
                                    MOIS2[int(version_texte[0].month)],
                                    version_texte[0].year)
        if version_texte[0].day == 1:
            date_fr = '1er {} {}'.format(MOIS2[int(version_texte[0].month)],
                                         version_texte[0].year)
        contenu = nom + '\n'   \
                  + '\n'   \
                  + '- Date de consolidation : ' + date_fr + '\n'            \
                  + '- [Lien permanent Légifrance](https://www.legifrance.gouv.fr/affichCode.do?cidTexte=' + cid + '&dateTexte=' + str(version_texte[0].year) + '{:02d}'.format(version_texte[0].month) + '{:02d}'.format(version_texte[0].day) + ')\n' \
                  + '\n' \
                  + '\n'

        # Enregistrement du fichier
        if format['organisation'] != 'fichier-unique':
            f_texte = open('README.md', 'w')
            f_texte.write(contenu.encode('utf-8'))
            f_texte.close()

            # Retrait des fichiers des anciennes versions
            subprocess.call('rm *.md', cwd=dossier, shell=True)

        # Créer les sections (donc tout le texte)
        contenu = creer_sections(contenu, 1, None, version_texte, sql, [],
                                 format, dossier, db, cache)

        # Enregistrement du fichier
        if format['organisation'] == 'fichier-unique':
            f_texte = open(fichier, 'w')
            f_texte.write(contenu.encode('utf-8'))
            f_texte.close()

        # Exécuter Git
        date_base_legi = '{} {} {} {}:{}:{}'.format('18', 'juillet', '2014',
                                                    '11', '30',
                                                    '10')  # TODO changer cela
        subprocess.call(['git', 'add', '.'], cwd=dossier)
        subprocess.call([
            'git', 'commit', '--author="Législateur <>"',
            '--date="' + str(version_texte[0]) + 'T00:00:00Z"', '-m',
            'Version consolidée au {}\n\nVersions :\n- base LEGI : {}\n- programme Archéo Lex : {}'
            .format(date_fr, date_base_legi,
                    version_archeolex), '-q', '--no-status'
        ],
                        cwd=dossier)

        if version_texte[1] == None:
            logger.info('Version {} enregistrée (du {} à maintenant)'.format(
                i_version + 1, version_texte[0]))
        else:
            logger.info('Version {} enregistrée (du {} au {})'.format(
                i_version + 1, version_texte[0], version_texte[1]))
Exemplo n.º 2
0
def creer_historique_texte(texte, format, dossier, cache):

    # Créer le dossier si besoin
    sousdossier = '.'
    cid = texte[1]
    nom = texte[0] or cid

    path(dossier).mkdir_p()
    entree_texte = Texte.get(Texte.cid == cid)
    if entree_texte.nature in ('code', 'loi', 'ordonnance'):
        if not os.path.exists(os.path.join(dossier,
                                           entree_texte.nature + 's')):
            os.makedirs(os.path.join(dossier, entree_texte.nature + 's'))
        sousdossier = entree_texte.nature + 's'
    elif entree_texte.nature == 'decret':
        if not os.path.exists(os.path.join(dossier, u'décrets')):
            os.makedirs(os.path.join(dossier, u'décrets'))
        sousdossier = 'décrets'
    elif entree_texte.nature == 'arrete':
        if not os.path.exists(os.path.join(dossier, u'arrêtés')):
            os.makedirs(os.path.join(dossier, u'arrêtés'))
        sousdossier = 'arrêtés'

    if texte[2]:
        identifiant, nom_fichier = normalisation_code(nom)
        sousdossier = os.path.join('codes', identifiant)
        path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(cid, True)
    else:
        sousdossier = os.path.join(sousdossier, nom)
        nom_fichier = cid
    dossier = os.path.join(dossier, sousdossier)
    sousdossier = '.'
    if not os.path.exists(dossier):
        os.makedirs(dossier)
    fichier = os.path.join(dossier, nom_fichier + '.md')

    # Créer le dépôt Git
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.Popen(['git', 'init'], cwd=dossier)
    else:
        subprocess.Popen(['git', 'checkout', '--', sousdossier], cwd=dossier)

    if os.path.exists(fichier):
        raise Exception(
            'Fichier existant : la mise à jour de fichiers existants n’est pas encore prise en charge.'
        )

    # Vérifier que les articles ont été transformés en Markdown ou les créer le cas échéant
    creer_markdown_texte(texte, cache)

    # Sélection des versions du texte
    versions_texte = Version_texte.select().where(
        Version_texte.texte == texte[1])

    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    for (i_version, version_texte) in enumerate(versions_texte):

        # Passer les versions 'nulles'
        if version_texte.base is None:
            continue

        # Sélectionner les versions d’articles et sections présentes dans cette version de texte, c’est-à-dire celles créées avant et détruites après (ou jamais)
        articles =                                                                       \
            Article.select()                                                             \
                   .where(  (Article.texte == cid)                                       \
                          & (Article.debut <= version_texte.debut)                       \
                          & ((Article.fin >= version_texte.fin) | (Article.fin == None)) \
                         )

        versions_sections =                                                                              \
            Version_section.select()                                                                     \
                   .where(  (Version_section.texte == cid)                                               \
                          & (Version_section.debut <= version_texte.debut)                               \
                          & ((Version_section.fin >= version_texte.fin) | (Version_section.fin == None)) \
                         )

        # Créer l’en-tête
        date_fr = '{} {} {}'.format(version_texte.debut.day,
                                    MOIS2[int(version_texte.debut.month)],
                                    version_texte.debut.year)
        if version_texte.debut.day == 1:
            date_fr = '1er {} {}'.format(MOIS2[int(version_texte.debut.month)],
                                         version_texte.debut.year)
        contenu = nom + '\n'   \
                  + '\n'   \
                  + '- Date de consolidation : ' + date_fr + '\n'            \
                  + '- [Lien permanent Légifrance](http://legifrance.gouv.fr/affichCode.do?cidTexte=' + cid + '&dateTexte=' + str(version_texte.debut.year) + '{:02d}'.format(version_texte.debut.month) + '{:02d}'.format(version_texte.debut.day) + ')\n' \
                  + '\n' \
                  + '\n'

        # Créer les sections (donc tout le texte)
        contenu = creer_sections(contenu, 1, None, versions_sections, articles,
                                 version_texte, cid, cache)

        # Enregistrement du fichier
        f_texte = open(fichier, 'w')
        f_texte.write(contenu.encode('utf-8'))
        f_texte.close()

        # Exécuter Git
        date_base_legi = '{} {} {} {}:{}:{}'.format('18', 'juillet', '2014',
                                                    '11', '30',
                                                    '10')  # TODO changer cela
        subprocess.call(
            ['git', 'add',
             os.path.join(sousdossier, nom_fichier + '.md')],
            cwd=dossier)
        subprocess.call([
            'git', 'commit', '--author="Législateur <>"',
            '--date="' + str(version_texte.debut) + 'T00:00:00Z"', '-m',
            'Version consolidée au {}\n\nVersions :\n- base LEGI : {}\n- programme Archéo Lex : {}'
            .format(date_fr, date_base_legi,
                    version_archeolex), '-q', '--no-status'
        ],
                        cwd=dossier)

        if version_texte.fin == None:
            logger.info('Version {} enregistrée (du {} à maintenant)'.format(
                i_version, version_texte.debut))
        else:
            logger.info('Version {} enregistrée (du {} au {})'.format(
                i_version, version_texte.debut, version_texte.fin))
Exemplo n.º 3
0
def creer_historique_texte(texte, format, dossier, cache):
    
    # Créer le dossier si besoin
    nom = texte[0]
    cid = texte[1]
    sousdossier = '.'
    path(dossier).mkdir_p()
    path(os.path.join(dossier, 'codes')).mkdir_p()
    path(os.path.join(dossier, 'constitutions')).mkdir_p()
    path(os.path.join(dossier, 'lois')).mkdir_p()
    path(os.path.join(dossier, 'décrets')).mkdir_p()
    path(os.path.join(dossier, 'ordonnances')).mkdir_p()
    if texte[2]:
        identifiant, tmp1 = normalisation_code(nom)
        dossier = os.path.join(dossier, 'codes', identifiant)
        sousdossier = '.'
        path(dossier).mkdir_p()
        path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(cid, True)
    fichier = os.path.join(dossier, sousdossier, nom + '.md')
    
    # Créer le dépôt Git
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.Popen(['git', 'init'], cwd=dossier)
    else:
        subprocess.Popen(['git', 'checkout', '--', sousdossier], cwd=dossier)
    
    if os.path.exists(fichier):
        raise Exception('Fichier existant : la mise à jour de fichiers existants n’est pas encore prise en charge.')
    
    # Vérifier que les articles ont été transformés en Markdown ou les créer le cas échéant
    creer_markdown_texte(texte, cache)
    
    # Sélection des versions du texte
    versions_texte = Version_texte.select().where(Version_texte.texte == texte[1])
    
    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    for (i_version, version_texte) in enumerate(versions_texte):
        
        # Passer les versions 'nulles'
        if version_texte.base is None:
            continue
        
        # Sélectionner les versions d’articles et sections présentes dans cette version de texte, c’est-à-dire celles créées avant et détruites après (ou jamais)
        articles =                                                                       \
            Article.select()                                                             \
                   .where(  (Article.texte == cid)                                       \
                          & (Article.debut <= version_texte.debut)                       \
                          & ((Article.fin >= version_texte.fin) | (Article.fin == None)) \
                         )
        
        versions_sections =                                                                              \
            Version_section.select()                                                                     \
                   .where(  (Version_section.texte == cid)                                               \
                          & (Version_section.debut <= version_texte.debut)                               \
                          & ((Version_section.fin >= version_texte.fin) | (Version_section.fin == None)) \
                         )
        
        # Créer l’en-tête
        date_fr = '{} {} {}'.format(version_texte.debut.day, MOIS2[int(version_texte.debut.month)], version_texte.debut.year)
        if version_texte.debut.day == 1:
            date_fr = '1er {} {}'.format(MOIS2[int(version_texte.debut.month)], version_texte.debut.year)
        contenu = nom + '\n'   \
                  + '\n'   \
                  + '- Date de consolidation : ' + date_fr + '\n'            \
                  + '- [Lien permanent Légifrance](http://legifrance.gouv.fr/affichCode.do?cidTexte=' + cid + '&dateTexte=' + str(version_texte.debut.year) + '{:02d}'.format(version_texte.debut.month) + '{:02d}'.format(version_texte.debut.day) + ')\n' \
                  + '\n' \
                  + '\n'
        
        # Créer les sections (donc tout le texte)
        contenu = creer_sections(contenu, 1, None, versions_sections, articles, version_texte, cid, cache)
        
        # Enregistrement du fichier
        f_texte = open(fichier, 'w')
        f_texte.write(contenu.encode('utf-8'))
        f_texte.close()
        
        # Exécuter Git
        subprocess.call(['git', 'add', os.path.join(sousdossier, nom + '.md')], cwd=dossier)
        subprocess.call(['git', 'commit', '--author="Législateur <>"', '--date="' + str(version_texte.debut) + 'T00:00:00Z"', '-m', 'Version consolidée au {}'.format(date_fr), '-q', '--no-status'], cwd=dossier)
        
        if version_texte.fin == None:
            logger.info('Version {} enregistrée (du {} à maintenant)'.format(i_version, version_texte.debut))
        else:
            logger.info('Version {} enregistrée (du {} au {})'.format(i_version, version_texte.debut, version_texte.fin))
Exemplo n.º 4
0
def creer_historique_legi(textes, format, dossier, bdd, production):

    if os.path.exists( textes ):
        f_textes = open( textes, 'r' )
        textes = f_textes.read()
        f_textes.close()

    if not os.path.exists( bdd ):
        raise Exception( 'Base de données legi.py manquante.' )

    textes = textes.strip()
    textes = re.split( r'[\n,]+', textes )

    liste_textes = []
    natures = []
    db = legi.utils.connect_db(bdd)
    if 'tout' in textes:
        liste_textes = db.all("""
              SELECT cid
              FROM textes_versions
              ORDER BY cid
        """)
        liste_textes = [ x[0] for x in liste_textes ]
        logger.info( '\nListe de textes : tous\n' )
    elif 'tout-obsolete' in textes:
        last_update = db.one("""
            SELECT value
            FROM db_meta
            WHERE key = 'last_update'
        """)
        liste_textes = db.all("""
              SELECT cid
              FROM textes_versions
              WHERE mtime > {0}
              ORDER BY cid
        """.format(last_update))
        liste_textes = [ x[0] for x in liste_textes ]
    else:
        for texte in textes:
            if re.match( r'^(JORF|LEGI)TEXT[0-9]{12}$', texte ):
                liste_textes.append( texte )
            elif re.match( r'^aleatoire-([0-9]+)$', texte ):
                m = re.match( r'^aleatoire-([0-9]+)$', texte )
                m = int( m.group(1) )
                liste = db.all("""
                      SELECT cid
                      FROM textes_versions
                      ORDER BY RANDOM()
                      LIMIT {0}
                """.format(m))
                liste = [ x[0] for x in liste ]
                liste_textes.extend( liste )
            else:
                if len( natures ) == 0:
                    liste = db.all( """
                          SELECT DISTINCT nature
                          FROM textes_versions
                    """ )
                    natures = [ x[0] for x in liste ]
                if texte.upper() in natures:
                    liste = db.all( """
                          SELECT cid
                          FROM textes_versions
                          WHERE nature = '{0}'
                    """.format( texte.upper() ) )
                    liste = [ x[0] for x in liste ]
                    liste_textes.extend( liste )
                else:
                    raise Exception( 'Mauvaise spécification de la liste de textes' )

    liste_textes.sort()
    if len( liste_textes ) < 100:
        logger.info( '\nListe de textes :\n' + '\n'.join( liste_textes ) + '\n' )

    args = [(texte, format, dossier, bdd) for texte in liste_textes]
    if len(liste_textes) == 1 or os.cpu_count() == 1 or not production:
        textes_traites = list(map(creer_historique_texte, args))
    else:
        nb_procs = min( len(liste_textes), os.cpu_count() )
        textes_traites = list(Pool(nb_procs).map(creer_historique_texte, args))

    return textes_traites
Exemplo n.º 5
0
def ranger(textes, cache):

    logger.info(textes)
    for texte in textes:

        lire_code_xml(texte, cache)
Exemplo n.º 6
0
def creer_historique_texte(arg):

    texte, format, dossier, bdd = arg

    logger.info( '> Texte {0}'.format( texte ) )

    # Constantes
    paris = timezone( 'Europe/Paris' )
    annee2100 = paris.localize( datetime.datetime( 2100, 1, 1 ) )
    annee1970 = paris.localize( datetime.datetime( 1970, 1, 1 ) )

    # Connexion à la base de données
    db = legi.utils.connect_db(bdd)

    # Créer le dossier si besoin
    sousdossier = '.'
    id = texte
    nom = texte

    # Flags: 1) s’il y a des versions en vigueur future, 2) si la première version est une vigueur future
    futur = False
    futur_debut = False

    # Obtenir la date de la base LEGI
    last_update = db.one("""
        SELECT value
        FROM db_meta
        WHERE key = 'last_update'
    """)
    last_update_jour = datetime.date(*(time.strptime(last_update, '%Y%m%d-%H%M%S')[0:3]))
    last_update = paris.localize( datetime.datetime(*(time.strptime(last_update, '%Y%m%d-%H%M%S')[0:6])) )
    date_base_legi_fr = date_en_francais( last_update )
    logger.info('Dernière mise à jour de la base LEGI : {}'.format(last_update.isoformat()))

    os.makedirs(dossier, exist_ok=True)
    entree_texte = db.one("""
        SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, visas, signataires, tp, nota, abro, rect, cid, mtime, date_texte
        FROM textes_versions
        WHERE id = '{0}'
    """.format(id))
    if entree_texte == None:
        entree_texte = db.one("""
            SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, visas, signataires, tp, nota, abro, rect, cid, mtime, date_texte
            FROM textes_versions
            WHERE cid = '{0}'
        """.format(id))
    if entree_texte == None:
        raise Exception('Pas de texte avec cet ID ou CID')

    texte_id = entree_texte[0]
    nature = entree_texte[1]
    titre = entree_texte[2]
    titrefull = entree_texte[3]
    etat_texte = entree_texte[4]
    date_debut_texte = entree_texte[5] if entree_texte[5] and entree_texte[5] != '2999-01-01' else None
    date_fin_texte = entree_texte[6] if entree_texte[6] and entree_texte[6] != '2999-01-01' else None
    visas = entree_texte[8] or ''
    signataires = entree_texte[9] or ''
    tp = entree_texte[10] or ''
    nota = entree_texte[11] or ''
    abro = entree_texte[12] or ''
    rect = entree_texte[13] or ''
    cid = entree_texte[14]
    mtime = entree_texte[15]
    date_promulgation_texte = entree_texte[16] if entree_texte[16] and entree_texte[16] != '2999-01-01' else None

    visas = visas.strip()
    signataires = signataires.strip()
    tp = tp.strip()
    nota = nota.strip()
    abro = abro.strip()
    rect = rect.strip()

    nature_min = nature.lower()
    nature_min_pluriel = re.sub( r'([- ])', r's\1', nature_min ) + 's'
    if nature in natures.keys():
        nature_min = natures[nature]
        nature_min_pluriel = re.sub( r'([- ])', r's\1', nature_min ) + 's'
        os.makedirs(os.path.join(dossier, nature_min_pluriel), exist_ok=True)
        sousdossier = nature_min_pluriel

    mise_a_jour = True
    if nature and (nature in natures.keys()) and entree_texte[7]:
        identifiant = nature_min+' '+entree_texte[7]
        identifiant = identifiant.replace(' ','_')
        nom_fichier = identifiant
        sousdossier = os.path.join(nature_min_pluriel, identifiant)
        if not os.path.exists(os.path.join(dossier, sousdossier)):
            mise_a_jour = False
        os.makedirs(os.path.join(dossier, sousdossier), exist_ok=True)
    elif nature and (nature in natures.keys()) and titre:
        identifiant = titrefull[0].lower()+titrefull[1:].replace(' ','_')
        nom_fichier = identifiant
        sousdossier = os.path.join(nature_min_pluriel, identifiant)
        if not os.path.exists(os.path.join(dossier, sousdossier)):
            mise_a_jour = False
        os.makedirs(os.path.join(dossier, sousdossier), exist_ok=True)
    else:
        raise Exception('Type bizarre ou inexistant')
        sousdossier = os.path.join(sousdossier, nom)
        nom_fichier = id
    dossier = os.path.join(dossier, sousdossier)
    dossier_final = sousdossier
    nom_final = identifiant
    sousdossier = '.'
    os.makedirs(dossier, exist_ok=True)
    fichier = os.path.join(dossier, nom_fichier + '.md')
    logger.info('Dossier : {}'.format(dossier))

    # Créer le dépôt Git avec comme branche maîtresse 'texte' ou 'articles'
    branche = None
    git_ref_base = None
    git_ref = git_ref_base
    if format['organisation'] in [ 'texte', 'articles', 'sections' ]:
        branche = format['organisation']
        git_ref_base = 'refs/' + format['organisation'] + '/' + format['dialecte'] + '/'
        git_ref = git_ref_base
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.call(['git', 'init'], cwd=dossier)
        subprocess.call(['git', 'symbolic-ref', 'HEAD', 'refs/heads/'+branche], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--', sousdossier], cwd=dossier)

    # Vérification si la référence nécessaire existe ; dans le cas contraire, ce n’est pas une mise à jour
    git_refs = ''
    if mise_a_jour:
        git_refs = str( subprocess.check_output(['git', 'show-ref'], cwd=dossier), 'utf-8' ).strip()
        git_refs_base = re.search( '^([0-9a-f]{40}) ' + git_ref_base + '([0-9]{8}-[0-9]{6})/vigueur(?:-future)?$', git_refs, flags=re.MULTILINE )
        mise_a_jour = ( git_refs_base != None )
        if not mise_a_jour:
            subprocess.call(['git', 'checkout', '--orphan', branche], cwd=dossier)
            subprocess.call(['git', 'rm', '--cached', '-rf', '.'], cwd=dossier, stdout=subprocess.DEVNULL)
            subprocess.call(['git', 'clean', '-f', '-x', '-d'], cwd=dossier, stdout=subprocess.DEVNULL)

    date_reprise_git = None
    reset_hash = ''
    if mise_a_jour:
        date_maj_git = False
        git_refs_base = re.findall( '^([0-9a-f]{40}) ' + git_ref_base + '([0-9]{8}-[0-9]{6})/(vigueur(?:-future)?)$', git_refs, flags=re.MULTILINE )
        git_refs_base = sorted( git_refs_base, key = lambda x: x[1]+x[2] )
        if git_refs_base:
            date_maj_git = paris.localize( datetime.datetime(*(time.strptime(git_refs_base[-1][1], '%Y%m%d-%H%M%S')[0:6])) )
        else:
            raise Exception('Pas de tag de la dernière mise à jour')        
        logger.info('Dernière mise à jour du dépôt : {}'.format(date_maj_git.isoformat()))

        # Obtention de la première date qu’il faudra mettre à jour
        r1 = db.one("""
            SELECT date_debut
            FROM articles
            WHERE cid = '{0}' AND mtime > {1} + 10
            ORDER BY date_debut
        """.format(cid,int(time.mktime(date_maj_git.timetuple()))))
        r2 = db.one("""
            SELECT sommaires.debut
            FROM sommaires
            INNER JOIN sections
               ON sommaires.element = sections.id
            WHERE sommaires.cid = '{0}' AND sections.mtime > {1} + 10
            ORDER BY sommaires.debut
        """.format(cid,int(time.mktime(date_maj_git.timetuple()))))
        if r1:
            date_reprise_git = r1
        if r2:
            date_reprise_git = min(r1, r2) if r1 else r2

        # Noter que le mtime des fichiers retarde de quelques secondes par rapport à la date de référence de la base LEGI
        if int(time.mktime(date_maj_git.timetuple())) >= mtime - 10 or not date_reprise_git:
            logger.info( 'Dossier : {0}'.format(dossier) )
            logger.info('Pas de mise à jour disponible')
            if git_refs_base[-1][1] == last_update.strftime('%Y%m%d-%H%M%S'):
                return

            logger.info('Ajout de la référence à la base LEGI du jour')
            if git_refs_base[-1][2] == 'vigueur':
                subprocess.call(['git', 'update-ref', git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur', git_refs_base[-1][0]], cwd=dossier)
            elif git_refs_base[-1][2] == 'vigueur-future':
                subprocess.call(['git', 'update-ref', git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur-future', git_refs_base[-1][0]], cwd=dossier)
                if len(git_refs_base) > 1 and git_refs_base[-2][1] == git_refs_base[-1][1] and git_refs_base[-2][2] == 'vigueur':
                    subprocess.call(['git', 'update-ref', git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur', git_refs_base[-2][0]], cwd=dossier)
            nettoyer_refs_intermediaires(dossier)
            return

        # Lecture des versions en vigueur dans le dépôt Git
        try:
            if subprocess.check_output(['git', 'rev-parse', '--verify', branche+'-futur'], cwd=dossier, stderr=subprocess.DEVNULL):
                subprocess.call(['git', 'checkout', branche+'-futur'], cwd=dossier)
        except subprocess.CalledProcessError:
            pass
        versions_git = str( subprocess.check_output(['git', 'log', '--oneline'], cwd=dossier), 'utf-8' ).strip().split('\n')
        for log_version in versions_git:
            for m, k in MOIS.items():
                log_version = log_version.replace( m, k )
            m = re.match(r'^([0-9a-f]+) .* ([0-9]+)(?:er)? ([0-9]+) ([0-9]+)$', log_version)
            if not m:
                raise Exception('Version non reconnue dans le dépôt Git')
            date = '{0:04d}-{1:02d}-{2:02d}'.format(int(m.group(4)), int(m.group(3)), int(m.group(2)))
            reset_hash = m.group(1)
            if date < date_reprise_git:
                break
            reset_hash = ''

        if reset_hash:
            if date_reprise_git <= last_update_jour.strftime('%Y-%m-%d'):
                subprocess.call(['git', 'checkout', branche], cwd=dossier)
            subprocess.call(['git', 'reset', '--hard', reset_hash], cwd=dossier) 
        else:
            subprocess.call(['git', 'branch', '-m', branche, 'junk'], cwd=dossier)
            subprocess.call(['git', 'checkout', '--orphan', branche], cwd=dossier)
            subprocess.call(['git', 'branch', '-D', 'junk'], cwd=dossier)
            subprocess.call(['git', 'rm', '--cached', '-rf', '.'], cwd=dossier, stdout=subprocess.DEVNULL)
            subprocess.call(['git', 'clean', '-f', '-x', '-d'], cwd=dossier, stdout=subprocess.DEVNULL)
        try:
            if subprocess.check_output(['git', 'rev-parse', '--verify', branche+'-futur'], cwd=dossier, stderr=subprocess.DEVNULL):
                subprocess.call(['git', 'branch', '-D', branche+'-futur'], cwd=dossier)
        except subprocess.CalledProcessError:
            pass

    # Sélection des versions du texte
    versions_texte_db = db.all("""
          SELECT DISTINCT debut, fin
          FROM sommaires
          WHERE cid = '{0}'
            AND debut < fin
          ORDER BY debut
    """.format(cid))
    dates_texte = []
    dates_fin_texte = []
    versions_texte = []
    for vers in versions_texte_db:
        vt = vers[0]
        if isinstance(vt, str):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        if not date_reprise_git or vt.strftime('%Y-%m-%d') >= date_reprise_git:
            dates_texte.append( vt )
        vt = vers[1]
        if isinstance(vt, str):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        if not date_reprise_git or vt.strftime('%Y-%m-%d') >= date_reprise_git:
            dates_fin_texte.append( vt )
    versions_texte = sorted(set(dates_texte).union(set(dates_fin_texte)))
    
    versions_texte = sorted(list(set(versions_texte)))

    if not versions_texte:
        versions_texte_db = db.all("""
              SELECT DISTINCT debut, fin
              FROM sommaires
              WHERE cid = '{0}'
        """.format(cid))
        if len(list(versions_texte_db)):
            if isinstance(date_debut_texte, str):
                versions_texte.append(datetime.date(*(time.strptime(date_debut_texte, '%Y-%m-%d')[0:3])))
            if isinstance(date_fin_texte, str):
                versions_texte.append(datetime.date(*(time.strptime(date_fin_texte, '%Y-%m-%d')[0:3])))
            else:
                versions_texte.append(datetime.date(*(time.strptime('2999-01-01', '%Y-%m-%d')[0:3])))
        else:
            return

    syntaxe = Markdown()
    if format['organisation'] == 'articles':
        un_article_par_fichier_sans_hierarchie = UnArticleParFichierSansHierarchie('md')
        stockage = StockageGitFichiers(dossier, un_article_par_fichier_sans_hierarchie)
    elif format['organisation'] == 'sections':
        un_article_par_fichier_avec_hierarchie = UnArticleParFichierAvecHierarchie('md')
        stockage = StockageGitFichiers(dossier, un_article_par_fichier_avec_hierarchie)
    else:
        fichier_unique = FichierUnique('md')
        stockage = StockageGitFichiers(dossier, fichier_unique)
    fa = FabriqueArticle( db, stockage, syntaxe, True )
    fs = FabriqueSection( fa )

    # Conversion en syntaxe des en-têtes et pied-de-texte
    if visas:
        visas_titre = fs.syntaxe.obtenir_titre( [(0, 'VISAS', 'Visas')], 'Visas' )
        visas = fs.syntaxe.transformer_depuis_html( visas ) + '\n\n'
    if signataires:
        signataires_titre = fs.syntaxe.obtenir_titre( [(0, 'SIGNATAIRES', 'Signataires')], 'Signataires' )
        signataires = fs.syntaxe.transformer_depuis_html( signataires )
    if tp:
        tp_titre = fs.syntaxe.obtenir_titre( [(0, 'TP', 'Travaux préparatoires')], 'Travaux préparatoires' )
        tp = fs.syntaxe.transformer_depuis_html( tp )
    if nota:
        nota_titre = fs.syntaxe.obtenir_titre( [(0, 'NOTA', 'Notas')], 'Notas' )
        nota = fs.syntaxe.transformer_depuis_html( nota )

    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    wnbver = str(len(str(len(versions_texte))))
    erreurs = {
        'versions_manquantes': False,
        'date_pre_1970': False,
        'date_post_2100': False,
    }
    branche_courante = branche
    for (i_version, version_texte) in enumerate(versions_texte):
        
        # Passer les versions 'nulles'
        #if version_texte.base is None:
        #    continue
        if i_version >= len(versions_texte)-1:
            break

        debut = versions_texte[i_version]
        fin = versions_texte[i_version+1]
        debut_datetime = paris.localize( datetime.datetime( debut.year, debut.month, debut.day ) )

        if not futur and debut > last_update_jour:
            if i_version == 0:
                subprocess.call(['git', 'symbolic-ref', 'HEAD', 'refs/heads/'+branche+'-futur'], cwd=dossier)
                if not reset_hash:
                    futur_debut = True
            else:
                subprocess.call(['git', 'checkout', '-b', branche+'-futur'], cwd=dossier)
            futur = True
            branche_courante = branche + '-futur'

        # Retrait des fichiers des anciennes versions
        if format['organisation'] != 'texte':
            rmrf(set(os.listdir(dossier)) - {'.git'}, dossier)

        # Créer les sections (donc tout le texte)
        contenu, fin_vigueur = fs.obtenir_texte_section( None, [], cid, debut, fin )
        
        if not contenu.strip():
            if str(fin) == '2999-01-01':
                logger.info(('Version {:'+wnbver+'} (du {} à  maintenant) non-enregistrée car vide').format(i_version+1, debut))
            else:
                logger.info(('Version {:'+wnbver+'} (du {} au {}) non-enregistrée car vide').format(i_version+1, debut, fin))
            erreurs['versions_manquantes'] = True
            continue

        # Ajout des en-têtes et pied-de-texte
        date_debut_fr = date_en_francais( debut )
        if visas:
            contenu = visas_titre + visas + contenu
            fs.stockage.ecrire_ressource( 'VISAS', [(0, 'VISAS', 'Visas')], '', 'Visas', visas )
        if signataires:
            contenu += signataires_titre + signataires
            fs.stockage.ecrire_ressource( 'SIGNATAIRES', [(0, 'SIGNATAIRES', 'Signataires')], '', 'Signataires', signataires )
        if tp:
            contenu += tp_titre + tp
            fs.stockage.ecrire_ressource( 'TP', [(0, 'TP', 'Travaux préparatoires')], '', 'Travaux préparatoires', tp )
        if nota:
            contenu += nota_titre + nota
            fs.stockage.ecrire_ressource( 'NOTA', [(0, 'NOTA', 'Nota')], '', 'Nota', nota )

        # Enregistrement du fichier global
        fs.stockage.ecrire_ressource( cid, [], '', nom_fichier, contenu )

        if not subprocess.check_output(['git', 'status', '--ignored', '-s'], cwd=dossier):
            if str(fin) == '2999-01-01':
                logger.info(('Version {:'+wnbver+'} (du {} à  maintenant) non-enregistrée car identique à la précédente').format(i_version+1, debut))
            else:
                logger.info(('Version {:'+wnbver+'} (du {} au {}) non-enregistrée car identique à la précédente').format(i_version+1, debut, fin))
            erreurs['versions_manquantes'] = True
            continue

        annee_incompatible = ''
        git_debut_datetime = debut_datetime
        if  debut_datetime <= annee1970:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_pre_1970'] = True
            git_debut_datetime = paris.localize( datetime.datetime( 1970, 1, 1, 12 ) )
        if debut_datetime >= annee2100:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_post_2100'] = True
            git_debut_datetime = paris.localize( datetime.datetime( 2099, 1, 1, 12 ) )

        # Enregistrer les fichiers dans Git
        subprocess.call(['git', 'commit', '--author="Législateur <>"', '--date="' + str(git_debut_datetime) + '"', '-m', 'Version consolidée au {}'.format(date_debut_fr), '-q', '--no-status'], cwd=dossier, env={ 'GIT_COMMITTER_DATE': str(git_debut_datetime), 'GIT_COMMITTER_NAME': 'Législateur', 'GIT_COMMITTER_EMAIL': '' })

        if ( format['dates-git-pre-1970'] and debut_datetime <= annee1970 ) or ( format['dates-git-post-2100'] and debut_datetime >= annee2100 ):
            annee_incompatible = ''
            commit = str( subprocess.check_output(['git', 'cat-file', '-p', 'HEAD'], cwd=dossier), 'utf-8' )
            commit = re.sub( r'author Législateur <> (-?\d+ [+-]\d{4})', 'author Législateur <> ' + str(int(debut_datetime.timestamp())) + debut_datetime.strftime(' %z'), commit )
            commit = re.sub( r'committer Législateur <> (-?\d+ [+-]\d{4})', 'committer Législateur <> ' + str(int(debut_datetime.timestamp())) + debut_datetime.strftime(' %z'), commit )
            sha1 = str( subprocess.check_output( ['git', 'hash-object', '-t', 'commit', '-w', '--stdin'], cwd=dossier, input=bytes(commit, 'utf-8') ), 'utf-8' )
            sha1 = re.sub( '[^a-f0-9]', '', sha1 )
            subprocess.call(['git', 'update-ref', 'refs/heads/' + branche_courante, sha1], cwd=dossier)

        if str(fin) == '2999-01-01':
            logger.info(('Version {:'+wnbver+'} (du {} à  maintenant) enregistrée{}').format(i_version+1, debut, annee_incompatible))
        else:
            logger.info(('Version {:'+wnbver+'} (du {} au {}) enregistrée{}').format(i_version+1, debut, fin, annee_incompatible))

    # Texte abrogé
    if date_fin_texte:
        subprocess.call(['git', 'rm', '*'], cwd=dossier, stdout=subprocess.DEVNULL)
        annee_incompatible = ''
        debut = datetime.date(*(time.strptime(date_fin_texte, '%Y-%m-%d')[0:3]))
        date_debut_fr = date_en_francais( debut )
        debut_datetime = paris.localize( datetime.datetime( debut.year, debut.month, debut.day ) )
        git_debut_datetime = debut_datetime
        if  debut_datetime <= annee1970:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_pre_1970'] = True
            git_debut_datetime = paris.localize( datetime.datetime( 1970, 1, 1, 12 ) )
        if debut_datetime >= annee2100:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_post_2100'] = True
            git_debut_datetime = paris.localize( datetime.datetime( 2099, 1, 1, 12 ) )
        subprocess.call(['git', 'commit', '--author="Législateur <>"', '--date="' + str(git_debut_datetime) + '"', '-m', 'Texte abrogé au {}'.format(date_debut_fr), '-q', '--no-status'], cwd=dossier, env={ 'GIT_COMMITTER_DATE': str(git_debut_datetime), 'GIT_COMMITTER_NAME': 'Législateur', 'GIT_COMMITTER_EMAIL': '' })
        if ( format['dates-git-pre-1970'] and debut_datetime <= annee1970 ) or ( format['dates-git-post-2100'] and debut_datetime >= annee2100 ):
            annee_incompatible = ''
            commit = str( subprocess.check_output(['git', 'cat-file', '-p', 'HEAD'], cwd=dossier), 'utf-8' )
            commit = re.sub( r'author Législateur <> (-?\d+ [+-]\d{4})', 'author Législateur <> ' + str(int(debut_datetime.timestamp())) + debut_datetime.strftime(' %z'), commit )
            commit = re.sub( r'committer Législateur <> (-?\d+ [+-]\d{4})', 'committer Législateur <> ' + str(int(debut_datetime.timestamp())) + debut_datetime.strftime(' %z'), commit )
            sha1 = str( subprocess.check_output( ['git', 'hash-object', '-t', 'commit', '-w', '--stdin'], cwd=dossier, input=bytes(commit, 'utf-8') ), 'utf-8' )
            sha1 = re.sub( '[^a-f0-9]', '', sha1 )
            subprocess.call(['git', 'update-ref', 'refs/heads/' + branche_courante, sha1], cwd=dossier)
        subprocess.call(['git', 'branch', '-m', branche_courante, branche + '-abrogé'], cwd=dossier)
        branche_courante = branche + '-abrogé'
        subprocess.call(['git', 'update-ref', '-d', 'refs/heads/' + branche], cwd=dossier)
        subprocess.call(['git', 'update-ref', '-d', 'refs/heads/' + branche + '-futur'], cwd=dossier)
        logger.info(('Version {:'+wnbver+'} (   abrogation au {}) enregistrée{}').format(i_version+1, debut, annee_incompatible))

    # Création des références Git
    if date_fin_texte:
        subprocess.call(['git', 'update-ref', git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/abrogé', 'refs/heads/' + branche + '-abrogé'], cwd=dossier)
    elif not futur_debut:
        subprocess.call(['git', 'update-ref', git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur', 'refs/heads/' + branche], cwd=dossier)
    if futur and not date_fin_texte:
        subprocess.call(['git', 'update-ref', git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur-future', 'refs/heads/'+branche+'-futur'], cwd=dossier)

    # Ajout d’une référence contenant un fichier de métadonnées
    if re.search( '^([0-9a-f]{40}) refs/meta$', git_refs, flags=re.MULTILINE ):
        subprocess.call(['git', 'checkout', '-b', 'meta', 'refs/meta'], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--orphan', 'meta'], cwd=dossier)
        subprocess.call(['git', 'rm', '--cached', '-rf', '.'], cwd=dossier, stdout=subprocess.DEVNULL)
        subprocess.call(['git', 'clean', '-f', '-x', '-d'], cwd=dossier, stdout=subprocess.DEVNULL)
    branche_prec = branche_courante
    branche_courante = 'meta'
    git_refs = str( subprocess.check_output(['git', 'show-ref'], cwd=dossier), 'utf-8' ).strip()
    date_vigueur_actuelle = None
    nb_versions_vigueur_actuelle = 0
    if re.search( '^([0-9a-f]{40}) refs/heads/' + branche + '$', git_refs, flags=re.MULTILINE ):
        date_vigueur_actuelle = str( subprocess.check_output(['git', 'show', '-s', '--pretty=format:%s', branche], cwd=dossier), 'utf-8' ).strip()
        nb_versions_vigueur_actuelle = len(str( subprocess.check_output(['git', 'log', '--oneline', branche], cwd=dossier), 'utf-8' ).strip().splitlines())
        date_vigueur_actuelle = re.match('^Version consolidée au ([0-9]+)(?:er)? ([a-zéû]+) ([0-9]+)$', date_vigueur_actuelle)
        if date_vigueur_actuelle:
            date_vigueur_actuelle = date_vigueur_actuelle.group(3) + '-' + MOIS[date_vigueur_actuelle.group(2)] + '-' + ('0' if len(date_vigueur_actuelle.group(1))==1 else '') + date_vigueur_actuelle.group(1)
    date_vigueur_future = None
    nb_versions_vigueur_future = 0
    if re.search( '^([0-9a-f]{40}) refs/heads/' + branche + '-futur$', git_refs, flags=re.MULTILINE ):
        date_vigueur_future = str( subprocess.check_output(['git', 'show', '-s', '--pretty=format:%s', branche + '-futur'], cwd=dossier), 'utf-8' ).strip()
        nb_versions_vigueur_future = len(str( subprocess.check_output(['git', 'log', '--oneline', branche + '-futur'], cwd=dossier), 'utf-8' ).strip().splitlines())
        date_vigueur_future = re.match('^Version consolidée au ([0-9]+)(?:er)? ([a-zéû]+) ([0-9]+)$', date_vigueur_future)
        if date_vigueur_future:
            date_vigueur_future = date_vigueur_future.group(3) + '-' + MOIS[date_vigueur_future.group(2)] + '-' + ('0' if len(date_vigueur_future.group(1))==1 else '') + date_vigueur_future.group(1)
    titre_long = re.sub(r' *\(\d+\) *$', '', titrefull)
    titre_long = titre_long[0].lower() + titre_long[1:]
    meta = {
        'titre': identifiant.replace('_',' ').replace('\\', '\\\\').replace('"', '\\"'),
        'titre_long': titre_long.replace('_',' ').replace('\\', '\\\\').replace('"', '\\"'),
        'nature': nature_min,
        'état': 'vigueur' if not date_fin_texte else ( 'abrogé' if etat_texte != 'MODIFIE' else 'modifié' ),
        'id': texte_id,
        'cid': cid,
        'éditorialisation_nb-versions': len(set(re.findall( '^(?:[0-9a-f]{40}) ' + git_ref_base + '([0-9]{8}-[0-9]{6})/vigueur(?:-future)?$', git_refs, flags=re.MULTILINE ))),
        'éditorialisation_dernière-date': last_update.strftime('%Y-%m-%d'),
        'vigueur_promulgation': "'" + date_promulgation_texte + "'" if date_promulgation_texte else 'null',
        'vigueur_début': "'" + date_debut_texte + "'" if date_debut_texte else 'null',
        'vigueur_fin': "'" + date_fin_texte + "'" if date_fin_texte else 'null',
        'vigueur_actuelle': "'" + date_vigueur_actuelle + "'" if date_vigueur_actuelle else 'null',
        'vigueur_future': "'" + date_vigueur_future + "'" if date_vigueur_future else 'null',
        'statistiques_nb-versions-vigueur-actuelle': nb_versions_vigueur_actuelle,
        'statistiques_nb-versions-vigueur-future': max( 0, nb_versions_vigueur_future - nb_versions_vigueur_actuelle ),
    }
    metatxt = """titre: "%s"
titre-long: "%s"
nature: '%s'
état: '%s'
id: '%s'
cid: '%s'
éditorialisation:
  nb-versions: %d
  dernière-date: '%s'
vigueur:
  promulgation: %s
  début: %s
  fin: %s
  actuelle: %s
  future: %s
statistiques:
  nb-versions-vigueur-actuelle: %d
  nb-versions-vigueur-future: %d
""" % ( meta['titre'], meta['titre_long'], meta['nature'], meta['état'], meta['id'], meta['cid'], meta['éditorialisation_nb-versions'], meta['éditorialisation_dernière-date'], meta['vigueur_promulgation'], meta['vigueur_début'], meta['vigueur_fin'], meta['vigueur_actuelle'], meta['vigueur_future'], meta['statistiques_nb-versions-vigueur-actuelle'], meta['statistiques_nb-versions-vigueur-future'] )
    with open( os.path.join(dossier, 'meta.yaml'), 'w' ) as f:
        f.write(metatxt)
    with open( os.path.join(dossier, '.git', 'meta.yaml'), 'w' ) as f:
        f.write(metatxt)
    subprocess.call(['git', 'add', 'meta.yaml'], cwd=dossier)
    subprocess.call(['git', 'commit', '--allow-empty-message', '--author="Archéo Lex <>"', '--date="' + last_update.isoformat() + '"', '-m', '', '-q', '--no-status'], cwd=dossier, env={ 'GIT_COMMITTER_DATE': last_update.isoformat(), 'GIT_COMMITTER_NAME': 'Archéo Lex', 'GIT_COMMITTER_EMAIL': '' })
    subprocess.call(['git', 'update-ref', 'refs/meta', 'refs/heads/meta'], cwd=dossier)

    # Positionnement des fichiers sur, dans l’ordre selon ce qui est disponible : texte, texte-futur, <organisation>, <organisation>-futur
    if branche_courante != 'texte' and re.search( '^([0-9a-f]{40}) refs/texte/' + format['dialecte'] + '/([0-9]{8}-[0-9]{6})/vigueur$', git_refs, flags=re.MULTILINE ):
        subprocess.call(['git', 'checkout', 'texte'], cwd=dossier)
    elif branche_courante != 'texte' and re.search( '^([0-9a-f]{40}) refs/texte/' + format['dialecte'] + '/([0-9]{8}-[0-9]{6})/vigueur-future$', git_refs, flags=re.MULTILINE ):
        subprocess.call(['git', 'checkout', 'texte-futur'], cwd=dossier)
    elif futur and not futur_debut:
        subprocess.call(['git', 'checkout', branche], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', branche_prec], cwd=dossier)
    subprocess.call(['git', 'branch', '-D', 'meta'], cwd=dossier)

    # Optimisation du dossier git
    subprocess.call(['git', 'gc'], cwd=dossier)
    subprocess.call(['git', 'prune', '--expire=all'], cwd=dossier)
    rmrf({'COMMIT_EDITMSG', 'branches', 'logs', 'hooks', os.path.join('refs', 'heads'), os.path.join('refs', 'tags'), os.path.join('refs', 'texte'), os.path.join('refs', 'sections'), os.path.join('refs', 'articles')}, os.path.join(dossier, '.git'))
    no_more_executable(os.path.join(dossier, '.git', 'config'))

    if erreurs['versions_manquantes']:
        logger.info( 'Erreurs détectées avec des versions vides ou identiques aux précédentes : erreurs dans la base LEGI (en général) ou dans Archéo Lex, voir le fichier doc/limitations.md.' )
    if erreurs['date_pre_1970']:
        if format['dates-git-pre-1970']:
            logger.info( 'Il est apparu des dates antérieures à 1970. Le stockage Git a été forcé à prendre cette valeur mais cela pourrait entraîner des incompatibilités avec certains logiciels ou plate-formes Git. Voir le fichier doc/limitations.md.')
        else:
            logger.info( 'Il est apparu des dates antérieures à 1970 qui ont été inscrites en 1970-01-01T12:00:00+0100 pour rester pleinement compatible avec tous les logiciels et plate-formes Git, même si cela est erroné. Voir le fichier doc/limitations.md.' )
    if erreurs['date_post_2100']:
        if format['dates-git-post-2100']:
            logger.info( 'Il est apparu des dates postérieures à 2100 (probablement le 22 février 2222 = date future indéterminée). Le stockage Git a été forcé à prendre cette valeur mais cela pourrait entraîner des incompatibilités avec certains logiciels ou plate-formes Git. Voir le fichier doc/limitations.md.')
        else:
            logger.info( 'Il est apparu des dates postérieures à 2100 (probablement le 22 février 2222 = date future indéterminée) qui ont été inscrites en 2099-01-01T12:00:00+0100 pour rester pleinement compatible avec tous les logiciels et plate-formes Git, même si cela est erroné. Voir le fichier doc/limitations.md.' )

    if fs.fabrique_article.erreurs:
        logger.info( 'Erreurs - voir le fichier doc/limitations.md :' )
        for erreur in fs.fabrique_article.erreurs:
            logger.info( '* ' + erreur )

    return dossier_final, nom_final, cid
Exemplo n.º 7
0
def creer_historique_legi(textes, format, dossier, bdd, production):

    if os.path.exists(textes):
        f_textes = open(textes, 'r')
        textes = f_textes.read()
        f_textes.close()

    if not os.path.exists(bdd):
        raise Exception('Base de données legi.py manquante.')

    textes = textes.strip()
    textes = re.split(r'[\n,]+', textes)

    liste_textes = []
    natures = []
    db = legi.utils.connect_db(bdd)
    if 'tout' in textes:
        liste_textes = db.all("""
              SELECT cid
              FROM textes_versions
              ORDER BY cid
        """)
        liste_textes = [x[0] for x in liste_textes]
        logger.info('\nListe de textes : tous\n')
    elif 'tout-obsolete' in textes:
        last_update = db.one("""
            SELECT value
            FROM db_meta
            WHERE key = 'last_update'
        """)
        liste_textes = db.all("""
              SELECT cid
              FROM textes_versions
              WHERE mtime > {0}
              ORDER BY cid
        """.format(last_update))
        liste_textes = [x[0] for x in liste_textes]
    else:
        for texte in textes:
            if re.match(r'^(JORF|LEGI)TEXT[0-9]{12}$', texte):
                liste_textes.append(texte)
            elif re.match(r'^aleatoire-([0-9]+)$', texte):
                m = re.match(r'^aleatoire-([0-9]+)$', texte)
                m = int(m.group(1))
                liste = db.all("""
                      SELECT cid
                      FROM textes_versions
                      ORDER BY RANDOM()
                      LIMIT {0}
                """.format(m))
                liste = [x[0] for x in liste]
                liste_textes.extend(liste)
            else:
                if len(natures) == 0:
                    liste = db.all("""
                          SELECT DISTINCT nature
                          FROM textes_versions
                    """)
                    natures = [x[0] for x in liste]
                if texte.upper() in natures:
                    liste = db.all("""
                          SELECT cid
                          FROM textes_versions
                          WHERE nature = '{0}'
                    """.format(texte.upper()))
                    liste = [x[0] for x in liste]
                    liste_textes.extend(liste)
                else:
                    raise Exception(
                        'Mauvaise spécification de la liste de textes')

    liste_textes.sort()
    if len(liste_textes) < 100:
        logger.info('\nListe de textes :\n' + '\n'.join(liste_textes) + '\n')

    args = [(texte, format, dossier, bdd) for texte in liste_textes]
    if len(liste_textes) == 1 or os.cpu_count() == 1 or not production:
        textes_traites = list(map(creer_historique_texte, args))
    else:
        nb_procs = min(len(liste_textes), os.cpu_count())
        textes_traites = list(Pool(nb_procs).map(creer_historique_texte, args))

    return textes_traites
Exemplo n.º 8
0
def creer_historique_texte(arg):

    texte, format, dossier, bdd = arg

    logger.info('> Texte {0}'.format(texte))

    # Constantes
    paris = timezone('Europe/Paris')
    annee2100 = paris.localize(datetime.datetime(2100, 1, 1))
    annee1970 = paris.localize(datetime.datetime(1970, 1, 1))

    # Connexion à la base de données
    db = legi.utils.connect_db(bdd)

    # Créer le dossier si besoin
    sousdossier = '.'
    id = texte
    nom = texte

    # Flags: 1) s’il y a des versions en vigueur future, 2) si la première version est une vigueur future
    futur = False
    futur_debut = False

    # Obtenir la date de la base LEGI
    last_update = db.one("""
        SELECT value
        FROM db_meta
        WHERE key = 'last_update'
    """)
    last_update_jour = datetime.date(
        *(time.strptime(last_update, '%Y%m%d-%H%M%S')[0:3]))
    last_update = paris.localize(
        datetime.datetime(*(time.strptime(last_update, '%Y%m%d-%H%M%S')[0:6])))
    date_base_legi_fr = date_en_francais(last_update)
    logger.info('Dernière mise à jour de la base LEGI : {}'.format(
        last_update.isoformat()))

    os.makedirs(dossier, exist_ok=True)
    entree_texte = db.one("""
        SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, visas, signataires, tp, nota, abro, rect, cid, mtime, date_texte
        FROM textes_versions
        WHERE id = '{0}'
    """.format(id))
    if entree_texte == None:
        entree_texte = db.one("""
            SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, visas, signataires, tp, nota, abro, rect, cid, mtime, date_texte
            FROM textes_versions
            WHERE cid = '{0}'
        """.format(id))
    if entree_texte == None:
        raise Exception('Pas de texte avec cet ID ou CID')

    texte_id = entree_texte[0]
    nature = entree_texte[1]
    titre = entree_texte[2]
    titrefull = entree_texte[3]
    etat_texte = entree_texte[4]
    date_debut_texte = entree_texte[
        5] if entree_texte[5] and entree_texte[5] != '2999-01-01' else None
    date_fin_texte = entree_texte[
        6] if entree_texte[6] and entree_texte[6] != '2999-01-01' else None
    visas = entree_texte[8] or ''
    signataires = entree_texte[9] or ''
    tp = entree_texte[10] or ''
    nota = entree_texte[11] or ''
    abro = entree_texte[12] or ''
    rect = entree_texte[13] or ''
    cid = entree_texte[14]
    mtime = entree_texte[15]
    date_promulgation_texte = entree_texte[
        16] if entree_texte[16] and entree_texte[16] != '2999-01-01' else None

    visas = visas.strip()
    signataires = signataires.strip()
    tp = tp.strip()
    nota = nota.strip()
    abro = abro.strip()
    rect = rect.strip()

    nature_min = nature.lower()
    nature_min_pluriel = re.sub(r'([- ])', r's\1', nature_min) + 's'
    if nature in natures.keys():
        nature_min = natures[nature]
        nature_min_pluriel = re.sub(r'([- ])', r's\1', nature_min) + 's'
        os.makedirs(os.path.join(dossier, nature_min_pluriel), exist_ok=True)
        sousdossier = nature_min_pluriel

    mise_a_jour = True
    if nature and (nature in natures.keys()) and entree_texte[7]:
        identifiant = nature_min + ' ' + entree_texte[7]
        identifiant = identifiant.replace(' ', '_')
        nom_fichier = identifiant
        sousdossier = os.path.join(nature_min_pluriel, identifiant)
        if not os.path.exists(os.path.join(dossier, sousdossier)):
            mise_a_jour = False
        os.makedirs(os.path.join(dossier, sousdossier), exist_ok=True)
    elif nature and (nature in natures.keys()) and titre:
        identifiant = titrefull[0].lower() + titrefull[1:].replace(' ', '_')
        nom_fichier = identifiant
        sousdossier = os.path.join(nature_min_pluriel, identifiant)
        if not os.path.exists(os.path.join(dossier, sousdossier)):
            mise_a_jour = False
        os.makedirs(os.path.join(dossier, sousdossier), exist_ok=True)
    else:
        raise Exception('Type bizarre ou inexistant')
        sousdossier = os.path.join(sousdossier, nom)
        nom_fichier = id
    dossier = os.path.join(dossier, sousdossier)
    dossier_final = sousdossier
    nom_final = identifiant
    sousdossier = '.'
    os.makedirs(dossier, exist_ok=True)
    fichier = os.path.join(dossier, nom_fichier + '.md')
    logger.info('Dossier : {}'.format(dossier))

    # Créer le dépôt Git avec comme branche maîtresse 'texte' ou 'articles'
    branche = None
    git_ref_base = None
    git_ref = git_ref_base
    if format['organisation'] in ['texte', 'articles', 'sections']:
        branche = format['organisation']
        git_ref_base = 'refs/' + format['organisation'] + '/' + format[
            'dialecte'] + '/'
        git_ref = git_ref_base
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.call(['git', 'init'], cwd=dossier)
        subprocess.call(
            ['git', 'symbolic-ref', 'HEAD', 'refs/heads/' + branche],
            cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--', sousdossier], cwd=dossier)

    # Vérification si la référence nécessaire existe ; dans le cas contraire, ce n’est pas une mise à jour
    git_refs = ''
    if mise_a_jour:
        git_refs = str(
            subprocess.check_output(['git', 'show-ref'], cwd=dossier),
            'utf-8').strip()
        git_refs_base = re.search('^([0-9a-f]{40}) ' + git_ref_base +
                                  '([0-9]{8}-[0-9]{6})/vigueur(?:-future)?$',
                                  git_refs,
                                  flags=re.MULTILINE)
        mise_a_jour = (git_refs_base != None)
        if not mise_a_jour:
            subprocess.call(['git', 'checkout', '--orphan', branche],
                            cwd=dossier)
            subprocess.call(['git', 'rm', '--cached', '-rf', '.'],
                            cwd=dossier,
                            stdout=subprocess.DEVNULL)
            subprocess.call(['git', 'clean', '-f', '-x', '-d'],
                            cwd=dossier,
                            stdout=subprocess.DEVNULL)

    date_reprise_git = None
    reset_hash = ''
    if mise_a_jour:
        date_maj_git = False
        git_refs_base = re.findall(
            '^([0-9a-f]{40}) ' + git_ref_base +
            '([0-9]{8}-[0-9]{6})/(vigueur(?:-future)?)$',
            git_refs,
            flags=re.MULTILINE)
        git_refs_base = sorted(git_refs_base, key=lambda x: x[1] + x[2])
        if git_refs_base:
            date_maj_git = paris.localize(
                datetime.datetime(
                    *(time.strptime(git_refs_base[-1][1], '%Y%m%d-%H%M%S')[0:6]
                      )))
        else:
            raise Exception('Pas de tag de la dernière mise à jour')
        logger.info('Dernière mise à jour du dépôt : {}'.format(
            date_maj_git.isoformat()))

        # Obtention de la première date qu’il faudra mettre à jour
        r1 = db.one("""
            SELECT date_debut
            FROM articles
            WHERE cid = '{0}' AND mtime > {1} + 10
            ORDER BY date_debut
        """.format(cid, int(time.mktime(date_maj_git.timetuple()))))
        r2 = db.one("""
            SELECT sommaires.debut
            FROM sommaires
            INNER JOIN sections
               ON sommaires.element = sections.id
            WHERE sommaires.cid = '{0}' AND sections.mtime > {1} + 10
            ORDER BY sommaires.debut
        """.format(cid, int(time.mktime(date_maj_git.timetuple()))))
        if r1:
            date_reprise_git = r1
        if r2:
            date_reprise_git = min(r1, r2) if r1 else r2

        # Noter que le mtime des fichiers retarde de quelques secondes par rapport à la date de référence de la base LEGI
        if int(time.mktime(date_maj_git.timetuple())
               ) >= mtime - 10 or not date_reprise_git:
            logger.info('Dossier : {0}'.format(dossier))
            logger.info('Pas de mise à jour disponible')
            if git_refs_base[-1][1] == last_update.strftime('%Y%m%d-%H%M%S'):
                return

            logger.info('Ajout de la référence à la base LEGI du jour')
            if git_refs_base[-1][2] == 'vigueur':
                subprocess.call([
                    'git', 'update-ref', git_ref_base +
                    last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur',
                    git_refs_base[-1][0]
                ],
                                cwd=dossier)
            elif git_refs_base[-1][2] == 'vigueur-future':
                subprocess.call([
                    'git', 'update-ref', git_ref_base +
                    last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur-future',
                    git_refs_base[-1][0]
                ],
                                cwd=dossier)
                if len(git_refs_base
                       ) > 1 and git_refs_base[-2][1] == git_refs_base[-1][
                           1] and git_refs_base[-2][2] == 'vigueur':
                    subprocess.call([
                        'git', 'update-ref', git_ref_base +
                        last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur',
                        git_refs_base[-2][0]
                    ],
                                    cwd=dossier)
            nettoyer_refs_intermediaires(dossier)
            return

        # Lecture des versions en vigueur dans le dépôt Git
        try:
            if subprocess.check_output(
                ['git', 'rev-parse', '--verify', branche + '-futur'],
                    cwd=dossier,
                    stderr=subprocess.DEVNULL):
                subprocess.call(['git', 'checkout', branche + '-futur'],
                                cwd=dossier)
        except subprocess.CalledProcessError:
            pass
        versions_git = str(
            subprocess.check_output(['git', 'log', '--oneline'], cwd=dossier),
            'utf-8').strip().split('\n')
        for log_version in versions_git:
            for m, k in MOIS.items():
                log_version = log_version.replace(m, k)
            m = re.match(r'^([0-9a-f]+) .* ([0-9]+)(?:er)? ([0-9]+) ([0-9]+)$',
                         log_version)
            if not m:
                raise Exception('Version non reconnue dans le dépôt Git')
            date = '{0:04d}-{1:02d}-{2:02d}'.format(int(m.group(4)),
                                                    int(m.group(3)),
                                                    int(m.group(2)))
            reset_hash = m.group(1)
            if date < date_reprise_git:
                break
            reset_hash = ''

        if reset_hash:
            if date_reprise_git <= last_update_jour.strftime('%Y-%m-%d'):
                subprocess.call(['git', 'checkout', branche], cwd=dossier)
            subprocess.call(['git', 'reset', '--hard', reset_hash],
                            cwd=dossier)
        else:
            subprocess.call(['git', 'branch', '-m', branche, 'junk'],
                            cwd=dossier)
            subprocess.call(['git', 'checkout', '--orphan', branche],
                            cwd=dossier)
            subprocess.call(['git', 'branch', '-D', 'junk'], cwd=dossier)
            subprocess.call(['git', 'rm', '--cached', '-rf', '.'],
                            cwd=dossier,
                            stdout=subprocess.DEVNULL)
            subprocess.call(['git', 'clean', '-f', '-x', '-d'],
                            cwd=dossier,
                            stdout=subprocess.DEVNULL)
        try:
            if subprocess.check_output(
                ['git', 'rev-parse', '--verify', branche + '-futur'],
                    cwd=dossier,
                    stderr=subprocess.DEVNULL):
                subprocess.call(['git', 'branch', '-D', branche + '-futur'],
                                cwd=dossier)
        except subprocess.CalledProcessError:
            pass

    # Sélection des versions du texte
    versions_texte_db = db.all("""
          SELECT DISTINCT debut, fin
          FROM sommaires
          WHERE cid = '{0}'
            AND debut < fin
          ORDER BY debut
    """.format(cid))
    dates_texte = []
    dates_fin_texte = []
    versions_texte = []
    for vers in versions_texte_db:
        vt = vers[0]
        if isinstance(vt, str):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        if not date_reprise_git or vt.strftime('%Y-%m-%d') >= date_reprise_git:
            dates_texte.append(vt)
        vt = vers[1]
        if isinstance(vt, str):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        if not date_reprise_git or vt.strftime('%Y-%m-%d') >= date_reprise_git:
            dates_fin_texte.append(vt)
    versions_texte = sorted(set(dates_texte).union(set(dates_fin_texte)))

    versions_texte = sorted(list(set(versions_texte)))

    if not versions_texte:
        versions_texte_db = db.all("""
              SELECT DISTINCT debut, fin
              FROM sommaires
              WHERE cid = '{0}'
        """.format(cid))
        if len(list(versions_texte_db)):
            if isinstance(date_debut_texte, str):
                versions_texte.append(
                    datetime.date(
                        *(time.strptime(date_debut_texte, '%Y-%m-%d')[0:3])))
            if isinstance(date_fin_texte, str):
                versions_texte.append(
                    datetime.date(
                        *(time.strptime(date_fin_texte, '%Y-%m-%d')[0:3])))
            else:
                versions_texte.append(
                    datetime.date(
                        *(time.strptime('2999-01-01', '%Y-%m-%d')[0:3])))
        else:
            return

    syntaxe = Markdown()
    if format['organisation'] == 'articles':
        un_article_par_fichier_sans_hierarchie = UnArticleParFichierSansHierarchie(
            'md')
        stockage = StockageGitFichiers(dossier,
                                       un_article_par_fichier_sans_hierarchie)
    elif format['organisation'] == 'sections':
        un_article_par_fichier_avec_hierarchie = UnArticleParFichierAvecHierarchie(
            'md')
        stockage = StockageGitFichiers(dossier,
                                       un_article_par_fichier_avec_hierarchie)
    else:
        fichier_unique = FichierUnique('md')
        stockage = StockageGitFichiers(dossier, fichier_unique)
    fa = FabriqueArticle(db, stockage, syntaxe, True)
    fs = FabriqueSection(fa)

    # Conversion en syntaxe des en-têtes et pied-de-texte
    if visas:
        visas_titre = fs.syntaxe.obtenir_titre([(0, 'VISAS', 'Visas')],
                                               'Visas')
        visas = fs.syntaxe.transformer_depuis_html(visas) + '\n\n'
    if signataires:
        signataires_titre = fs.syntaxe.obtenir_titre(
            [(0, 'SIGNATAIRES', 'Signataires')], 'Signataires')
        signataires = fs.syntaxe.transformer_depuis_html(signataires)
    if tp:
        tp_titre = fs.syntaxe.obtenir_titre(
            [(0, 'TP', 'Travaux préparatoires')], 'Travaux préparatoires')
        tp = fs.syntaxe.transformer_depuis_html(tp)
    if nota:
        nota_titre = fs.syntaxe.obtenir_titre([(0, 'NOTA', 'Notas')], 'Notas')
        nota = fs.syntaxe.transformer_depuis_html(nota)

    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    wnbver = str(len(str(len(versions_texte))))
    erreurs = {
        'versions_manquantes': False,
        'date_pre_1970': False,
        'date_post_2100': False,
    }
    branche_courante = branche
    for (i_version, version_texte) in enumerate(versions_texte):

        # Passer les versions 'nulles'
        #if version_texte.base is None:
        #    continue
        if i_version >= len(versions_texte) - 1:
            break

        debut = versions_texte[i_version]
        fin = versions_texte[i_version + 1]
        debut_datetime = paris.localize(
            datetime.datetime(debut.year, debut.month, debut.day))

        if not futur and debut > last_update_jour:
            if i_version == 0:
                subprocess.call([
                    'git', 'symbolic-ref', 'HEAD',
                    'refs/heads/' + branche + '-futur'
                ],
                                cwd=dossier)
                if not reset_hash:
                    futur_debut = True
            else:
                subprocess.call(['git', 'checkout', '-b', branche + '-futur'],
                                cwd=dossier)
            futur = True
            branche_courante = branche + '-futur'

        # Retrait des fichiers des anciennes versions
        if format['organisation'] != 'texte':
            rmrf(set(os.listdir(dossier)) - {'.git'}, dossier)

        # Créer les sections (donc tout le texte)
        contenu, fin_vigueur = fs.obtenir_texte_section(
            None, [], cid, debut, fin)

        if not contenu.strip():
            if str(fin) == '2999-01-01':
                logger.info((
                    'Version {:' + wnbver +
                    '} (du {} à  maintenant) non-enregistrée car vide').format(
                        i_version + 1, debut))
            else:
                logger.info(
                    ('Version {:' + wnbver +
                     '} (du {} au {}) non-enregistrée car vide').format(
                         i_version + 1, debut, fin))
            erreurs['versions_manquantes'] = True
            continue

        # Ajout des en-têtes et pied-de-texte
        date_debut_fr = date_en_francais(debut)
        if visas:
            contenu = visas_titre + visas + contenu
            fs.stockage.ecrire_ressource('VISAS', [(0, 'VISAS', 'Visas')], '',
                                         'Visas', visas)
        if signataires:
            contenu += signataires_titre + signataires
            fs.stockage.ecrire_ressource('SIGNATAIRES',
                                         [(0, 'SIGNATAIRES', 'Signataires')],
                                         '', 'Signataires', signataires)
        if tp:
            contenu += tp_titre + tp
            fs.stockage.ecrire_ressource('TP',
                                         [(0, 'TP', 'Travaux préparatoires')],
                                         '', 'Travaux préparatoires', tp)
        if nota:
            contenu += nota_titre + nota
            fs.stockage.ecrire_ressource('NOTA', [(0, 'NOTA', 'Nota')], '',
                                         'Nota', nota)

        # Enregistrement du fichier global
        fs.stockage.ecrire_ressource(cid, [], '', nom_fichier, contenu)

        if not subprocess.check_output(['git', 'status', '--ignored', '-s'],
                                       cwd=dossier):
            if str(fin) == '2999-01-01':
                logger.info((
                    'Version {:' + wnbver +
                    '} (du {} à  maintenant) non-enregistrée car identique à la précédente'
                ).format(i_version + 1, debut))
            else:
                logger.info((
                    'Version {:' + wnbver +
                    '} (du {} au {}) non-enregistrée car identique à la précédente'
                ).format(i_version + 1, debut, fin))
            erreurs['versions_manquantes'] = True
            continue

        annee_incompatible = ''
        git_debut_datetime = debut_datetime
        if debut_datetime <= annee1970:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_pre_1970'] = True
            git_debut_datetime = paris.localize(
                datetime.datetime(1970, 1, 1, 12))
        if debut_datetime >= annee2100:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_post_2100'] = True
            git_debut_datetime = paris.localize(
                datetime.datetime(2099, 1, 1, 12))

        # Enregistrer les fichiers dans Git
        subprocess.call(
            [
                'git', 'commit', '--author="Législateur <>"',
                '--date="' + str(git_debut_datetime) + '"', '-m',
                'Version consolidée au {}'.format(date_debut_fr), '-q',
                '--no-status'
            ],
            cwd=dossier,
            env={
                'GIT_COMMITTER_DATE': str(git_debut_datetime),
                'GIT_COMMITTER_NAME': 'Législateur',
                'GIT_COMMITTER_EMAIL': ''
            })

        if (format['dates-git-pre-1970'] and debut_datetime <= annee1970) or (
                format['dates-git-post-2100'] and debut_datetime >= annee2100):
            annee_incompatible = ''
            commit = str(
                subprocess.check_output(['git', 'cat-file', '-p', 'HEAD'],
                                        cwd=dossier), 'utf-8')
            commit = re.sub(
                r'author Législateur <> (-?\d+ [+-]\d{4})',
                'author Législateur <> ' +
                str(int(debut_datetime.timestamp())) +
                debut_datetime.strftime(' %z'), commit)
            commit = re.sub(
                r'committer Législateur <> (-?\d+ [+-]\d{4})',
                'committer Législateur <> ' +
                str(int(debut_datetime.timestamp())) +
                debut_datetime.strftime(' %z'), commit)
            sha1 = str(
                subprocess.check_output(
                    ['git', 'hash-object', '-t', 'commit', '-w', '--stdin'],
                    cwd=dossier,
                    input=bytes(commit, 'utf-8')), 'utf-8')
            sha1 = re.sub('[^a-f0-9]', '', sha1)
            subprocess.call(
                ['git', 'update-ref', 'refs/heads/' + branche_courante, sha1],
                cwd=dossier)

        if str(fin) == '2999-01-01':
            logger.info(('Version {:' + wnbver +
                         '} (du {} à  maintenant) enregistrée{}').format(
                             i_version + 1, debut, annee_incompatible))
        else:
            logger.info(('Version {:' + wnbver +
                         '} (du {} au {}) enregistrée{}').format(
                             i_version + 1, debut, fin, annee_incompatible))

    # Texte abrogé
    if date_fin_texte:
        subprocess.call(['git', 'rm', '*'],
                        cwd=dossier,
                        stdout=subprocess.DEVNULL)
        annee_incompatible = ''
        debut = datetime.date(
            *(time.strptime(date_fin_texte, '%Y-%m-%d')[0:3]))
        date_debut_fr = date_en_francais(debut)
        debut_datetime = paris.localize(
            datetime.datetime(debut.year, debut.month, debut.day))
        git_debut_datetime = debut_datetime
        if debut_datetime <= annee1970:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_pre_1970'] = True
            git_debut_datetime = paris.localize(
                datetime.datetime(1970, 1, 1, 12))
        if debut_datetime >= annee2100:
            annee_incompatible = ' avec une date Git erronée mais compatible'
            erreurs['date_post_2100'] = True
            git_debut_datetime = paris.localize(
                datetime.datetime(2099, 1, 1, 12))
        subprocess.call(
            [
                'git', 'commit', '--author="Législateur <>"',
                '--date="' + str(git_debut_datetime) + '"', '-m',
                'Texte abrogé au {}'.format(date_debut_fr), '-q', '--no-status'
            ],
            cwd=dossier,
            env={
                'GIT_COMMITTER_DATE': str(git_debut_datetime),
                'GIT_COMMITTER_NAME': 'Législateur',
                'GIT_COMMITTER_EMAIL': ''
            })
        if (format['dates-git-pre-1970'] and debut_datetime <= annee1970) or (
                format['dates-git-post-2100'] and debut_datetime >= annee2100):
            annee_incompatible = ''
            commit = str(
                subprocess.check_output(['git', 'cat-file', '-p', 'HEAD'],
                                        cwd=dossier), 'utf-8')
            commit = re.sub(
                r'author Législateur <> (-?\d+ [+-]\d{4})',
                'author Législateur <> ' +
                str(int(debut_datetime.timestamp())) +
                debut_datetime.strftime(' %z'), commit)
            commit = re.sub(
                r'committer Législateur <> (-?\d+ [+-]\d{4})',
                'committer Législateur <> ' +
                str(int(debut_datetime.timestamp())) +
                debut_datetime.strftime(' %z'), commit)
            sha1 = str(
                subprocess.check_output(
                    ['git', 'hash-object', '-t', 'commit', '-w', '--stdin'],
                    cwd=dossier,
                    input=bytes(commit, 'utf-8')), 'utf-8')
            sha1 = re.sub('[^a-f0-9]', '', sha1)
            subprocess.call(
                ['git', 'update-ref', 'refs/heads/' + branche_courante, sha1],
                cwd=dossier)
        subprocess.call(
            ['git', 'branch', '-m', branche_courante, branche + '-abrogé'],
            cwd=dossier)
        branche_courante = branche + '-abrogé'
        subprocess.call(['git', 'update-ref', '-d', 'refs/heads/' + branche],
                        cwd=dossier)
        subprocess.call(
            ['git', 'update-ref', '-d', 'refs/heads/' + branche + '-futur'],
            cwd=dossier)
        logger.info(('Version {:' + wnbver +
                     '} (   abrogation au {}) enregistrée{}').format(
                         i_version + 1, debut, annee_incompatible))

    # Création des références Git
    if date_fin_texte:
        subprocess.call([
            'git', 'update-ref',
            git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/abrogé',
            'refs/heads/' + branche + '-abrogé'
        ],
                        cwd=dossier)
    elif not futur_debut:
        subprocess.call([
            'git', 'update-ref',
            git_ref_base + last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur',
            'refs/heads/' + branche
        ],
                        cwd=dossier)
    if futur and not date_fin_texte:
        subprocess.call([
            'git', 'update-ref', git_ref_base +
            last_update.strftime('%Y%m%d-%H%M%S') + '/vigueur-future',
            'refs/heads/' + branche + '-futur'
        ],
                        cwd=dossier)

    # Ajout d’une référence contenant un fichier de métadonnées
    if re.search('^([0-9a-f]{40}) refs/meta$', git_refs, flags=re.MULTILINE):
        subprocess.call(['git', 'checkout', '-b', 'meta', 'refs/meta'],
                        cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--orphan', 'meta'], cwd=dossier)
        subprocess.call(['git', 'rm', '--cached', '-rf', '.'],
                        cwd=dossier,
                        stdout=subprocess.DEVNULL)
        subprocess.call(['git', 'clean', '-f', '-x', '-d'],
                        cwd=dossier,
                        stdout=subprocess.DEVNULL)
    branche_prec = branche_courante
    branche_courante = 'meta'
    git_refs = str(subprocess.check_output(['git', 'show-ref'], cwd=dossier),
                   'utf-8').strip()
    date_vigueur_actuelle = None
    nb_versions_vigueur_actuelle = 0
    if re.search('^([0-9a-f]{40}) refs/heads/' + branche + '$',
                 git_refs,
                 flags=re.MULTILINE):
        date_vigueur_actuelle = str(
            subprocess.check_output(
                ['git', 'show', '-s', '--pretty=format:%s', branche],
                cwd=dossier), 'utf-8').strip()
        nb_versions_vigueur_actuelle = len(
            str(
                subprocess.check_output(['git', 'log', '--oneline', branche],
                                        cwd=dossier),
                'utf-8').strip().splitlines())
        date_vigueur_actuelle = re.match(
            '^Version consolidée au ([0-9]+)(?:er)? ([a-zéû]+) ([0-9]+)$',
            date_vigueur_actuelle)
        if date_vigueur_actuelle:
            date_vigueur_actuelle = date_vigueur_actuelle.group(
                3) + '-' + MOIS[date_vigueur_actuelle.group(2)] + '-' + (
                    '0' if len(date_vigueur_actuelle.group(1)) == 1 else
                    '') + date_vigueur_actuelle.group(1)
    date_vigueur_future = None
    nb_versions_vigueur_future = 0
    if re.search('^([0-9a-f]{40}) refs/heads/' + branche + '-futur$',
                 git_refs,
                 flags=re.MULTILINE):
        date_vigueur_future = str(
            subprocess.check_output([
                'git', 'show', '-s', '--pretty=format:%s', branche + '-futur'
            ],
                                    cwd=dossier), 'utf-8').strip()
        nb_versions_vigueur_future = len(
            str(
                subprocess.check_output(
                    ['git', 'log', '--oneline', branche + '-futur'],
                    cwd=dossier), 'utf-8').strip().splitlines())
        date_vigueur_future = re.match(
            '^Version consolidée au ([0-9]+)(?:er)? ([a-zéû]+) ([0-9]+)$',
            date_vigueur_future)
        if date_vigueur_future:
            date_vigueur_future = date_vigueur_future.group(3) + '-' + MOIS[
                date_vigueur_future.group(2)] + '-' + (
                    '0' if len(date_vigueur_future.group(1)) == 1 else
                    '') + date_vigueur_future.group(1)
    titre_long = re.sub(r' *\(\d+\) *$', '', titrefull)
    titre_long = titre_long[0].lower() + titre_long[1:]
    meta = {
        'titre':
        identifiant.replace('_', ' ').replace('\\',
                                              '\\\\').replace('"', '\\"'),
        'titre_long':
        titre_long.replace('_', ' ').replace('\\', '\\\\').replace('"', '\\"'),
        'nature':
        nature_min,
        'état':
        'vigueur' if not date_fin_texte else
        ('abrogé' if etat_texte != 'MODIFIE' else 'modifié'),
        'id':
        texte_id,
        'cid':
        cid,
        'éditorialisation_nb-versions':
        len(
            set(
                re.findall('^(?:[0-9a-f]{40}) ' + git_ref_base +
                           '([0-9]{8}-[0-9]{6})/vigueur(?:-future)?$',
                           git_refs,
                           flags=re.MULTILINE))),
        'éditorialisation_dernière-date':
        last_update.strftime('%Y-%m-%d'),
        'vigueur_promulgation':
        "'" + date_promulgation_texte +
        "'" if date_promulgation_texte else 'null',
        'vigueur_début':
        "'" + date_debut_texte + "'" if date_debut_texte else 'null',
        'vigueur_fin':
        "'" + date_fin_texte + "'" if date_fin_texte else 'null',
        'vigueur_actuelle':
        "'" + date_vigueur_actuelle + "'" if date_vigueur_actuelle else 'null',
        'vigueur_future':
        "'" + date_vigueur_future + "'" if date_vigueur_future else 'null',
        'statistiques_nb-versions-vigueur-actuelle':
        nb_versions_vigueur_actuelle,
        'statistiques_nb-versions-vigueur-future':
        max(0, nb_versions_vigueur_future - nb_versions_vigueur_actuelle),
    }
    metatxt = """titre: "%s"
titre-long: "%s"
nature: '%s'
état: '%s'
id: '%s'
cid: '%s'
éditorialisation:
  nb-versions: %d
  dernière-date: '%s'
vigueur:
  promulgation: %s
  début: %s
  fin: %s
  actuelle: %s
  future: %s
statistiques:
  nb-versions-vigueur-actuelle: %d
  nb-versions-vigueur-future: %d
""" % (meta['titre'], meta['titre_long'], meta['nature'], meta['état'],
       meta['id'], meta['cid'], meta['éditorialisation_nb-versions'],
       meta['éditorialisation_dernière-date'], meta['vigueur_promulgation'],
       meta['vigueur_début'], meta['vigueur_fin'], meta['vigueur_actuelle'],
       meta['vigueur_future'],
       meta['statistiques_nb-versions-vigueur-actuelle'],
       meta['statistiques_nb-versions-vigueur-future'])
    with open(os.path.join(dossier, 'meta.yaml'), 'w') as f:
        f.write(metatxt)
    with open(os.path.join(dossier, '.git', 'meta.yaml'), 'w') as f:
        f.write(metatxt)
    subprocess.call(['git', 'add', 'meta.yaml'], cwd=dossier)
    subprocess.call(
        [
            'git', 'commit', '--allow-empty-message',
            '--author="Archéo Lex <>"', '--date="' + last_update.isoformat() +
            '"', '-m', '', '-q', '--no-status'
        ],
        cwd=dossier,
        env={
            'GIT_COMMITTER_DATE': last_update.isoformat(),
            'GIT_COMMITTER_NAME': 'Archéo Lex',
            'GIT_COMMITTER_EMAIL': ''
        })
    subprocess.call(['git', 'update-ref', 'refs/meta', 'refs/heads/meta'],
                    cwd=dossier)

    # Positionnement des fichiers sur, dans l’ordre selon ce qui est disponible : texte, texte-futur, <organisation>, <organisation>-futur
    if branche_courante != 'texte' and re.search(
            '^([0-9a-f]{40}) refs/texte/' + format['dialecte'] +
            '/([0-9]{8}-[0-9]{6})/vigueur$',
            git_refs,
            flags=re.MULTILINE):
        subprocess.call(['git', 'checkout', 'texte'], cwd=dossier)
    elif branche_courante != 'texte' and re.search(
            '^([0-9a-f]{40}) refs/texte/' + format['dialecte'] +
            '/([0-9]{8}-[0-9]{6})/vigueur-future$',
            git_refs,
            flags=re.MULTILINE):
        subprocess.call(['git', 'checkout', 'texte-futur'], cwd=dossier)
    elif futur and not futur_debut:
        subprocess.call(['git', 'checkout', branche], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', branche_prec], cwd=dossier)
    subprocess.call(['git', 'branch', '-D', 'meta'], cwd=dossier)

    # Optimisation du dossier git
    subprocess.call(['git', 'gc'], cwd=dossier)
    subprocess.call(['git', 'prune', '--expire=all'], cwd=dossier)
    rmrf(
        {
            'COMMIT_EDITMSG', 'branches', 'logs', 'hooks',
            os.path.join('refs', 'heads'),
            os.path.join('refs', 'tags'),
            os.path.join('refs', 'texte'),
            os.path.join('refs', 'sections'),
            os.path.join('refs', 'articles')
        }, os.path.join(dossier, '.git'))
    no_more_executable(os.path.join(dossier, '.git', 'config'))

    if erreurs['versions_manquantes']:
        logger.info(
            'Erreurs détectées avec des versions vides ou identiques aux précédentes : erreurs dans la base LEGI (en général) ou dans Archéo Lex, voir le fichier doc/limitations.md.'
        )
    if erreurs['date_pre_1970']:
        if format['dates-git-pre-1970']:
            logger.info(
                'Il est apparu des dates antérieures à 1970. Le stockage Git a été forcé à prendre cette valeur mais cela pourrait entraîner des incompatibilités avec certains logiciels ou plate-formes Git. Voir le fichier doc/limitations.md.'
            )
        else:
            logger.info(
                'Il est apparu des dates antérieures à 1970 qui ont été inscrites en 1970-01-01T12:00:00+0100 pour rester pleinement compatible avec tous les logiciels et plate-formes Git, même si cela est erroné. Voir le fichier doc/limitations.md.'
            )
    if erreurs['date_post_2100']:
        if format['dates-git-post-2100']:
            logger.info(
                'Il est apparu des dates postérieures à 2100 (probablement le 22 février 2222 = date future indéterminée). Le stockage Git a été forcé à prendre cette valeur mais cela pourrait entraîner des incompatibilités avec certains logiciels ou plate-formes Git. Voir le fichier doc/limitations.md.'
            )
        else:
            logger.info(
                'Il est apparu des dates postérieures à 2100 (probablement le 22 février 2222 = date future indéterminée) qui ont été inscrites en 2099-01-01T12:00:00+0100 pour rester pleinement compatible avec tous les logiciels et plate-formes Git, même si cela est erroné. Voir le fichier doc/limitations.md.'
            )

    if fs.fabrique_article.erreurs:
        logger.info('Erreurs - voir le fichier doc/limitations.md :')
        for erreur in fs.fabrique_article.erreurs:
            logger.info('* ' + erreur)

    return dossier_final, nom_final, cid
Exemplo n.º 9
0
def creer_historique_texte(texte, format, dossier, cache, bdd):

    # Connexion à la base de données
    db = legi.utils.connect_db(bdd)

    # Créer le dossier si besoin
    sousdossier = '.'
    id = texte
    nom = texte

    Path(dossier).mkdir_p()
    entree_texte = db.one("""
        SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, cid
        FROM textes_versions
        WHERE id = '{0}'
    """.format(id))
    cid = entree_texte[8]
    if entree_texte[1] in natures.keys():
        if not os.path.exists(
                os.path.join(dossier, natures[entree_texte[1]] + 's')):
            os.makedirs(os.path.join(dossier, natures[entree_texte[1]] + 's'))
        sousdossier = natures[entree_texte[1]] + 's'

    if entree_texte[1] and (entree_texte[1]
                            in natures.keys()) and entree_texte[7]:
        identifiant = natures[entree_texte[1]] + ' ' + entree_texte[7]
        identifiant = identifiant.replace(' ', '_')
        nom_fichier = identifiant
        sousdossier = os.path.join(natures[entree_texte[1]] + 's', identifiant)
        Path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(id, entree_texte[1] == 'CODE')
    elif entree_texte[1] and (entree_texte[1]
                              in natures.keys()) and entree_texte[2]:
        identifiant = entree_texte[2][0].lower() + entree_texte[2][1:].replace(
            ' ', '_')
        nom_fichier = identifiant
        sousdossier = os.path.join(natures[entree_texte[1]] + 's', identifiant)
        Path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(id, entree_texte[1] == 'CODE')
    else:
        raise Exception('Type bizarre ou inexistant')
        sousdossier = os.path.join(sousdossier, nom)
        nom_fichier = id
    dossier = os.path.join(dossier, sousdossier)
    sousdossier = '.'
    if not os.path.exists(dossier):
        os.makedirs(dossier)
    fichier = os.path.join(dossier, nom_fichier + '.md')

    # Créer le dépôt Git
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.call(['git', 'init'], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--', sousdossier], cwd=dossier)

    if os.path.exists(fichier):
        raise Exception(
            'Fichier existant : la mise à jour de fichiers existants n’est pas encore prise en charge.'
        )

    # Vérifier que les articles ont été transformés en Markdown ou les créer le cas échéant
    creer_markdown_texte((None, cid, None, None), db, cache)

    # Sélection des versions du texte
    versions_texte_db = db.all("""
          SELECT debut, fin
          FROM sommaires
          WHERE cid = '{0}'
    """.format(cid))
    dates_texte = []
    dates_fin_texte = []
    versions_texte = []
    for vers in versions_texte_db:
        vt = vers[0]
        if isinstance(vt, basestring):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        dates_texte.append(vt)
        vt = vers[1]
        if isinstance(vt, basestring):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        dates_fin_texte.append(vt)
    versions_texte = sorted(set(dates_texte).union(set(dates_fin_texte)))

    sql_texte = "cid = '{0}'".format(cid)
    versions_texte = sorted(list(set(versions_texte)))

    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    for (i_version, version_texte) in enumerate(versions_texte):

        # Passer les versions 'nulles'
        #if version_texte.base is None:
        #    continue
        if i_version >= len(versions_texte) - 1:
            break

        debut = versions_texte[i_version]
        fin = versions_texte[i_version + 1]

        sql = sql_texte + " AND debut <= '{0}' AND ( fin >= '{1}' OR fin == '2999-01-01' OR etat == 'VIGUEUR' )".format(
            debut, fin)

        # Créer l’en-tête
        date_fr = '{} {} {}'.format(debut.day, MOIS2[int(debut.month)],
                                    debut.year)
        if debut.day == 1:
            date_fr = '1er {} {}'.format(MOIS2[int(debut.month)], debut.year)
        contenu = nom + '\n'   \
                  + '\n'   \
                  + '- Date de consolidation : ' + date_fr + '\n'            \
                  + '- [Lien permanent Légifrance](https://www.legifrance.gouv.fr/affichCode.do?cidTexte=' + cid + '&dateTexte=' + debut.isoformat().replace('-','') + ')\n' \
                  + '\n' \
                  + '\n'

        # Enregistrement du fichier
        if format['organisation'] != 'fichier-unique':
            f_texte = open('README.md', 'w')
            f_texte.write(contenu.encode('utf-8'))
            f_texte.close()

            # Retrait des fichiers des anciennes versions
            subprocess.call('rm *.md', cwd=dossier, shell=True)

        # Créer les sections (donc tout le texte)
        contenu = creer_sections(contenu, 1, None, (debut, fin), sql, [],
                                 format, dossier, db, cache)

        # Enregistrement du fichier
        if format['organisation'] == 'fichier-unique':
            f_texte = open(fichier, 'w')
            f_texte.write(contenu.encode('utf-8'))
            f_texte.close()

        # Exécuter Git
        date_base_legi = '{} {} {} {}:{}:{}'.format('18', 'juillet', '2014',
                                                    '11', '30',
                                                    '10')  # TODO changer cela
        subprocess.call(['git', 'add', '.'], cwd=dossier)
        #subprocess.call(['git', 'commit', '--author="Législateur <>"', '--date="' + str(debut) + 'T00:00:00Z"', '-m', 'Version consolidée au {}\n\nVersions :\n- base LEGI : {}\n- programme Archéo Lex : {}'.format(date_fr, date_base_legi, version_archeolex), '-q', '--no-status'], cwd=dossier)
        subprocess.call([
            'git', 'commit', '--author="Législateur <>"',
            '--date="' + str(debut) + 'T00:00:00Z"', '-m',
            'Version consolidée au {}'.format(date_fr), '-q', '--no-status'
        ],
                        cwd=dossier)

        if fin == None or str(fin) == '2999-01-01':
            logger.info('Version {} enregistrée (du {} à maintenant)'.format(
                i_version + 1, debut))
        else:
            logger.info('Version {} enregistrée (du {} au {})'.format(
                i_version + 1, debut, fin))
Exemplo n.º 10
0
def creer_historique_texte(texte, format, dossier, cache, bdd):

    # Constantes
    paris = timezone( 'Europe/Paris' )

    # Connexion à la base de données
    db = legi.utils.connect_db(bdd)

    # Créer le dossier si besoin
    sousdossier = '.'
    id = texte
    nom = texte

    # Flags: 1) s’il y a des versions en vigueur future, 2) si la première version est une vigueur future
    futur = False
    futur_debut = False

    # Obtenir la date de la base LEGI
    last_update = db.one("""
        SELECT value
        FROM db_meta
        WHERE key = 'last_update'
    """)
    last_update_jour = datetime.date(*(time.strptime(last_update, '%Y%m%d-%H%M%S')[0:3]))
    last_update = paris.localize( datetime.datetime(*(time.strptime(last_update, '%Y%m%d-%H%M%S')[0:6])) )
    logger.info('Dernière mise à jour de la base LEGI : {}'.format(last_update.isoformat()))

    Path(dossier).mkdir_p()
    entree_texte = db.one("""
        SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, cid, mtime
        FROM textes_versions
        WHERE id = '{0}'
    """.format(id))
    if entree_texte == None:
        entree_texte = db.one("""
            SELECT id, nature, titre, titrefull, etat, date_debut, date_fin, num, cid, mtime
            FROM textes_versions
            WHERE cid = '{0}'
        """.format(id))
    if entree_texte == None:
        raise Exception('Pas de texte avec cet ID ou CID')
    cid = entree_texte[8]
    mtime = entree_texte[9]
    if entree_texte[1] in natures.keys():
        if not os.path.exists(os.path.join(dossier, natures[entree_texte[1]]+'s')):
            os.makedirs(os.path.join(dossier, natures[entree_texte[1]]+'s'))
        sousdossier = natures[entree_texte[1]]+'s'

    mise_a_jour = True
    if entree_texte[1] and (entree_texte[1] in natures.keys()) and entree_texte[7]:
        identifiant = natures[entree_texte[1]]+' '+entree_texte[7]
        identifiant = identifiant.replace(' ','_')
        nom_fichier = identifiant
        sousdossier = os.path.join(natures[entree_texte[1]]+'s', identifiant)
        if not os.path.exists(os.path.join(dossier, sousdossier)):
            mise_a_jour = False
        Path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(id, entree_texte[1] == 'CODE')
    elif entree_texte[1] and (entree_texte[1] in natures.keys()) and entree_texte[2]:
        identifiant = entree_texte[2][0].lower()+entree_texte[2][1:].replace(' ','_')
        nom_fichier = identifiant
        sousdossier = os.path.join(natures[entree_texte[1]]+'s', identifiant)
        if not os.path.exists(os.path.join(dossier, sousdossier)):
            mise_a_jour = False
        Path(os.path.join(dossier, sousdossier)).mkdir_p()
        chemin_base = chemin_texte(id, entree_texte[1] == 'CODE')
    else:
        raise Exception('Type bizarre ou inexistant')
        sousdossier = os.path.join(sousdossier, nom)
        nom_fichier = id
    dossier = os.path.join(dossier, sousdossier)
    sousdossier = '.'
    if not os.path.exists(dossier):
        os.makedirs(dossier)
    fichier = os.path.join(dossier, nom_fichier + '.md')

    # Créer le dépôt Git avec comme branche maîtresse 'texte' ou 'articles'
    branche = 'texte'
    if format['organisation'] == 'repertoires-simple':
        branche = 'articles'
    if not os.path.exists(os.path.join(dossier, '.git')):
        subprocess.call(['git', 'init'], cwd=dossier)
        subprocess.call(['git', 'symbolic-ref', 'HEAD', 'refs/heads/'+branche], cwd=dossier)
    else:
        subprocess.call(['git', 'checkout', '--', sousdossier], cwd=dossier)
    
    date_reprise_git = None
    reset_hash = ''
    if mise_a_jour:
        tags = subprocess.check_output(['git', 'tag', '-l'], cwd=dossier)
        tags = strip(tags).split('\n')
        date_maj_git = False
        if len(tags) == 0:
            raise Exception('Pas de tag de la dernière mise à jour')
        date_maj_git = paris.localize( datetime.datetime(*(time.strptime(tags[-1], '%Y%m%d-%H%M%S')[0:6])) )
        logger.info('Dernière mise à jour du dépôt : {}'.format(date_maj_git.isoformat()))
        if int(time.mktime(date_maj_git.timetuple())) >= mtime:
            logger.info('Pas de mise à jour disponible')
            return

        # Obtention de la première date qu’il faudra mettre à jour
        date_reprise_git = db.one("""
            SELECT date_debut
            FROM articles
            WHERE cid = '{0}' AND mtime > {1}
        """.format(cid,int(time.mktime(date_maj_git.timetuple()))))

        # Lecture des versions en vigueur dans le dépôt Git
        try:
            if subprocess.check_output(['git', 'rev-parse', '--verify', 'futur-'+branche], cwd=dossier):
                subprocess.call(['git', 'checkout', 'futur-'+branche], cwd=dossier)
        except subprocess.CalledProcessError:
            pass
        versions_git = strip(subprocess.check_output(['git', 'log', '--oneline'], cwd=dossier).decode('utf-8')).split('\n')
        for log_version in versions_git:
            for m, k in MOIS.items():
                log_version = log_version.replace( m, k )
            m = re.match(r'^([0-9a-f]+) .* ([0-9]+)(?:er)? ([0-9]+) ([0-9]+)$', log_version.encode('utf-8'))
            if not m:
                raise Exception('Version non reconnue dans le dépôt Git')
            date = '{0:04d}-{1:02d}-{2:02d}'.format(int(m.group(4)), int(m.group(3)), int(m.group(2)))
            reset_hash = m.group(1)
            if date < date_reprise_git:
                break
            reset_hash = ''

        if reset_hash:
            if date_reprise_git <= last_update_jour.strftime('%Y-%m-%d'):
                subprocess.call(['git', 'checkout', branche], cwd=dossier)
                try:
                    if subprocess.check_output(['git', 'rev-parse', '--verify', 'futur-'+branche], cwd=dossier):
                        subprocess.call(['git', 'branch', '-D', 'futur-'+branche], cwd=dossier)
                except subprocess.CalledProcessError:
                    pass
            subprocess.call(['git', 'reset', '--hard', reset_hash], cwd=dossier) 
        else:
            subprocess.call(['git', 'branch', '-m', 'texte', 'junk'], cwd=dossier)
            subprocess.call(['git', 'checkout', '--orphan', branche], cwd=dossier)
            subprocess.call(['git', 'branch', '-D', 'junk'], cwd=dossier)

    # Vérifier que les articles ont été transformés en Markdown ou les créer le cas échéant
    creer_markdown_texte((None, cid, None, None), db, cache)
    
    # Sélection des versions du texte
    versions_texte_db = db.all("""
          SELECT debut, fin
          FROM sommaires
          WHERE cid = '{0}'
          ORDER BY debut
    """.format(cid))
    dates_texte = []
    dates_fin_texte = []
    versions_texte = []
    for vers in versions_texte_db:
        vt = vers[0]
        if isinstance(vt, basestring):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        if date_reprise_git and vt.strftime('%Y-%m-%d') < date_reprise_git:
            continue
        dates_texte.append( vt )
        vt = vers[1]
        if isinstance(vt, basestring):
            vt = datetime.date(*(time.strptime(vt, '%Y-%m-%d')[0:3]))
        dates_fin_texte.append( vt )
    versions_texte = sorted(set(dates_texte).union(set(dates_fin_texte)))
    
    sql_texte = "cid = '{0}'".format(cid)
    versions_texte = sorted(list(set(versions_texte)))

    # Pour chaque version
    # - rechercher les sections et articles associés
    # - créer le fichier texte au format demandé
    # - commiter le fichier
    for (i_version, version_texte) in enumerate(versions_texte):
        
        # Passer les versions 'nulles'
        #if version_texte.base is None:
        #    continue
        if i_version >= len(versions_texte)-1:
            break

        debut = versions_texte[i_version]
        fin = versions_texte[i_version+1]
        debut_datetime = paris.localize( datetime.datetime( debut.year, debut.month, debut.day ) )

        if not futur and debut > last_update_jour:
            if i_version == 0:
                subprocess.call(['git', 'symbolic-ref', 'HEAD', 'refs/heads/futur-'+branche], cwd=dossier)
                if not reset_hash:
                    futur_debut = True
            else:
                subprocess.call(['git', 'checkout', '-b', 'futur-'+branche], cwd=dossier)
            futur = True

        sql = sql_texte + " AND debut <= '{0}' AND ( fin >= '{1}' OR fin == '2999-01-01' OR etat == 'VIGUEUR' )".format(debut,fin)

        # Créer l’en-tête
        date_fr = '{} {} {}'.format(debut.day, MOIS2[int(debut.month)], debut.year)
        if debut.day == 1:
            date_fr = '1er {} {}'.format(MOIS2[int(debut.month)], debut.year)
        contenu = nom + '\n'   \
                  + '\n'   \
                  + '- Date de consolidation : ' + date_fr + '\n'            \
                  + '- [Lien permanent Légifrance](https://www.legifrance.gouv.fr/affichCode.do?cidTexte=' + cid + '&dateTexte=' + debut.isoformat().replace('-','') + ')\n' \
                  + '\n' \
                  + '\n'

        # Enregistrement du fichier
        if format['organisation'] != 'fichier-unique':
            f_texte = open('README.md', 'w')
            f_texte.write(contenu.encode('utf-8'))
            f_texte.close()

            # Retrait des fichiers des anciennes versions
            subprocess.call('rm *.md', cwd=dossier, shell=True)

        # Créer les sections (donc tout le texte)
        contenu = creer_sections(contenu, 1, None, (debut,fin), sql, [], format, dossier, db, cache)
        
        # Enregistrement du fichier
        if format['organisation'] == 'fichier-unique':
            f_texte = open(fichier, 'w')
            f_texte.write(contenu.encode('utf-8'))
            f_texte.close()
        
        # Exécuter Git
        subprocess.call(['git', 'add', '.'], cwd=dossier)
        #subprocess.call(['git', 'commit', '--author="Législateur <>"', '--date="' + str(debut_datetime) + '"', '-m', 'Version consolidée au {}\n\nVersions :\n- base LEGI : {}\n- programme Archéo Lex : {}'.format(date_fr, date_base_legi, version_archeolex), '-q', '--no-status'], cwd=dossier)
        subprocess.call(['git', 'commit', '--author="Législateur <>"', '--date="' + str(debut_datetime) + '"', '-m', 'Version consolidée au {}'.format(date_fr), '-q', '--no-status'], cwd=dossier, env={ 'GIT_COMMITTER_DATE': last_update.isoformat(), 'GIT_COMMITTER_NAME': 'Législateur'.encode('utf-8'), 'GIT_COMMITTER_EMAIL': '' })
        
        if fin == None or str(fin) == '2999-01-01':
            logger.info('Version {} enregistrée (du {} à maintenant)'.format(i_version+1, debut))
        else:
            logger.info('Version {} enregistrée (du {} au {})'.format(i_version+1, debut, fin))
    
    if futur and not futur_debut:
        subprocess.call(['git', 'checkout', branche], cwd=dossier)
    
    # Optimisation du dossier git
    subprocess.call(['git', 'gc', '--aggressive'], cwd=dossier)
    subprocess.call('rm -rf .git/hooks .git/refs/heads .git/refs/tags .git/logs .git/COMMIT_EDITMSG .git/branches', cwd=dossier, shell=True)
    subprocess.call('chmod -x .git/config', cwd=dossier, shell=True)

    # Ajout du tag de date éditoriale
    subprocess.call(['git', 'tag', last_update.strftime('%Y%m%d-%H%M%S')], cwd=dossier)

    # Suppression du cache
    subprocess.call('rm -rf markdown/{0}'.format(cid), cwd=cache, shell=True)