Commit 3ca5e5d4 by Nicolas Joyard

Move to python3 and fix flake8 errors

parent d40a316d
......@@ -16,7 +16,7 @@ n'est ajoutée au modèle.
### Prérequis
- Python 2.7
- Python 3.6
- PostgreSQL 9.2+
- pip
- virtualenv
......@@ -49,19 +49,6 @@ $ parlapi runserver
$ parlapi db upgrade
```
### Déploiement Openshift
```bash
$ export APP=parlapi
$ rhc app-create $APP python-2.7 postgresql-9.2
$ rhc env set -a $APP OPENSHIFT_PYTHON_WSGI_APPLICATION=wsgi.py
$ rhc env set -a $APP PARLAPI_CONFIG=parlapi.config.OpenshiftConfig
$ rhc app show -a $APP | grep Git
Git URL: ssh://xxx@parlapi-xxx.rhcloud.com/~/git/parlapi.git/
$ git remote add openshift ssh://xxx@parlapi-xxx.rhcloud.com/~/git/parlapi.git/
$ git push --force openshift openshift:master
```
### Déploiement Apache2/mod_wsgi
Voir le rôle `parlapi` dans le [playbook citoyen][gh-playbook]
......
[flake8]
exclude=.git,__pycache__,migrations
......@@ -24,7 +24,7 @@ class ImportAmendementsJob(BaseANJob):
self.save_texte(obj)
def save_texte(self, json):
self.current = u'Texte %s' % json['refTexteLegislatif']
self.current = 'Texte %s' % json['refTexteLegislatif']
document = self.save(Document, json['refTexteLegislatif'])
......@@ -36,7 +36,7 @@ class ImportAmendementsJob(BaseANJob):
self.save_amendement(am, document)
def save_amendement(self, json, document):
self.current = u'Amendement %s' % json['uid']
self.current = 'Amendement %s' % json['uid']
id = json['identifiant']
fr = json['pointeurFragmentTexte']
......@@ -58,8 +58,10 @@ class ImportAmendementsJob(BaseANJob):
'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_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,
......@@ -94,7 +96,7 @@ class ImportAmendementsJob(BaseANJob):
auteur_ref = None
if 'acteurRef' in auteur and auteur['acteurRef']:
auteur_ref = auteur['acteurRef']
aa = ActeurAmendement(relation=u'auteur')
aa = ActeurAmendement(relation='auteur')
aa.acteur_id = auteur_ref
signataires.append(aa)
......@@ -105,13 +107,13 @@ class ImportAmendementsJob(BaseANJob):
else:
cosignataires = cosignataires['acteurRef']
if isinstance(cosignataires, basestring):
if isinstance(cosignataires, str):
cosignataires = [cosignataires]
for cosignataire in cosignataires:
if auteur_ref and cosignataire == auteur_ref:
continue
acs = ActeurAmendement(relation=u'cosignataire')
acs = ActeurAmendement(relation='cosignataire')
acs.acteur_id = cosignataire
signataires.append(acs)
......@@ -127,12 +129,12 @@ class ImportAmendementsJob(BaseANJob):
data['division_alinea'] = int(al['numero'])
data['division_alinea_position'] = al['avant_A_Apres']
am = self.save(Amendement, json['uid'], data)
self.save(Amendement, json['uid'], data)
def run(app, force=False, file=None):
ImportAmendementsJob(
app,
u'AN: amendements',
'AN: amendements',
'/travaux-parlementaires/amendements'
).run(force, file)
......@@ -26,7 +26,7 @@ class ImportAMOJob(BaseANJob):
self.save_organe(obj)
def save_acteur(self, json):
self.current = u'Acteur %s' % json['uid']['#text']
self.current = 'Acteur %s' % json['uid']['#text']
ec = json['etatCivil']
id = ec['ident']
......@@ -62,9 +62,9 @@ class ImportAMOJob(BaseANJob):
self.save_mandat(acteur, mandat_json)
def save_mandat(self, acteur, json):
self.current = u'Mandat %s' % json['uid']
self.current = 'Mandat %s' % json['uid']
if isinstance(json['organes']['organeRef'], basestring):
if isinstance(json['organes']['organeRef'], str):
organe_refs = [json['organes']['organeRef']]
else:
organe_refs = json['organes']['organeRef']
......@@ -115,10 +115,10 @@ class ImportAMOJob(BaseANJob):
if li['numCirco']:
data['election_circo'] = int(li['numCirco'])
mandat = self.save(Mandat, json['uid'], data)
self.save(Mandat, json['uid'], data)
def save_organe(self, json):
self.current = u'Organe %s' % json['uid']
self.current = 'Organe %s' % json['uid']
data = {
'abbreviation': json['libelleAbrev'],
......@@ -146,14 +146,14 @@ class ImportAMOJob(BaseANJob):
if data['regime']:
data['legislature'].regime = data['regime']
organe = self.save(Organe, json['uid'], data)
self.save(Organe, json['uid'], data)
def run(app, force=False, file=None):
if file:
ImportAMOJob(app, u'AN: députés (historique)', '').run(force, file)
ImportAMOJob(app, 'AN: députés (historique)', '').run(force, file)
else:
ImportAMOJob(app, u'AN: députés-sénateurs-ministres',
ImportAMOJob(app, 'AN: députés-sénateurs-ministres',
'/acteurs/deputes-senateurs-ministres').run(force)
ImportAMOJob(app, u'AN: députés (historique)',
ImportAMOJob(app, 'AN: députés (historique)',
'/acteurs/historique-des-deputes').run(force)
......@@ -30,14 +30,14 @@ class ImportDossiersJob(BaseANJob):
def save_document(self, json, chaine=[]):
if json['uid'] in chaine:
self.warn(
u'Dépendance circulaire sur documents: %s > %s' %
'Dépendance circulaire sur documents: %s > %s' %
(' > '.join(chaine), json['uid'])
)
return None
chaine.append(json['uid'])
self.current = u'Document %s' % json['uid']
self.current = 'Document %s' % json['uid']
chrono = json['cycleDeVie']['chrono']
klass = json['classification']
......@@ -48,7 +48,8 @@ class ImportDossiersJob(BaseANJob):
'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']),
'date_publication_web':
self.parse_date(chrono['datePublicationWeb']),
'denomination_structurelle': json['denominationStructurelle'],
'divisions': [],
'dossier': None,
......@@ -72,13 +73,15 @@ class ImportDossiersJob(BaseANJob):
data['soustype_libelle'] = klass['sousType'].get('libelle', None)
if json.get('legislature', None):
data['legislature'] = self.save(Legislature, int(json['legislature']))
data['legislature'] = self.save(Legislature,
int(json['legislature']))
if 'dossierRef' in json:
data['dossier'] = self.save(Dossier, json['dossierRef'])
if json.get('indexation', None):
data['themes'] = [self.save(Theme, json['indexation']['themes']['theme']['libelleTheme'])]
libelle = json['indexation']['themes']['theme']['libelleTheme']
data['themes'] = [self.save(Theme, libelle)]
if json.get('divisions', None):
divs = json['divisions']['division']
......@@ -99,15 +102,15 @@ class ImportDossiersJob(BaseANJob):
for auteur in auteurs:
if 'acteur' in auteur:
qual = auteur['acteur'].get('qualite', None)
ad = ActeurDocument(relation=u'auteur', qualite=qual)
ad = ActeurDocument(relation='auteur', qualite=qual)
ad.acteur = self.save(Acteur, auteur['acteur']['acteurRef'])
acteurs.append(ad)
elif 'organe' in auteur:
od = OrganeDocument(relation=u'auteur')
od = OrganeDocument(relation='auteur')
od.organe = self.save(Organe, auteur['organe']['organeRef'])
organes.append(od)
else:
self.warn(u'Ignoré type auteur inconnu dans %s' % self.current)
self.warn('Ignoré type auteur inconnu dans %s' % self.current)
if json.get('coSignataires', None):
cosign = json['coSignataires']['coSignataire']
......@@ -122,17 +125,18 @@ class ImportDossiersJob(BaseANJob):
dr = None
if 'acteur' in auteur:
ad = ActeurDocument(relation=u'cosignataire',
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=u'cosignataire',
od = OrganeDocument(relation='cosignataire',
date_cosignature=dc,
date_retrait_cosignature=dr)
od.organe = self.save(Organe, auteur['organe']['organeRef'])
od.organe = self.save(Organe,
auteur['organe']['organeRef'])
organes.append(od)
data['acteurs'] = acteurs
......@@ -144,7 +148,7 @@ class ImportDossiersJob(BaseANJob):
return document
def save_dossier(self, json):
self.current = u'Dossier %s' % json['uid']
self.current = 'Dossier %s' % json['uid']
td = json['titreDossier']
pp = json['procedureParlementaire']
......@@ -177,7 +181,7 @@ class ImportDossiersJob(BaseANJob):
if isinstance(acteurs, dict):
acteurs = [acteurs]
for acteur in acteurs:
ad = ActeurDossier(relation=u'initiateur')
ad = ActeurDossier(relation='initiateur')
ad.acteur = self.save(Acteur, acteur['acteurRef'])
ad.mandat = self.save(Mandat, acteur['mandatRef'])
acteursdossier.append(ad)
......@@ -186,7 +190,7 @@ class ImportDossiersJob(BaseANJob):
if isinstance(organes, dict):
organes = [organes]
for organe in organes:
od = OrganeDossier(relation=u'initiateur')
od = OrganeDossier(relation='initiateur')
od.organe = self.save(Organe, organe['organeRef']['uid'])
organesdossier.append(od)
......@@ -197,7 +201,7 @@ class ImportDossiersJob(BaseANJob):
return dossier
def save_acte(self, json):
self.current = u'Acte %s' % json['uid']
self.current = 'Acte %s' % json['uid']
data = {
'code': json['codeActe'],
......@@ -208,7 +212,6 @@ class ImportDossiersJob(BaseANJob):
'document': None
}
if json.get('date', None):
data['date'] = self.parse_date(json['date'])
elif json.get('dateActe', None):
......@@ -234,6 +237,6 @@ class ImportDossiersJob(BaseANJob):
def run(app, force=False, file=None):
ImportDossiersJob(
app,
u'AN: dossiers législatifs',
'AN: dossiers législatifs',
'/travaux-parlementaires/dossiers-legislatifs'
).run(force, file)
......@@ -2,7 +2,7 @@
from ..base import BaseANJob
from ..utils import ijson_items
from ...models.an import (Organe, Acteur, Reunion, ODJItem, ODJPoint,
from ...models.an import (Organe, Acteur, Dossier, Reunion, ODJItem, ODJPoint,
OrganeReunion, ActeurReunion)
......@@ -23,7 +23,7 @@ class ImportReunionsJob(BaseANJob):
self.save_reunion(obj)
def save_reunion(self, json):
self.current = u'Reunion %s' % json['uid']
self.current = 'Reunion %s' % json['uid']
data = {
'acteurs': [],
......@@ -75,13 +75,13 @@ class ImportReunionsJob(BaseANJob):
acts = [acts]
for act in acts:
ar = ActeurReunion(relation=u'demandeur')
ar = ActeurReunion(relation='demandeur')
ar.acteur = self.save(Acteur, act['acteurRef'])
data['acteurs'].append(ar)
# SRSLY WTF !? I don't want to live on this planet anymore D:<
if json.get('demandeur', None) and 'acteurRef' in json['demandeur']:
ar = ActeurReunion(relation=u'demandeur')
ar = ActeurReunion(relation='demandeur')
ar.acteur = self.save(Acteur, json['demandeur']['acteurRef'])
data['acteurs'].append(ar)
......@@ -91,12 +91,12 @@ class ImportReunionsJob(BaseANJob):
orgs = [orgs]
for org in orgs:
or_ = OrganeReunion(relation=u'demandeur')
or_ = OrganeReunion(relation='demandeur')
or_.organe = self.save(Organe, org['organeRef'])
data['organes'].append(or_)
if json.get('organeReuniRef', None):
or_ = OrganeReunion(relation=u'organeReuni')
or_ = OrganeReunion(relation='organeReuni')
or_.organe = self.save(Organe, json['organeReuniRef'])
data['organes'].append(or_)
......@@ -107,7 +107,8 @@ class ImportReunionsJob(BaseANJob):
if isinstance(pris, dict):
pris = [pris]
for p in pris:
ar = ActeurReunion(relation=u'participant', presence=p['presence'])
ar = ActeurReunion(relation='participant',
presence=p['presence'])
ar.acteur = self.save(Acteur, p['acteurRef'])
data['acteurs'].append(ar)
......@@ -117,7 +118,7 @@ class ImportReunionsJob(BaseANJob):
if isinstance(pras, dict):
pras = [pras]
for p in pras:
ar = ActeurReunion(relation=u'auditionne')
ar = ActeurReunion(relation='auditionne')
adata = {}
if p['uid']['@xsi:type'] == 'IdPersonneExterne_type':
......@@ -133,23 +134,24 @@ class ImportReunionsJob(BaseANJob):
ar.acteur = self.save(Acteur, p['uid']['#text'], adata)
data['acteurs'].append(ar)
if json.get('ODJ', None):
odj = json['ODJ']
if odj.get('resumeODJ', None) and odj['resumeODJ'].get('item', None):
if odj.get('resumeODJ', None) \
and odj['resumeODJ'].get('item', None):
items = odj['resumeODJ']['item']
if isinstance(items, basestring):
if isinstance(items, str):
items = [items]
data['items_odj'] = [ODJItem(item=i) for i in items]
if odj.get('pointsODJ', None) and odj['pointsODJ'].get('pointODJ', None):
if odj.get('pointsODJ', None) \
and odj['pointsODJ'].get('pointODJ', None):
points = odj['pointsODJ']['pointODJ']
if isinstance(points, dict):
points = [points]
data['points_odj'] = [self.save_point(p) for p in points]
reunion = self.save(Reunion, json['uid'], data)
self.save(Reunion, json['uid'], data)
def save_point(self, json):
cur = self.current
......@@ -185,7 +187,7 @@ class ImportReunionsJob(BaseANJob):
if json.get('dossierLegislatifsRefs', None):
dossiers = json['dossierLegislatifsRefs']
if isinstance(dossier, dict):
if isinstance(dossiers, dict):
dossiers = [dossiers]
data['dossiers'] = [self.save(Dossier, d['dossierRef'])
for d in dossiers]
......@@ -199,6 +201,6 @@ class ImportReunionsJob(BaseANJob):
def run(app, force=False, file=None):
ImportReunionsJob(
app,
u'AN: réunions',
'AN: réunions',
'/reunions/reunions'
).run(force, file)
......@@ -9,10 +9,10 @@ from ...models.an import (Organe, Legislature, Acteur, Mandat, Scrutin, Votant,
class ImportScrutinsJob(BaseANJob):
positions = {
'nonVotants': u'non-votant',
'pours': u'pour',
'contres': u'contre',
'abstentions': u'abstention'
'nonVotants': 'non-votant',
'pours': 'pour',
'contres': 'contre',
'abstentions': 'abstention'
}
def __init__(self, app, name, url):
......@@ -30,7 +30,7 @@ class ImportScrutinsJob(BaseANJob):
self.save_scrutin(obj)
def save_scrutin(self, json):
self.current = u'Scrutin %s' % json['uid']
self.current = 'Scrutin %s' % json['uid']
sy = json['syntheseVote']
gr = json['ventilationVotes']['organe']['groupes']['groupe']
......@@ -60,11 +60,11 @@ class ImportScrutinsJob(BaseANJob):
'type_majorite': json['typeVote']['typeMajorite'],
}
scrutin = self.save(Scrutin, json['uid'], data)
self.save(Scrutin, json['uid'], data)
def save_groupe(self, json):
cur = self.current
self.current = u'%s > groupe %s' % (cur, json['organeRef'])
self.current = '%s > groupe %s' % (cur, json['organeRef'])
groupe = ScrutinGroupe()
......@@ -85,7 +85,7 @@ class ImportScrutinsJob(BaseANJob):
votants = []
for k, pos in self.positions.items():
if vote['decompteNominatif'].get(k, None):
if isinstance(vote['decompteNominatif'][k], basestring):
if isinstance(vote['decompteNominatif'][k], str):
continue
vs = vote['decompteNominatif'][k]['votant']
......@@ -106,6 +106,6 @@ class ImportScrutinsJob(BaseANJob):
def run(app, force=False, file=None):
ImportScrutinsJob(
app,
u'AN: scrutins',
'AN: scrutins',
'/travaux-parlementaires/votes'
).run(force, file)
......@@ -2,7 +2,6 @@
from datetime import datetime
from collections import defaultdict
import copy
import os
import traceback
from zipfile import ZipFile
......@@ -46,16 +45,16 @@ class BaseJob(object):
self._updated = defaultdict(lambda: 0)
def debug(self, msg):
self.app.logger.debug(u'<%s> %s' % (self.job_name, msg))
self.app.logger.debug('<%s> %s' % (self.job_name, msg))
def info(self, msg):
self.app.logger.info(u'<%s> %s' % (self.job_name, msg))
self.app.logger.info('<%s> %s' % (self.job_name, msg))
def warn(self, msg):
self.app.logger.warn(u'<%s> %s' % (self.job_name, msg))
self.app.logger.warn('<%s> %s' % (self.job_name, msg))
def error(self, msg):
self.app.logger.error(u'<%s> %s' % (self.job_name, msg))
self.app.logger.error('<%s> %s' % (self.job_name, msg))
def parse_date(self, date):
if not date:
......@@ -72,30 +71,28 @@ class BaseJob(object):
# Get column info
if model.__tablename__ not in self.columns:
mapper = inspect(model)
self.columns[model.__tablename__] = { c.name: type(c.type).__name__ for c in mapper.columns }
# self.relations[model.__tablename__] = mapper.relationships
self.columns[model.__tablename__] = {
c.name: type(c.type).__name__ for c in mapper.columns
}
columns = self.columns[model.__tablename__]
# relations = self.relations[model.__tablename__]
# Search for item in cache
cache = self.cache[model.__tablename__]
pkfield = self.cache_pk.get(model.__tablename__, 'id')
item = cache.get(pk, None)
# Search for item in db
# Search for item in db
if not item:
item = model.query.filter_by(**{pkfield: pk}).first()
if not item:
# Create new item
# self.debug(u'{} #{} created'.format(model.__tablename__, pk))
item = model(**{pkfield: pk})
db.session.add(item)
created = True
# Ensure item is now in cache
# Ensure item is now in cache
cache[pk] = item
# Update item data
......@@ -107,24 +104,14 @@ class BaseJob(object):
newvalue = newvalue.date()
if curvalue != newvalue:
# self.debug(
# u"{} #{} changed {}: {} => {}".format(
# model.__tablename__,
# pk,
# key,
# curvalue,
# newvalue
# )
# )
changed = True
setattr(item, key, newvalue)
# Update last mod date
# Update last mod date
if changed or created:
item._last_modified = self._start
# Update job counters
# Update job counters
if created:
self._created[model.__tablename__] += 1
elif changed:
......@@ -136,9 +123,9 @@ class BaseJob(object):
job = self.job
job.date_exec = datetime.now()
job.temps_exec = (datetime.now() - self._start).seconds
job.nb_items = sum([v for k, v in self._created.items()]) + \
sum([v for k, v in self._updated.items()])
job.resultat = status or u''
job.nb_items = (sum([v for k, v in self._created.items()]) +
sum([v for k, v in self._updated.items()]))
job.resultat = status or ''
if file:
job.url_fichier = file
......@@ -163,34 +150,34 @@ class BaseANJob(BaseJob):
self.current = None
self.parse_json(filename, filestream)
return True
except Exception, e:
except Exception as e:
db.session.rollback()
if self.current:
msg = u'Erreur (%s)' % self.current
msg = 'Erreur (%s)' % self.current
else:
msg = u'Erreur'
stack = u''.join(traceback.format_exc())
self.error(u'%s: %s\n%s' % (msg, e, stack))
self.update_status(u'error:parse-json')
msg = 'Erreur'
stack = ''.join(traceback.format_exc())
self.error('%s: %s\n%s' % (msg, e, stack))
self.update_status('error:parse-json')
return False
def run(self, ignore_lmd=False, file=None):
if file:
self.info(u'Exécution avec fichier %s' % file)
self.info('Exécution avec fichier %s' % file)
with open(file) as f:
if not self.handle_json(os.path.basename(file), f):
return
db.session.commit()
self.info(u'Job terminé')
self.info('Job terminé')
return
self.info(u'Téléchargement %s' % self.url)
self.info('Téléchargement %s' % self.url)
try:
soup = BeautifulSoup(requests.get(self.url).content, 'html5lib')
except:
self.error(u'Téléchargement %s impossible' % self.url)
self.update_status(u'error:download-html')
except Exception:
self.error('Téléchargement %s impossible' % self.url)
self.update_status('error:download-html')
return
def match_link(a):
......@@ -198,34 +185,34 @@ class BaseANJob(BaseJob):
try:
link = [a for a in soup.select('a[href]') if match_link(a)][0]
except:
self.error(u'Lien vers dump .json.zip introuvable')
self.update_status(u'error:zip-link')
except Exception:
self.error('Lien vers dump .json.zip introuvable')
self.update_status('error:zip-link')
return
jsonzip_url = link['href']
if jsonzip_url.startswith('/'):
jsonzip_url = '%s%s' % (self.base_url, jsonzip_url)
self.info(u'URL JSON zippé : %s' % jsonzip_url)
self.info('URL JSON zippé : %s' % jsonzip_url)
try:
lastmod = requests.head(jsonzip_url).headers['Last-Modified']
except:
self.error(u'Date du dump .json.zip introuvable')
self.update_status(u'error:zip-lastmod')
except Exception:
self.error('Date du dump .json.zip introuvable')
self.update_status('error:zip-lastmod')
return
self.info(u'Date modification dump .json.zip: %s' % lastmod)
self.info('Date modification dump .json.zip: %s' % lastmod)
jsonzip_lmd = dateparser.parse(lastmod)
if not ignore_lmd:
if self.job.date_fichier and self.job.date_fichier >= jsonzip_lmd:
self.info(u'Dump .json.zip non modifié')
self.update_status(u'ok')
self.info('Dump .json.zip non modifié')
self.update_status('ok')
return
self.info(u'Téléchargement .json.zip')
self.info('Téléchargement .json.zip')
localzip = os.path.join(self.app.config['DATA_DIR'],
os.path.basename(jsonzip_url))
......@@ -235,30 +222,30 @@ class BaseANJob(BaseJob):
r = requests.get(jsonzip_url, stream=True)
for block in r.iter_content(1024):
out.write(block)
except:
self.error(u'Téléchargement .json.zip')
self.update_status(u'error:zip-download')
except Exception:
self.error('Téléchargement .json.zip')
self.update_status('error:zip-download')
return
try:
with ZipFile(localzip, 'r') as z:
for f in [f for f in z.namelist() if f.endswith('.json')]:
self.info(u'JSON extrait : %s' % f)
self.info('JSON extrait : %s' % f)
with z.open(f) as zf:
if not self.handle_json(f, zf):
return
except Exception, e:
except Exception as e:
if self.current:
msg = u'Erreur (%s)' % self.current
msg = 'Erreur (%s)' % self.current
else:
msg = u'Erreur'
stack = u''.join(traceback.format_exc())
self.error(u'%s: %s\n%s' % (msg, e, stack))
self.error(u'Ouverture ZIP impossible')
self.update_status(u'error:zip-open')
msg = 'Erreur'
stack = ''.join(traceback.format_exc())
self.error('%s: %s\n%s' % (msg, e, stack))
self.error('Ouverture ZIP impossible')
self.update_status('error:zip-open')
return
self.info(u'Job terminé')
self.update_status(u'ok', jsonzip_url, jsonzip_lmd)
self.info('Job terminé')
self.update_status('ok', jsonzip_url, jsonzip_lmd)
db.session.flush()
# -*- coding: utf-8 -*-
from .database import db
from .database import db # NOQA
......@@ -400,7 +400,8 @@ class Amendement(db.Model):
backref=db.backref('amendement_parent',
remote_side=[id]))
signataires = db.relationship('ActeurAmendement', back_populates='amendement')
signataires = db.relationship('ActeurAmendement',
back_populates='amendement')
search_vector = db.Column(TSVectorType('numero_long', 'corps_expose',
'corps_dispositif', 'code_loi',
......
......@@ -23,4 +23,3 @@ class Job(db.Model):
resultat = db.Column(db.Unicode)
search_vector = db.Column(TSVectorType('nom', 'url_fichier'))
# -*- coding: utf-8 -*-
import collections
from copy import copy
from datetime import datetime
from flask import abort, json, jsonify, request
from flask import abort, jsonify, request
from sqlalchemy.inspection import inspect
from sqlalchemy.sql import not_
......@@ -18,10 +16,11 @@ def parse_datetime(string):
'%Y-%m-%dT%H:%M:%S'
]
exc = None
for f in formats:
try:
return datetime.strptime(string, f)
except ValueError, exc:
except ValueError as exc:
pass
raise exc
......@@ -72,7 +71,7 @@ class Endpoint(object):
return Paginated
def _make_error(self, message, code=400):
res = jsonify({ '_error': message, '_code':