Commit d77d01c9 by Nicolas Joyard

Refacto complête jobs

parent 3ca5e5d4
#!/bin/bash
set -e
# Usage
# extractjson.sh JSONFILE OUTPUTDIR OBJTYPE [OBJTYPE ...]
jsonfile=$1
shift
outdir=$1
shift
changes=$outdir/changes
tmpdir=$outdir/tmp
[ -d $tmpdir ] && rm -R $tmpdir
mkdir -p $tmpdir $changes
for objtype in $*; do
objdir=$outdir/$objtype
mkdir -p $objdir
singulartype=${objtype}
pluraltype=${objtype}s
uidfield=uid
case $objtype in
document)
pluraltype=textesLegislatifs
;;
dossier)
pluraltype=dossiersLegislatifs
;;
texte)
singulartype=texteleg
pluraltype=textesEtAmendements
uidfield=refTexteLegislatif
;;
esac
jsonpath=.${pluraltype}.${singulartype}[]
case $objtype in
dossier)
jsonpath=.export${jsonpath}.dossierParlementaire
;;
texte|scrutin)
;;
*)
jsonpath=.export${jsonpath}
esac
echo "Extract $objtype ($jsonpath)"
# Split json into one array
jq -ac $jsonpath $jsonfile | split -l 1 -a 10 -- - $tmpdir/${objtype}_
for f in $tmpdir/${objtype}_*; do
uid=$(jq -r .$uidfield $f)
if [ "$(echo "$uid" | grep "#text")" ]; then
uid=$(jq -r ".$uidfield.\"#text\"" $f)
fi
if [ -z "$uid" -o "$uid" = "null" ]; then
echo "Invalid UID in $f"
continue
fi
destfile=$objdir/$uid
changed=no
if [ ! -f $destfile ]; then
changed=yes
else
cmp --quiet <(jq -cS . $destfile) <(jq -cS . $f) || changed=yes
fi
if [ "$changed" = "yes" ]; then
# # Uncomment to debug
# [ -f $destfile ] || echo "$destfile does not exist"
# if [ -f $destfile ]; then
# echo "$destfile is different from $f"
# jq -cS . $destfile
# jq -cS . $f
# fi
[ -f $destfile ] && rm $destfile
mv $f $destfile
ln -sf $destfile $changes/${objtype}_$uid
else
rm $f
fi
done
done
"""Ajout jobid
Revision ID: 6b5e54751114
Revises: 44148b36c11f
Create Date: 2018-04-08 12:24:05.078029
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6b5e54751114'
down_revision = '44148b36c11f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('parlapi_jobs', sa.Column('jobid', sa.Unicode(), nullable=True))
op.execute("DELETE FROM parlapi_jobs")
op.alter_column('parlapi_jobs', 'jobid', nullable=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('parlapi_jobs', 'jobid')
# ### end Alembic commands ###
......@@ -4,6 +4,7 @@
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from .jobs.command import JobCommand
from .parlapi import app
from .models import db
......@@ -11,22 +12,11 @@ from .models import db
manager = Manager(app)
#
# Run server
#
@manager.command
def runserver():
u"Exécute le serveur web flask intégré"
app.run()
#
# Manage database
#
@manager.command
@manager.option('-o', '--output', dest='output')
def erdiagram(output):
......@@ -37,62 +27,7 @@ def erdiagram(output):
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
#
# Run import jobs
#
@manager.command
@manager.option('-f', '--file', dest='file', default=None)
def update_amo_an(file=None):
u"Met à jour acteurs, mandats, organes depuis l'AN"
from .jobs.an.amo import run
app.config.update(SQLALCHEMY_ECHO=False)
run(app, False, file)
@manager.command
@manager.option('-f', '--file', dest='file', default=None)
def update_dossiers_an(file=None):
u"Met à jour dossiers, documents, actes depuis l'AN"
from .jobs.an.dossiers import run
app.config.update(SQLALCHEMY_ECHO=False)
run(app, False, file)
@manager.command
@manager.option('-f', '--file', dest='file', default=None)
def update_amendements_an(file=None):
u"Met à jour amendements depuis l'AN"
from .jobs.an.amendements import run
app.config.update(SQLALCHEMY_ECHO=False)
run(app, False, file)
@manager.command
@manager.option('-f', '--file', dest='file', default=None)
def update_scrutins_an(file=None):
u"Met à jour scrutins depuis l'AN"
from .jobs.an.scrutins import run
app.config.update(SQLALCHEMY_ECHO=False)
run(app, False, file)
@manager.command
@manager.option('-f', '--file', dest='file', default=None)
def update_reunions_an(file=None):
u"Met à jour réunions depuis l'AN"
from .jobs.an.reunions import run
app.config.update(SQLALCHEMY_ECHO=False)
run(app, False, file)
manager.add_command('job', JobCommand)
if __name__ == '__main__':
manager.run()
......@@ -16,6 +16,7 @@ class DefaultConfig(object):
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
DATA_DIR = os.path.join(BASE_DIR, 'data')
BIN_DIR = os.path.join(BASE_DIR, 'bin')
API_PAGE_SIZE = 10
SECRET_KEY = 'no-secret-key'
PIWIK_HOST = None
......
# -*- coding: utf-8 -*-
from ..base import BaseANJob
from ..utils import ijson_items
from ...models.an import (Amendement, ActeurAmendement, Document, Legislature,
Organe)
class ImportAmendementsJob(BaseANJob):
def __init__(self, app, name, url):
self._job_name = name
super(ImportAmendementsJob, self).__init__(app, url)
@property
def job_name(self):
return self._job_name
def parse_json(self, filename, stream):
texte = 'textesEtAmendements.texteleg.item'
for prefix, obj in ijson_items(stream, [texte]):
if prefix == texte:
self.save_texte(obj)
def save_texte(self, json):
self.current = 'Texte %s' % json['refTexteLegislatif']
document = self.save(Document, json['refTexteLegislatif'])
amendements = json['amendements']['amendement']
if isinstance(amendements, dict):
amendements = [amendements]
for am in amendements:
self.save_amendement(am, document)
def save_amendement(self, json, document):
self.current = 'Amendement %s' % json['uid']
id = json['identifiant']
fr = json['pointeurFragmentTexte']
dv = fr['division']
co = json['corps']
lo = json['loiReference']
si = json['signataires']
data = {
'amendement_parent': None,
'article_99': int(json['article99']) > 0,
'cardinal_multiples': int(json['cardinaliteAmdtMultiples']),
'code_loi': lo['codeLoi'],
'code_loi_division': lo['divisionCodeLoi'],
'corps_dispositif': co.get('dispositif', None),
'corps_expose': co.get('exposeSommaire', None),
'date_depot': self.parse_date(json['dateDepot']),
'date_distribution': self.parse_date(json['dateDistribution']),
'date_sort': None,
'division_alinea': None,
'division_alinea_position': None,
'division_article_additionnel':
dv.get('articleAdditionnel', '0') != '0',
'division_chapitre_additionnel':
dv.get('chapitreAdditionnel', '0') != '0',
'division_position': dv['avant_A_Apres'],
'division_texte': dv['titre'],
'document': document,
'etape_texte': json['etapeTexte'],
'etat': json['etat'],
'legislature': self.save(Legislature, int(id['legislature'])),
'num_rect': int(id['numRect']),
'numero': int(id['numero']),
'numero_long': json['numeroLong'],
'organe': self.save(Organe, id['saisine']['organeExamen']),
'sort': None,
'tri': json['triAmendement'],
'auteur_texte_affichable': si['texteAffichable'],
}
if json.get('representations', None):
reps = json['representations']
if isinstance(reps, dict):
reps = [reps]
rep = reps[0]['representation']
if len(reps) and rep.get('nom', '') == 'PDF' and \
rep.get('contenu', {}).get('documentURI', None):
data['url_pdf'] = rep['contenu']['documentURI']
if json.get('amendementParent', None):
data['amendement_parent'] = \
self.save(Amendement, json['amendementParent'])
signataires = []
auteur = si['auteur']
auteur_ref = None
if 'acteurRef' in auteur and auteur['acteurRef']:
auteur_ref = auteur['acteurRef']
aa = ActeurAmendement(relation='auteur')
aa.acteur_id = auteur_ref
signataires.append(aa)
cosignataires = si['cosignataires']
if cosignataires:
if 'acteurRef' not in cosignataires:
cosignataires = []
else:
cosignataires = cosignataires['acteurRef']
if isinstance(cosignataires, str):
cosignataires = [cosignataires]
for cosignataire in cosignataires:
if auteur_ref and cosignataire == auteur_ref:
continue
acs = ActeurAmendement(relation='cosignataire')
acs.acteur_id = cosignataire
signataires.append(acs)
data['signataires'] = signataires
so = json['sort']
if so:
data['sort'] = so['sortEnSeance']
data['date_sort'] = self.parse_date(so['dateSaisie'])
al = fr['alinea']
if al and al.get('numero', None):
data['division_alinea'] = int(al['numero'])
data['division_alinea_position'] = al['avant_A_Apres']
self.save(Amendement, json['uid'], data)
def run(app, force=False, file=None):
ImportAmendementsJob(
app,
'AN: amendements',
'/travaux-parlementaires/amendements'
).run(force, file)
# -*- coding: utf-8 -*-
from ..base import BaseANJob
from ..utils import ijson_items
from ...models.an import Organe, Regime, Legislature, Acteur, Mandat
class ImportAMOJob(BaseANJob):
def __init__(self, app, name, url):
self._job_name = name
super(ImportAMOJob, self).__init__(app, url)
@property
def job_name(self):
return self._job_name
def parse_json(self, filename, stream):
acteur = 'export.acteurs.acteur.item'
organe = 'export.organes.organe.item'
for prefix, obj in ijson_items(stream, [organe, acteur]):
if prefix == acteur:
self.save_acteur(obj)
elif prefix == organe:
self.save_organe(obj)
def save_acteur(self, json):
self.current = 'Acteur %s' % json['uid']['#text']
ec = json['etatCivil']
id = ec['ident']
nais = ec['infoNaissance']
data = {
'civilite': id['civ'],
'date_deces': None,
'date_naissance': self.parse_date(nais['dateNais']),
'dept_naissance': nais['depNais'],
'nom': id['nom'],
'pays_naissance': nais['paysNais'],
'prenom': id['prenom'],
'profession': None,
'profession_cat_insee': None,
'profession_fam_insee': None,
'ville_naissance': nais['villeNais'],
}
if ec.get('dateDeces', None):
data['date_deces'] = self.parse_date(ec['dateDeces'])
if 'profession' in json:
pro = json['profession']
data['profession'] = pro['libelleCourant']
data['profession_cat_insee'] = pro['socProcINSEE']['catSocPro']
data['profession_fam_insee'] = pro['socProcINSEE']['famSocPro']
acteur = self.save(Acteur, json['uid']['#text'], data)
for mandat_json in json['mandats']['mandat']:
self.save_mandat(acteur, mandat_json)
def save_mandat(self, acteur, json):
self.current = 'Mandat %s' % json['uid']
if isinstance(json['organes']['organeRef'], str):
organe_refs = [json['organes']['organeRef']]
else:
organe_refs = json['organes']['organeRef']
data = {
'acteur': acteur,
'date_debut': self.parse_date(json['dateDebut']),
'date_fin': None,
'date_publication': None,
'election_cause': None,
'election_circo': None,
'election_dept': None,
'election_dept_num': None,
'election_region': None,
'libelle': None,
'nomination_principale': json['nominPrincipale'] == '1',
'organes': [self.save(Organe, ref) for ref in organe_refs],
'preseance': None,
'qualite': json['infosQualite']['codeQualite'],
'url_hatvp': None,
}
if json.get('datePublication', None):
data['date_publication'] = self.parse_date(json['datePublication'])
if json.get('dateFin', None):
data['date_fin'] = self.parse_date(json['dateFin'])
if json.get('preseance', None):
data['preseance'] = int(json['preseance'])
if json.get('libelle', None):
data['libelle'] = json['libelle']
if json.get('InfosHorsSIAN', None):
if json['InfosHorsSIAN'].get('HATVP_URI', None):
data['url_hatvp'] = json['InfosHorsSIAN']['HATVP_URI']
if 'election' in json:
el = json['election']
li = el['lieu']
data['election_region'] = li['region']
data['election_dept'] = li['departement']
data['election_dept_num'] = li['numDepartement']
data['election_cause'] = el['causeMandat']
if li['numCirco']:
data['election_circo'] = int(li['numCirco'])
self.save(Mandat, json['uid'], data)
def save_organe(self, json):
self.current = 'Organe %s' % json['uid']
data = {
'abbreviation': json['libelleAbrev'],
'date_debut': None,
'date_fin': None,
'legislature': None,
'libelle': json['libelle'],
'libelle_court': json['libelleAbrege'],
'regime': None,
'type': json['codeType'],
}
if json['viMoDe']['dateDebut']:
data['date_debut'] = self.parse_date(json['viMoDe']['dateDebut'])
if json['viMoDe']['dateFin']:
data['date_fin'] = self.parse_date(json['viMoDe']['dateFin'])
if json.get('regime', None):
data['regime'] = self.save(Regime, json['regime'])
if json.get('legislature', None):
data['legislature'] = self.save(Legislature,
int(json['legislature']))
if data['regime']:
data['legislature'].regime = data['regime']
self.save(Organe, json['uid'], data)
def run(app, force=False, file=None):
if file:
ImportAMOJob(app, 'AN: députés (historique)', '').run(force, file)
else:
ImportAMOJob(app, 'AN: députés-sénateurs-ministres',
'/acteurs/deputes-senateurs-ministres').run(force)
ImportAMOJob(app, 'AN: députés (historique)',
'/acteurs/historique-des-deputes').run(force)
# -*- coding: utf-8 -*-
from ..base import BaseANJob
from ..utils import ijson_items
from ...models.an import (Organe, Legislature, Acteur, Document, Theme, Mandat,
ActeurDocument, OrganeDocument, Dossier, Acte,
OrganeDossier, ActeurDossier)
class ImportDossiersJob(BaseANJob):
def __init__(self, app, name, url):
self._job_name = name
super(ImportDossiersJob, self).__init__(app, url)
@property
def job_name(self):
return self._job_name
def parse_json(self, filename, stream):
document = 'export.textesLegislatifs.document.item'
dossier = 'export.dossiersLegislatifs.dossier.item'
for prefix, obj in ijson_items(stream, [document, dossier]):
if prefix == document:
self.save_document(obj)
if prefix == dossier:
self.save_dossier(obj['dossierParlementaire'])
def save_document(self, json, chaine=[]):
if json['uid'] in chaine:
self.warn(
'Dépendance circulaire sur documents: %s > %s' %
(' > '.join(chaine), json['uid'])
)
return None
chaine.append(json['uid'])
self.current = 'Document %s' % json['uid']
chrono = json['cycleDeVie']['chrono']
klass = json['classification']
notice = json['notice']
data = {
'acteurs': [],
'date_creation': self.parse_date(chrono['dateCreation']),
'date_depot': self.parse_date(chrono['dateDepot']),
'date_publication': self.parse_date(chrono['datePublication']),
'date_publication_web':
self.parse_date(chrono['datePublicationWeb']),
'denomination_structurelle': json['denominationStructurelle'],
'divisions': [],
'dossier': None,
'legislature': None,
'notice_numero': notice['numNotice'],
'organes': [],
'soustype_code': None,
'soustype_libelle': None,
'statut_adoption': klass['statutAdoption'],
'themes': [],
'titre': json['titres']['titrePrincipal'],
'type_code': None,
'type_libelle': None,
}
if klass.get('type', None):
data['type_code'] = klass['type']['code']
data['type_libelle'] = klass['type']['libelle']
if klass.get('sousType', None):
data['soustype_code'] = klass['sousType']['code']
data['soustype_libelle'] = klass['sousType'].get('libelle', None)
if json.get('legislature', None):
data['legislature'] = self.save(Legislature,
int(json['legislature']))
if 'dossierRef' in json:
data['dossier'] = self.save(Dossier, json['dossierRef'])
if json.get('indexation', None):
libelle = json['indexation']['themes']['theme']['libelleTheme']
data['themes'] = [self.save(Theme, libelle)]
if json.get('divisions', None):
divs = json['divisions']['division']
if isinstance(divs, dict):
divs = [divs]
data['divisions'] = [div for div in
[self.save_document(d, chaine) for d in divs]
if div]
acteurs = []
organes = []
auteurs = json['auteurs']['auteur']
if isinstance(auteurs, dict):
auteurs = [auteurs]
for auteur in auteurs:
if 'acteur' in auteur:
qual = auteur['acteur'].get('qualite', None)
ad = ActeurDocument(relation='auteur', qualite=qual)
ad.acteur = self.save(Acteur, auteur['acteur']['acteurRef'])
acteurs.append(ad)
elif 'organe' in auteur:
od = OrganeDocument(relation='auteur')
od.organe = self.save(Organe, auteur['organe']['organeRef'])
organes.append(od)
else:
self.warn('Ignoré type auteur inconnu dans %s' % self.current)
if json.get('coSignataires', None):
cosign = json['coSignataires']['coSignataire']
if isinstance(cosign, dict):
cosign = [cosign]
for auteur in cosign:
dc = self.parse_date(auteur['dateCosignature'])
if auteur.get('dateRetraitCosignature', None):
dr = self.parse_date(auteur['dateRetraitCosignature'])
else:
dr = None
if 'acteur' in auteur:
ad = ActeurDocument(relation='cosignataire',
date_cosignature=dc,
date_retrait_cosignature=dr)
ad.acteur = self.save(Acteur,
auteur['acteur']['acteurRef'])
acteurs.append(ad)
elif 'organe' in auteur:
od = OrganeDocument(relation='cosignataire',
date_cosignature=dc,
date_retrait_cosignature=dr)
od.organe = self.save(Organe,
auteur['organe']['organeRef'])
organes.append(od)
data['acteurs'] = acteurs
data['organes'] = organes
document = self.save(Document, json['uid'], data)
chaine.pop()
return document
def save_dossier(self, json):
self.current = 'Dossier %s' % json['uid']
td = json['titreDossier']
pp = json['procedureParlementaire']
data = {
'actes_legislatifs': [],
'acteurs': [],
'legislature': self.save(Legislature, int(json['legislature'])),
'organes': [],
'procedure_code': int(pp['code']),
'procedure_libelle': pp['libelle'],
'senat_chemin': td['senatChemin'],
'titre': td.get('titre', None),
'titre_chemin': td['titreChemin'],
}
if json.get('actesLegislatifs', None):
json_actes = json['actesLegislatifs']['acteLegislatif']
if isinstance(json_actes, dict):
json_actes = [json_actes]
data['actes_legislatifs'] = [self.save_acte(j) for j in json_actes]
acteursdossier = []
organesdossier = []
if json.get('initiateur', None):
if json['initiateur'].get('acteurs', None):
acteurs = json['initiateur']['acteurs']['acteur']
if isinstance(acteurs, dict):
acteurs = [acteurs]
for acteur in acteurs:
ad = ActeurDossier(relation='initiateur')
ad.acteur = self.save(Acteur, acteur['acteurRef'])
ad.mandat = self.save(Mandat, acteur['mandatRef'])
acteursdossier.append(ad)
if json['initiateur'].get('organes', None):
organes = json['initiateur']['organes']['organe']
if isinstance(organes, dict):
organes = [organes]
for organe in organes:
od = OrganeDossier(relation='initiateur')
od.organe = self.save(Organe, organe['organeRef']['uid'])
organesdossier.append(od)
data['acteurs'] = acteursdossier
data['organes'] = organesdossier
dossier = self.save(Dossier, json['uid'], data)
return dossier
def save_acte(self, json):
self.current = 'Acte %s' % json['uid']
data = {
'code': json['codeActe'],
'libelle': json['libelleActe']['nomCanonique'],
'date': None,
'actes': [],
'organe': None,
'document': None
}
if json.get('date', None):
data['date'] = self.parse_date(json['date'])
elif json.get('dateActe', None