Commit 7e0f16a3 authored by Nicolas Joyard's avatar Nicolas Joyard

Suppression etapes en base

parent fbd8ac2f
......@@ -16,7 +16,6 @@ $ pip install -e .
$ sudo -u postgres psql -c "create user irfm with password 'irfm';"
$ sudo -u postgres psql -c "create database irfm with owner irfm;"
$ irfm db upgrade
$ irfm import_etapes
$ irfm import_nd
$ irfm import_adresses
```
......
......@@ -8,7 +8,6 @@ from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from .importers.adresses import AdressesImporter
from .importers.etapes import EtapesImporter
from .importers.nosdeputes import NosDeputesImporter
from .irfm import app
......@@ -80,13 +79,6 @@ def generer_demandes():
print('Aucun parlementaire sans adresse :)')
@manager.command
def import_etapes():
"""Crée ou met à jour la liste des étapes"""
app.config.update(SQLALCHEMY_ECHO=False)
EtapesImporter(app).run()
@manager.command
def import_nd():
"""Importe les députés depuis NosDéputés.fr"""
......
......@@ -5,7 +5,7 @@ from bs4 import BeautifulSoup
import requests
from .base import BaseImporter
from ..models import Etape, Parlementaire, db
from ..models import Parlementaire, db
from ..models.constants import ETAPE_NA
......@@ -80,8 +80,7 @@ class AdressesImporter(BaseImporter):
def run(self):
self.info('Début import adresses')
qs = Parlementaire.query.join(Parlementaire.etape) \
.filter(Etape.ordre > ETAPE_NA) \
qs = Parlementaire.query.filter(Parlementaire.etape > ETAPE_NA) \
.filter(Parlementaire.adresse is None) \
.order_by(Parlementaire.nom) \
.all()
......
# -*- coding: utf-8 -*-
from .base import BaseImporter
from ..models import Etape, db
from ..models.constants import ETAPES
class EtapesImporter(BaseImporter):
def import_etape(self, data):
created = False
updated = False
id_data = {'ordre': data['ordre']}
etape = Etape.query.filter_by(**id_data).first()
if not etape:
etape = Etape(**id_data)
db.session.add(etape)
created = True
for key, newvalue in data.items():
if key == 'ordre':
continue
curvalue = getattr(etape, key)
if curvalue != newvalue:
updated = True
setattr(etape, key, newvalue)
return created, updated
def run(self):
self.info('Début import étapes')
self.info('%s étapes trouvés' % len(ETAPES))
created = 0
updated = 0
ordres = []
for etape in ETAPES:
ordres.append(etape['ordre'])
c, u = self.import_etape(etape)
if c:
created += 1
elif u:
updated += 1
deleted = 0
for etape in Etape.query.all():
if etape.ordre not in ordres:
db.session.delete(etape)
deleted += 1
db.session.commit()
self.info('Import étapes terminé: %s créées, %s mises à jour, '
'%s supprimées' % (created, updated, deleted))
......@@ -11,7 +11,7 @@ from sqlalchemy.inspection import inspect
from .base import BaseImporter
from ..models import Etape, Groupe, Parlementaire, db
from ..models import Groupe, Parlementaire, db
from ..models.constants import DEBUT_RELEVES, ETAPE_A_ENVOYER, ETAPE_NA
......@@ -79,7 +79,7 @@ class NosDeputesImporter(BaseImporter):
depute = Parlementaire.query.filter_by(**id_data).first()
if not depute:
id_data.update({'etape': self.etape_ae})
id_data.update({'etape': ETAPE_A_ENVOYER})
depute = Parlementaire(**id_data)
db.session.add(depute)
created = True
......@@ -106,7 +106,7 @@ class NosDeputesImporter(BaseImporter):
}
if fields['nom_complet'] in IGNORER:
fields['etape'] = self.etape_na
fields['etape'] = ETAPE_NA
if fields['mandat_fin']:
fin = fields['mandat_fin']
......@@ -115,7 +115,7 @@ class NosDeputesImporter(BaseImporter):
if fin < DEBUT_RELEVES:
# Mandat terminé avant T - 6 mois => député non concerné
fields['etape'] = self.etape_na
fields['etape'] = ETAPE_NA
else:
self.info('Vérification décès pour %s' % fields['nom_complet'])
try:
......@@ -132,12 +132,12 @@ class NosDeputesImporter(BaseImporter):
if deces:
self.info('%s est décédé(e)' % fields['nom_complet'])
fields['etape'] = self.etape_na
elif not created and depute.etape == self.etape_na:
fields['etape'] = ETAPE_NA
elif not created and depute.etape == ETAPE_A_ENVOYER:
# Gestion des députés précédemment marqués comme non
# concernés
self.info('Député concerné : %s' % fields['nom_complet'])
fields['etape'] = self.etape_ae
fields['etape'] = ETAPE_A_ENVOYER
for key, newvalue in fields.items():
curvalue = getattr(depute, key)
......@@ -153,16 +153,6 @@ class NosDeputesImporter(BaseImporter):
return created, updated
def import_deputes(self):
self.etape_na = Etape.query.filter_by(ordre=ETAPE_NA).first()
if not self.etape_na:
self.error('Etape N/A introuvable, exécuter import_etapes ?')
return
self.etape_ae = Etape.query.filter_by(ordre=ETAPE_A_ENVOYER).first()
if not self.etape_ae:
self.error('Etape À envoyer introuvable, exécuter import_etapes ?')
return
try:
data = requests.get(self.URL_DEPUTES).json()
except Exception as e:
......
......@@ -2,5 +2,5 @@
from .database import db # noqa
from .parlementaire import Groupe, Parlementaire # noqa
from .procedure import Action, Etape # noqa
from .procedure import Action # noqa
from .user import User # noqa
......@@ -43,9 +43,7 @@ class Parlementaire(db.Model):
url_rc = db.Column(db.Unicode)
url_off = db.Column(db.Unicode)
etape_id = db.Column(db.Integer, db.ForeignKey('etapes.id'))
etape = db.relationship('Etape', back_populates='parlementaires')
etape = db.Column(db.Integer)
actions = db.relationship('Action', back_populates='parlementaire',
order_by='Action.date')
......
......@@ -3,20 +3,6 @@
from .database import db
class Etape(db.Model):
__tablename__ = 'etapes'
id = db.Column(db.Integer, primary_key=True)
ordre = db.Column(db.Integer)
label = db.Column(db.Unicode)
description = db.Column(db.Unicode)
couleur = db.Column(db.Unicode)
icone = db.Column(db.Unicode)
parlementaires = db.relationship('Parlementaire', back_populates='etape')
class Action(db.Model):
__tablename__ = 'actions'
......@@ -28,8 +14,7 @@ class Action(db.Model):
suivi = db.Column(db.Unicode)
attachment = db.Column(db.Unicode)
etape_id = db.Column(db.Integer, db.ForeignKey('etapes.id'))
etape = db.relationship('Etape')
etape = db.Column(db.Integer)
parlementaire_id = db.Column(db.Integer,
db.ForeignKey('parlementaires.id'))
......
......@@ -7,9 +7,10 @@ from flask import (make_response, redirect, render_template, request, session,
url_for)
from sqlalchemy.orm import joinedload
from ..models import Action, Etape, Parlementaire, User, db
from ..models import Action, Parlementaire, User, db
from ..models.constants import (ETAPE_A_CONFIRMER, ETAPE_A_ENVOYER,
ETAPE_COM_A_MODERER, ETAPE_COM_PUBLIE)
ETAPES_BY_ORDRE, ETAPE_COM_A_MODERER,
ETAPE_COM_PUBLIE)
from ..tools.files import EXTENSIONS, handle_upload
from ..tools.routing import (not_found, redirect_back, remote_addr,
......@@ -23,9 +24,7 @@ def setup_routes(app):
@require_admin
def admin_recent():
# Les 500 actions les plus récentes
qs = Action.query.options(joinedload(Action.parlementaire)
.joinedload(Parlementaire.etape)) \
.options(joinedload(Action.etape)) \
qs = Action.query.options(joinedload(Action.parlementaire)) \
.options(joinedload(Action.user)) \
.order_by(Action.date.desc()) \
.limit(500) \
......@@ -40,14 +39,12 @@ def setup_routes(app):
def admin_en_attente():
# Sous requête des parlementaires à l'étape "à confirmer"
parls = db.session.query(Parlementaire.id) \
.join(Parlementaire.etape) \
.filter(Etape.ordre == ETAPE_A_CONFIRMER) \
.filter(Parlementaire.etape == ETAPE_A_CONFIRMER) \
.subquery()
# Actions "à confirmer" pour ces parlementaires
qs = Action.query.join(Action.etape) \
.filter(Action.parlementaire_id.in_(parls)) \
.filter(Etape.ordre == ETAPE_A_CONFIRMER) \
qs = Action.query.filter(Action.parlementaire_id.in_(parls)) \
.filter(Action.etape == ETAPE_A_CONFIRMER) \
.options(joinedload(Action.user)) \
.order_by(Action.date) \
.all()
......@@ -59,8 +56,7 @@ def setup_routes(app):
@app.route('/admin/commentaires', endpoint='admin_commentaires')
@require_admin
def admin_commentaires():
qs = Action.query.join(Action.etape) \
.filter(Etape.ordre == ETAPE_COM_A_MODERER) \
qs = Action.query.filter(Action.etape == ETAPE_COM_A_MODERER) \
.options(joinedload(Action.parlementaire)) \
.options(joinedload(Action.user)) \
.order_by(Action.date) \
......@@ -80,15 +76,14 @@ def setup_routes(app):
db.session.flush()
last_action = Action.query \
.join(Action.etape) \
.filter(Action.parlementaire_id == parl_id) \
.order_by(Etape.ordre.desc()) \
.order_by(Action.etape.desc()) \
.first()
if last_action and last_action.etape.ordre > ETAPE_A_ENVOYER:
if last_action and last_action.etape > ETAPE_A_ENVOYER:
etape = last_action.etape
else:
etape = Etape.query.filter_by(ordre=ETAPE_A_ENVOYER).first()
etape = ETAPE_A_ENVOYER
parl = Parlementaire.query.filter_by(id=parl_id).first()
parl.etape = etape
......@@ -101,15 +96,12 @@ def setup_routes(app):
@require_admin
def admin_publish(id):
action = Action.query \
.join(Action.etape) \
.filter(Etape.ordre == ETAPE_COM_A_MODERER) \
.filter(Action.etape == ETAPE_COM_A_MODERER) \
.filter(Action.id == id) \
.first()
if action:
action.etape = Etape.query \
.filter(Etape.ordre == ETAPE_COM_PUBLIE) \
.one()
action.etape = ETAPE_COM_PUBLIE
db.session.commit()
return redirect_back()
......@@ -138,16 +130,20 @@ def setup_routes(app):
if not parl:
return not_found()
etape = Etape.query.filter_by(id=request.form['etape']).first()
if not etape:
msg = 'Etape inconnue !?'
try:
etape = int(request.form['etape'])
except ValueError:
etape = None
if etape is None or etape not in ETAPES_BY_ORDRE:
msg = 'Etape inconnue.'
return redirect_back(error=msg,
fallback=url_for('parlementaire', id=id_parl))
try:
filename = handle_upload(
os.path.join(app.config['DATA_DIR'], 'uploads'),
'etape-%s-%s' % (etape.ordre, slugify(parl.nom_complet))
'etape-%s-%s' % (etape, slugify(parl.nom_complet))
)
except Exception as e:
return redirect_back(error=str(e),
......
......@@ -2,7 +2,7 @@
from flask import session, url_for
from ..models import Action, Etape
from ..models import Action
from ..models.constants import (CHAMBRES, ETAPES, ETAPES_BY_ORDRE,
ETAPE_AR_RECU, ETAPE_A_CONFIRMER,
ETAPE_A_ENVOYER, ETAPE_COM_A_MODERER,
......@@ -70,8 +70,7 @@ def setup(app):
]
nb_moderer = Action.query \
.join(Action.etape) \
.filter(Etape.ordre == ETAPE_COM_A_MODERER) \
.filter(Action.etape == ETAPE_COM_A_MODERER) \
.count()
if nb_moderer > 0:
......
......@@ -4,7 +4,7 @@ import os
from flask import redirect, send_from_directory, url_for
from ..models import Action, Etape, Parlementaire
from ..models import Action, Parlementaire
from ..models.constants import ETAPE_ENVOYE
from ..tools.files import generer_demande
......@@ -32,9 +32,8 @@ def setup_routes(app):
@app.route('/parlementaire/<id>/preuve-envoi', endpoint='preuve_envoi')
def preuve_envoi(id):
act = Action.query.join(Action.etape) \
.filter(Etape.ordre == ETAPE_ENVOYE,
Action.parlementaire_id == id) \
act = Action.query.filter(Action.etape == ETAPE_ENVOYE) \
.filter(Action.parlementaire_id == id) \
.first()
if not act or not act.attachment:
......
......@@ -6,6 +6,8 @@ from flask import url_for
from jinja2 import Markup, escape, evalcontextfilter
from ..models.constants import ETAPES_BY_ORDRE
def setup(app):
......@@ -70,26 +72,20 @@ def setup(app):
@app.template_filter('label_etape')
def label_etape(etape):
if isinstance(etape, dict):
data = (etape['description'], etape['couleur'], etape['icone'],
etape['label'])
else:
data = (etape.description, etape.couleur, etape.icone, etape.label)
if isinstance(etape, int):
etape = ETAPES_BY_ORDRE[etape]
return '<span class="label" title="%s" ' \
'data-toggle="tooltip" ' \
'style="background-color: %s;"><i class="fa fa-%s"></i> ' \
'%s</span>' % data
return ('<span class="label" title="%(description)s" '
'data-toggle="tooltip" style="background-color: %(couleur)s;">'
'<i class="fa fa-%(icone)s"></i> %(label)s</span>') % etape
@app.template_filter('label_etape_text')
def label_etape_text(etape):
if isinstance(etape, dict):
data = (etape['icone'], etape['label'])
else:
data = (etape.icone, etape.label)
if isinstance(etape, int):
etape = ETAPES_BY_ORDRE[etape]
return '<span data-toggle="tooltip"><i class="fa fa-%s"></i> ' \
'%s</span>' % data
return ('<span data-toggle="tooltip"><i class="fa fa-%(icone)s"></i> '
'%(label)s</span>') % etape
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
......
......@@ -4,8 +4,9 @@ from flask import render_template
from sqlalchemy.sql.expression import case, func
from ..models import Etape, Parlementaire, db
from ..models.constants import ETAPES, ETAPE_A_ENVOYER, ETAPE_ENVOYE, ETAPE_NA
from ..models import Parlementaire, db
from ..models.constants import (ETAPES, ETAPES_BY_ORDRE, ETAPE_A_ENVOYER,
ETAPE_ENVOYE, ETAPE_NA)
def setup_routes(app):
......@@ -13,37 +14,35 @@ def setup_routes(app):
@app.route('/', endpoint='home')
def home():
# Un parlementaire à l'étape "à envoyer" au hasard
parl = Parlementaire.query.join(Parlementaire.etape) \
.filter(Etape.ordre == ETAPE_A_ENVOYER) \
.order_by(func.random()) \
.first()
parl = Parlementaire.query \
.filter(Parlementaire.etape == ETAPE_A_ENVOYER) \
.order_by(func.random()) \
.first()
# Toutes les étapes avec le nombre de parlementaires à cette étape
etapes_qs = db.session.query(Etape) \
.outerjoin(Etape.parlementaires) \
.add_columns(func.count(Parlementaire.id)
.label('nb')) \
.filter(Etape.ordre > ETAPE_NA) \
.group_by(Etape) \
.order_by(Etape.ordre) \
.all()
# Comptage des parlementaires par département
etapes_qs = db.session \
.query(Parlementaire.etape,
func.count(Parlementaire.id).label('nb')) \
.filter(Parlementaire.etape > ETAPE_NA) \
.group_by(Parlementaire.etape) \
.order_by(Parlementaire.etape) \
.all()
# Comptage des parlementaires par département...
dept_qs = db.session \
.query(Parlementaire.num_deptmt,
func.count(Parlementaire.id).label('total')) \
.join(Etape, Parlementaire.etape_id == Etape.id)
func.count(Parlementaire.id).label('total'))
# ...et par étape
dept_qs = dept_qs.add_columns(*[
func.sum(case([(Etape.ordre == e['ordre'], 1)], else_=0))
func.sum(case([(Parlementaire.etape == e['ordre'], 1)], else_=0))
.label('nb_etape_%s' % e['ordre'])
for e in ETAPES
])
# ...et qui sont dans une étape >= envoyé
dept_qs = dept_qs.add_columns(
func.sum(case([(Etape.ordre >= ETAPE_ENVOYE, 1)], else_=0))
func.sum(case([(Parlementaire.etape >= ETAPE_ENVOYE, 1)], else_=0))
.label('nb_envoyes')
)
......@@ -51,18 +50,22 @@ def setup_routes(app):
.order_by(Parlementaire.num_deptmt) \
.all()
def for_nz(getter):
return [getter(e) for e in etapes_qs if e.nb > 0]
def key_for_nz(key):
return for_nz(lambda e: ETAPES_BY_ORDRE[e.etape][key])
return render_template(
'index.html.j2',
parlementaire=parl,
etapes_data={
'labels': [e.Etape.label for e in etapes_qs if e.nb > 0],
'labels': key_for_nz('label'),
'datasets': [{
'data': [e.nb for e in etapes_qs if e.nb > 0],
'backgroundColor': [e.Etape.couleur for e in etapes_qs
if e.nb > 0],
'hoverBackgroundColor': [e.Etape.couleur for e in etapes_qs
if e.nb > 0],
'borderWidth': [0 for e in etapes_qs if e.nb > 0]
'data': for_nz(lambda e: e.nb),
'backgroundColor': key_for_nz('couleur'),
'hoverBackgroundColor': key_for_nz('couleur'),
'borderWidth': for_nz(lambda e: 0)
}]
},
departements=dept_qs
......
......@@ -7,9 +7,9 @@ from flask import (flash, redirect, render_template, request, session, url_for)
from flask_mail import Mail, Message
from sqlalchemy.orm import contains_eager, joinedload
from sqlalchemy.orm import joinedload
from ..models import Action, Etape, Parlementaire, User, db
from ..models import Action, Parlementaire, User, db
from ..models.constants import (ETAPE_A_CONFIRMER, ETAPE_A_ENVOYER,
ETAPE_COM_A_MODERER, ETAPE_COM_PUBLIE,
ETAPE_ENVOYE)
......@@ -24,13 +24,13 @@ def pris_en_charge(parl, force=False):
bien à l'étape "à confirmer".
Renvoie l'action correspondante ou None
"""
if session.get('user') and parl.etape.ordre == ETAPE_A_CONFIRMER:
if session.get('user') and parl.etape == ETAPE_A_CONFIRMER:
if force:
action = [a for a in parl.actions
if a.etape.ordre == ETAPE_A_CONFIRMER]
if a.etape == ETAPE_A_CONFIRMER]
else:
action = [a for a in parl.actions
if a.etape.ordre == ETAPE_A_CONFIRMER and
if a.etape == ETAPE_A_CONFIRMER and
a.user_id == session.get('user')['id']]
if len(action):
......@@ -45,10 +45,8 @@ def setup_routes(app):
@app.route('/parlementaires', endpoint='parlementaires')
def parlementaires():
qs = Parlementaire.query.join(Parlementaire.etape) \
.options(joinedload(Parlementaire.groupe)) \
.options(contains_eager(Parlementaire.etape)) \
.filter(Etape.ordre > 0) \
qs = Parlementaire.query.options(joinedload(Parlementaire.groupe)) \
.filter(Parlementaire.etape > 0) \
.all()
return render_template(
......@@ -61,7 +59,6 @@ def setup_routes(app):
parl = Parlementaire.query \
.filter_by(id=id) \
.options(joinedload(Parlementaire.groupe)) \
.options(joinedload(Parlementaire.etape)) \
.options(joinedload(Parlementaire.actions)
.joinedload(Action.user)) \
.first()
......@@ -78,10 +75,9 @@ def setup_routes(app):
.first()
abonne = parl in user.abonnements
dept = Parlementaire.query.join(Parlementaire.etape) \
.filter(Parlementaire.num_deptmt ==
dept = Parlementaire.query.filter(Parlementaire.num_deptmt ==
parl.num_deptmt) \
.filter(Etape.ordre > 0) \
.filter(Parlementaire.etape > 0) \
.all()
abonne_dept = all([p in user.abonnements for p in dept])
......@@ -92,13 +88,6 @@ def setup_routes(app):
'pris_en_charge': bool(pris_en_charge(parl))
}
if session.get('user') and session['user']['admin']:
etapes = Etape.query.filter(Etape.ordre > parl.etape.ordre) \
.order_by(Etape.ordre) \
.all()
data['next_etapes'] = etapes
return render_template('parlementaire.html.j2', **data)
@app.route('/parlementaires/<id>/envoi', endpoint='envoi')
......@@ -116,12 +105,12 @@ def setup_routes(app):
if not parl:
return not_found()
if parl.etape.ordre != ETAPE_A_ENVOYER:
if parl.etape != ETAPE_A_ENVOYER:
msg = 'Oups, la situation a changé pour ce parlementaire...'
return redirect_back(error=msg,
fallback=url_for('parlementaire', id=id))
parl.etape = Etape.query.filter_by(ordre=ETAPE_A_CONFIRMER).first()
parl.etape = ETAPE_A_CONFIRMER
action = Action(
date=datetime.now(),
......@@ -158,7 +147,6 @@ def setup_routes(app):
def annuler(id):
parl = Parlementaire.query.filter_by(id=id) \
.options(joinedload(Parlementaire.groupe)) \
.options(joinedload(Parlementaire.etape)) \
.options(joinedload(Parlementaire.actions)) \
.first()
......@@ -178,7 +166,7 @@ def setup_routes(app):
return redirect_back(error=msg,
fallback=url_for('parlementaire', id=id))
parl.etape = Etape.query.filter_by(ordre=ETAPE_A_ENVOYER).first()
parl.etape = ETAPE_A_ENVOYER
db.session.delete(action)
db.session.commit()
......@@ -190,7 +178,6 @@ def setup_routes(app):
def confirmer(id):
parl = Parlementaire.query.filter_by(id=id) \
.options(joinedload(Parlementaire.groupe)) \
.options(joinedload(Parlementaire.etape)) \
.options(joinedload(Parlementaire.actions)) \
.first()
......@@ -224,7 +211,7 @@ def setup_routes(app):
return redirect_back(error=str(e),
fallback=url_for('parlementaire', id=id))
parl.etape = Etape.query.filter_by(ordre=ETAPE_ENVOYE).first()
parl.etape = ETAPE_ENVOYE
action = Action(
date=datetime.now(),
......@@ -256,11 +243,10 @@ def setup_routes(app):
fallback=url_for('parlementaire', id=id))
if session.get('user') and session['user']['admin']:
ordre = ETAPE_COM_PUBLIE
etape = ETAPE_COM_PUBLIE
else:
ordre = ETAPE_COM_A_MODERER
etape = ETAPE_COM_A_MODERER
etape = Etape.query.filter(Etape.ordre == ordre).one()
action = Action(
date=datetime.now(),
user=User.query.filter(User.id == session['user']['id'])
......
......@@ -4,7 +4,7 @@ from flask import flash, render_template, request, session
from sqlalchemy.orm import joinedload
from ..models import Action, Etape, Parlementaire, User, db
from ..models import Action, Parlementaire, User, db
from ..models.constants import ETAPE_ENVOYE
from ..tools.routing import not_found, redirect_back, require_user, url_for
......@@ -100,9 +100,8 @@ def setup_routes(app):
if not user:
return not_found()
envois = Action.query.join(Action.etape) \
.filter(Action.user == user) \
.filter(Etape.ordre == ETAPE_ENVOYE) \
envois = Action.query.filter(Action.user == user) \
.filter(Action.etape == ETAPE_ENVOYE) \
.count()
if request.method == 'POST':
......@@ -146,9 +145,8 @@ def setup_routes(app):
@require_user
def abo_departement(deptmt, action):
user = User.query.filter(User.id == session['user']['id']).first()
parl = Parlementaire.query.join(Parlementaire.etape) \
.filter(Parlementaire.num_deptmt == deptmt) \
.filter(Etape.ordre > 0) \
parl = Parlementaire.query.filter(Parlementaire.num_deptmt == deptmt) \
.filter(Parlementaire.etape > 0) \
.all()
if not user:
......
......@@ -38,7 +38,7 @@
{% endif %}
</td>
<td>
{% if action.suivi and action.etape.ordre == ordres.ETAPE_ENVOYE %}
{% if action.suivi and action.etape == ordres.ETAPE_ENVOYE %}
<a href="http://www.part.csuivi.courrier.laposte.fr/suivi/index?id={{ action.suivi }}" target="_blank">{{ action.suivi }}</a>
{% elif action.suivi %}
{{ action.suivi|e }}
......
......@@ -41,7 +41,7 @@
{% endif %}
</td>
<td>
{% if action.suivi and action.etape.ordre == ordres.ETAPE_ENVOYE %}
{% if action.suivi and action.etape == ordres.ETAPE_ENVOYE %}
<a href="http://www.part.csuivi.courrier.laposte.fr/suivi/index?id={{ action.suivi }}" target="_blank">{{ action.suivi }}</a>
{% elif action.suivi %}