Commit 40326940 authored by Nicolas Joyard's avatar Nicolas Joyard

Ajout import députés et page de liste

parent 95a78cc8
## Prérequis
* Python 3
* Python 3 + headers (ie. python3-dev)
* virtualenvwrapper
* PostgreSQL
......
......@@ -3,6 +3,7 @@
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from .importers.nosdeputes import NosDeputesImporter
from .irfm import app
from .models import db
......@@ -19,3 +20,8 @@ def runserver():
"""Exécute le serveur web flask intégré"""
app.run()
@manager.command
def import_nd():
"""Importe les députés depuis NosDéputés.fr"""
NosDeputesImporter(app).run()
# -*- coding: utf-8 -*-
from datetime import datetime
import dateparser
import requests
from sqlalchemy.inspection import inspect
from ..models import db, Parlementaire
def parse_date(date):
if not date:
return None
elif len(date) >= 19:
return dateparser.parse(date[0:19])
else:
return dateparser.parse(date[0:10])
class NosDeputesImporter(object):
URL_LISTE = 'https://www.nosdeputes.fr/deputes/json'
URL_PHOTO = '//www.nosdeputes.fr/depute/photo/%(slug)s'
columns = None
def __init__(self, app):
self.app = app
def info(self, msg):
self.app.logger.info(u'<%s> %s' % (self.__class__.__name__, msg))
def error(self, msg):
self.app.logger.error(u'<%s> %s' % (self.__class__.__name__, msg))
def import_depute(self, data):
if not self.columns:
mapper = inspect(Parlementaire)
self.columns = {c.name: type(c.type).__name__
for c in mapper.columns}
cols = self.columns
created = False
updated = False
id_data = {
'chambre': 'AN',
'nom': data['nom_de_famille'],
'prenom': data['prenom'],
}
depute = Parlementaire.query.filter_by(**id_data).first()
if not depute:
id_data.update({'etape': 'NOUVEAU'})
depute = Parlementaire(**id_data)
db.session.add(depute)
created = True
fields = {
'sexe': data['sexe'],
'mandat_debut': parse_date(data['mandat_debut']),
'mandat_fin': parse_date(data.get('mandat_fin', None)),
'num_deptmt': data['num_deptmt'],
'nom_circo': data['nom_circo'],
'num_circo': data['num_circo'],
'groupe': data['parti_ratt_financier'],
'groupe_sigle': data['groupe_sigle'],
'url_photo': self.URL_PHOTO % data,
'url_rc': data['url_nosdeputes'],
'url_off': data['url_an'],
}
for key, newvalue in fields.items():
curvalue = getattr(depute, key)
if key in cols:
if cols[key] == 'Date' and isinstance(newvalue, datetime):
newvalue = newvalue.date()
if curvalue != newvalue:
updated = True
setattr(depute, key, newvalue)
return created, updated
def run(self):
self.info('Début import NosDéputés.fr')
try:
data = requests.get(self.URL_LISTE).json()
except Exception as e:
self.error('Téléchargement %s impossible: %s' % (URL_LISTE, e))
return
self.info('%s députés trouvés' % len(data['deputes']))
created = 0
updated = 0
for depute in data['deputes']:
c, u = self.import_depute(depute['depute'])
if c:
created += 1
elif u:
updated += 1
db.session.commit()
db.session.flush()
self.info('Import terminé: %s créés, %s mis à jour' % (created,
updated))
......@@ -21,7 +21,7 @@ class Parlementaire(db.Model):
mandat_debut = db.Column(db.DateTime)
mandat_fin = db.Column(db.DateTime)
num_deptmt = db.Column(db.Integer)
num_deptmt = db.Column(db.Unicode)
nom_circo = db.Column(db.Unicode)
num_circo = db.Column(db.Integer)
groupe = db.Column(db.Unicode)
......@@ -31,4 +31,4 @@ class Parlementaire(db.Model):
url_rc = db.Column(db.Unicode)
url_off = db.Column(db.Unicode)
etat = db.Column(db.Enum(*ETAPES.keys(), name='etapes'))
etape = db.Column(db.Enum(*ETAPES.keys(), name='etapes'))
table.sortable span.sign {
display: block;
position: absolute;
top: 50%;
right: 5px;
font-size: 12px;
margin-top: -10px;
color: #bfbfc1;
}
table.sortable th:after {
display: block;
position: absolute;
top: 50%;
right: 5px;
font-size: 12px;
margin-top: -10px;
color: #bfbfc1;
}
table.sortable th.arrow:after {
content: '';
}
table.sortable span.arrow, span.reversed, th.arrow.down:after, th.reversedarrow.down:after, th.arrow.up:after, th.reversedarrow.up:after {
border-style: solid;
border-width: 5px;
font-size: 0;
border-color: #ccc transparent transparent transparent;
line-height: 0;
height: 0;
width: 0;
margin-top: -2px;
}
table.sortable span.arrow.up, th.arrow.up:after {
border-color: transparent transparent #ccc transparent;
margin-top: -7px;
}
table.sortable span.reversed, th.reversedarrow.down:after {
border-color: transparent transparent #ccc transparent;
margin-top: -7px;
}
table.sortable span.reversed.up, th.reversedarrow.up:after {
border-color: #ccc transparent transparent transparent;
margin-top: -2px;
}
table.sortable span.az:before, th.az.down:after {
content: "a .. z";
}
table.sortable span.az.up:before, th.az.up:after {
content: "z .. a";
}
table.sortable th.az.nosort:after, th.AZ.nosort:after, th._19.nosort:after, th.month.nosort:after {
content: "..";
}
table.sortable span.AZ:before, th.AZ.down:after {
content: "A .. Z";
}
table.sortable span.AZ.up:before, th.AZ.up:after {
content: "Z .. A";
}
table.sortable span._19:before, th._19.down:after {
content: "1 .. 9";
}
table.sortable span._19.up:before, th._19.up:after {
content: "9 .. 1";
}
table.sortable span.month:before, th.month.down:after {
content: "jan .. dec";
}
table.sortable span.month.up:before, th.month.up:after {
content: "dec .. jan";
}
table.sortable thead th:not([data-defaultsort=disabled]) {
cursor: pointer;
position: relative;
top: 0;
left: 0;
}
table.sortable thead th:hover:not([data-defaultsort=disabled]) {
background: #efefef;
}
table.sortable thead th div.mozilla {
position: relative;
}
This diff is collapsed.
This diff is collapsed.
.chamber-icon {
height: 1.5em;
}
<!doctype html>
<head>
<!DOCTYPE html>
<head lang="fr">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Transparence IRFM</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />
<!-- <link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}" /> -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
{% block header %}
{% endblock %}
</head>
<body>
<section class="container-fluid">
......@@ -54,6 +61,9 @@
</small>
</footer>
{% block scripts %}
{% endblock %}
{% if piwik %}
<!-- Piwik -->
<script type="text/javascript">
......
......@@ -3,32 +3,51 @@
{% block content %}
<section class="panel panel-default">
<header class="panel-heading">
<b>Liste des parlementaires</b>
<b>Liste des {{ parlementaires|length }} parlementaires</b>
</header>
<table class="table table-striped">
{% for parl in parlementaires %}
{% if loop.first %}
<tr>
<th>Parlementaire</th>
<th>Etape</th>
</tr>
{% endif %}
<table class="table table-condensed table-striped sortable">
<thead>
<tr>
<th>{{ parl.nom }}</th>
<th>{{ parl.etat }}</th>
<th colspan="2" data-defaultsort="asc" data-mainsort="1">Parlementaire</th>
<th>Groupe</th>
<th>Circonscription</th>
<th>Etape</th>
</tr>
</thead>
<tbody>
{% for parl in parlementaires %}
{% else %}
<tr>
<td>
<img class="chamber-icon" src="{{ url_for('static', filename=parl.chambre|lower+'.png') }}">
</td>
<td data-value="{{ parl.nom }} {{ parl.prenom }}">{{ parl.prenom }} {{ parl.nom }}</td>
<td>{{ parl.groupe_sigle }}</td>
<td data-value="{{ parl.num_deptmt }} {{ parl.num_circo }}">{{ parl.nom_circo }} n°{{ parl.num_circo }}</td>
<td>{{ parl.etape }}</td>
</tr>
<tr>
<td class="warning" colspan="2">
<em>Aucun parlementaire trouvé :(</em>
</td>
</tr>
{% else %}
{% endfor %}
<tr>
<td class="warning" colspan="5">
<em>Aucun parlementaire trouvé :(</em>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endblock %}
{% block header %}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='bootstrap-sortable.css') }}">
{% endblock %}
{% block scripts %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="{{ url_for('static', filename='moment.min.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap-sortable.js') }}"></script>
{% endblock %}
"""Initialisation
Revision ID: eef599a4fb63
Revision ID: 8d32ba048444
Revises:
Create Date: 2017-05-05 21:23:01.220831
Create Date: 2017-05-05 23:06:35.469060
"""
from alembic import op
......@@ -10,7 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'eef599a4fb63'
revision = '8d32ba048444'
down_revision = None
branch_labels = None
depends_on = None
......@@ -27,7 +27,7 @@ def upgrade():
sa.Column('chambre', sa.Enum('SEN', 'AN', name='chambres'), nullable=True),
sa.Column('mandat_debut', sa.DateTime(), nullable=True),
sa.Column('mandat_fin', sa.DateTime(), nullable=True),
sa.Column('num_deptmt', sa.Integer(), nullable=True),
sa.Column('num_deptmt', sa.Unicode(), nullable=True),
sa.Column('nom_circo', sa.Unicode(), nullable=True),
sa.Column('num_circo', sa.Integer(), nullable=True),
sa.Column('groupe', sa.Unicode(), nullable=True),
......@@ -35,7 +35,7 @@ def upgrade():
sa.Column('url_photo', sa.Unicode(), nullable=True),
sa.Column('url_rc', sa.Unicode(), nullable=True),
sa.Column('url_off', sa.Unicode(), nullable=True),
sa.Column('etat', sa.Enum('NOUVEAU', name='etapes'), nullable=True),
sa.Column('etape', sa.Enum('NOUVEAU', name='etapes'), nullable=True),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment