Comptes utilisateur: option pour forcer modif mot de passe.

This commit is contained in:
Emmanuel Viennet 2024-11-01 11:58:58 +01:00
parent 83a5855f3d
commit d7f4209a5a
21 changed files with 408 additions and 269 deletions

View File

@ -38,7 +38,7 @@ def after_cas_login():
flask.session["scodoc_cas_login_date"] = ( flask.session["scodoc_cas_login_date"] = (
datetime.datetime.now().isoformat() datetime.datetime.now().isoformat()
) )
user.cas_last_login = datetime.datetime.utcnow() user.cas_last_login = datetime.datetime.now()
if flask.session.get("CAS_EDT_ID"): if flask.session.get("CAS_EDT_ID"):
# essaie de récupérer l'edt_id s'il est présent # essaie de récupérer l'edt_id s'il est présent
# cet ID peut être renvoyé par le CAS et extrait par ScoDoc # cet ID peut être renvoyé par le CAS et extrait par ScoDoc

View File

@ -9,8 +9,8 @@ from flask import current_app, g, redirect, request, url_for
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
import flask_login import flask_login
from app import db, login from app import db, log, login
from app.auth.models import User from app.auth.models import User, Role
from app.models.config import ScoDocSiteConfig from app.models.config import ScoDocSiteConfig
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
@ -19,7 +19,7 @@ token_auth = HTTPTokenAuth()
@basic_auth.verify_password @basic_auth.verify_password
def verify_password(username, password): def verify_password(username, password) -> User | None:
"""Verify password for this user """Verify password for this user
Appelé lors d'une demande de jeton (normalement via la route /tokens) Appelé lors d'une demande de jeton (normalement via la route /tokens)
""" """
@ -28,6 +28,7 @@ def verify_password(username, password):
g.current_user = user g.current_user = user
# note: est aussi basic_auth.current_user() # note: est aussi basic_auth.current_user()
return user return user
return None
@basic_auth.error_handler @basic_auth.error_handler
@ -61,7 +62,8 @@ def token_auth_error(status):
@token_auth.get_user_roles @token_auth.get_user_roles
def get_user_roles(user): def get_user_roles(user) -> list[Role]:
"list roles"
return user.roles return user.roles
@ -82,7 +84,7 @@ def load_user_from_request(req: flask.Request) -> User:
@login.unauthorized_handler @login.unauthorized_handler
def unauthorized(): def unauthorized():
"flask-login: si pas autorisé, redirige vers page login, sauf si API" "flask-login: si pas autorisé, redirige vers page login, sauf si API"
if request.blueprint == "api" or request.blueprint == "apiweb": if request.blueprint in ("api", "apiweb"):
return json_error(http.HTTPStatus.UNAUTHORIZED, "Non autorise (logic)") return json_error(http.HTTPStatus.UNAUTHORIZED, "Non autorise (logic)")
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))

View File

@ -105,6 +105,9 @@ class User(UserMixin, ScoDocModel):
date_modif_passwd = db.Column(db.DateTime, default=datetime.now) date_modif_passwd = db.Column(db.DateTime, default=datetime.now)
date_created = db.Column(db.DateTime, default=datetime.now) date_created = db.Column(db.DateTime, default=datetime.now)
date_expiration = db.Column(db.DateTime, default=None) date_expiration = db.Column(db.DateTime, default=None)
passwd_must_be_changed = db.Column(
db.Boolean, nullable=False, server_default="false", default=False
)
passwd_temp = db.Column(db.Boolean, default=False) passwd_temp = db.Column(db.Boolean, default=False)
"""champ obsolete. Si connexion alors que passwd_temp est vrai, """champ obsolete. Si connexion alors que passwd_temp est vrai,
efface mot de passe et redirige vers accueil.""" efface mot de passe et redirige vers accueil."""
@ -185,6 +188,8 @@ class User(UserMixin, ScoDocModel):
# La création d'un mot de passe efface l'éventuel mot de passe historique # La création d'un mot de passe efface l'éventuel mot de passe historique
self.password_scodoc7 = None self.password_scodoc7 = None
self.passwd_temp = False self.passwd_temp = False
# Retire le flag
self.passwd_must_be_changed = False
def check_password(self, password: str) -> bool: def check_password(self, password: str) -> bool:
"""Check given password vs current one. """Check given password vs current one.
@ -282,6 +287,7 @@ class User(UserMixin, ScoDocModel):
if self.date_modif_passwd if self.date_modif_passwd
else None else None
), ),
"passwd_must_be_changed": self.passwd_must_be_changed,
"date_created": ( "date_created": (
self.date_created.isoformat() + "Z" if self.date_created else None self.date_created.isoformat() + "Z" if self.date_created else None
), ),
@ -385,7 +391,7 @@ class User(UserMixin, ScoDocModel):
def get_token(self, expires_in=3600): def get_token(self, expires_in=3600):
"Un jeton pour cet user. Stocké en base, non commité." "Un jeton pour cet user. Stocké en base, non commité."
now = datetime.utcnow() now = datetime.now()
if self.token and self.token_expiration > now + timedelta(seconds=60): if self.token and self.token_expiration > now + timedelta(seconds=60):
return self.token return self.token
self.token = base64.b64encode(os.urandom(24)).decode("utf-8") self.token = base64.b64encode(os.urandom(24)).decode("utf-8")
@ -395,7 +401,7 @@ class User(UserMixin, ScoDocModel):
def revoke_token(self): def revoke_token(self):
"Révoque le jeton de cet utilisateur" "Révoque le jeton de cet utilisateur"
self.token_expiration = datetime.utcnow() - timedelta(seconds=1) self.token_expiration = datetime.now() - timedelta(seconds=1)
@staticmethod @staticmethod
def check_token(token): def check_token(token):
@ -403,7 +409,7 @@ class User(UserMixin, ScoDocModel):
and returns the user object. and returns the user object.
""" """
user = User.query.filter_by(token=token).first() user = User.query.filter_by(token=token).first()
if user is None or user.token_expiration < datetime.utcnow(): if user is None or user.token_expiration < datetime.now():
return None return None
return user return user

View File

@ -4,7 +4,7 @@ auth.routes.py
""" """
import flask import flask
from flask import current_app, flash, render_template from flask import current_app, flash, g, render_template
from flask import redirect, url_for, request from flask import redirect, url_for, request
from flask_login import login_user, current_user from flask_login import login_user, current_user
from sqlalchemy import func from sqlalchemy import func
@ -23,6 +23,7 @@ from app.auth.email import send_password_reset_email
from app.decorators import admin_required from app.decorators import admin_required
from app.forms.generic import SimpleConfirmationForm from app.forms.generic import SimpleConfirmationForm
from app.models.config import ScoDocSiteConfig from app.models.config import ScoDocSiteConfig
from app.models.departements import Departement
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
@ -49,6 +50,24 @@ def _login_form():
login_user(user, remember=form.remember_me.data) login_user(user, remember=form.remember_me.data)
current_app.logger.info("login: success (%s)", form.user_name.data) current_app.logger.info("login: success (%s)", form.user_name.data)
if user.passwd_must_be_changed:
# Mot de passe à changer à la première connexion
dept = user.dept or getattr(g, "scodoc_dept", None)
if not dept:
departement = db.session.query(Departement).first()
dept = departement.acronym
if dept:
# Redirect to the password change page
flash("Votre mot de passe doit être changé")
return redirect(
url_for(
"users.form_change_password",
scodoc_dept=dept,
user_name=user.user_name,
)
)
return form.redirect("scodoc.index") return form.redirect("scodoc.index")
return render_template( return render_template(

View File

@ -407,7 +407,9 @@ class BulletinBUT:
d = { d = {
"version": "0", "version": "0",
"type": "BUT", "type": "BUT",
"date": datetime.datetime.utcnow().isoformat() + "Z", "date": datetime.datetime.now(datetime.timezone.utc)
.astimezone()
.isoformat(),
"publie": not formsemestre.bul_hide_xml, "publie": not formsemestre.bul_hide_xml,
"etat_inscription": etud.inscription_etat(formsemestre.id), "etat_inscription": etud.inscription_etat(formsemestre.id),
"etudiant": etud.to_dict_bul(), "etudiant": etud.to_dict_bul(),

View File

@ -76,7 +76,7 @@ class ApcReferentielCompetences(models.ScoDocModel, XMLModel):
"version": "version_orebut", "version": "version_orebut",
} }
# ScoDoc specific fields: # ScoDoc specific fields:
scodoc_date_loaded = db.Column(db.DateTime, default=datetime.utcnow) scodoc_date_loaded = db.Column(db.DateTime, default=datetime.now)
scodoc_orig_filename = db.Column(db.Text()) scodoc_orig_filename = db.Column(db.Text())
# Relations: # Relations:
competences = db.relationship( competences = db.relationship(

View File

@ -646,9 +646,11 @@ class FormSemestre(models.ScoDocModel):
) )
return [db.session.get(ModuleImpl, modimpl_id) for modimpl_id in cursor] return [db.session.get(ModuleImpl, modimpl_id) for modimpl_id in cursor]
def can_be_edited_by(self, user): def can_be_edited_by(self, user: User):
"""Vrai si user peut modifier ce semestre (est chef ou l'un des responsables)""" """Vrai si user peut modifier ce semestre (est chef ou l'un des responsables)"""
if not user.has_permission(Permission.EditFormSemestre): # pas chef if user.passwd_must_be_changed or not user.has_permission(
Permission.EditFormSemestre
): # pas chef
if not self.resp_can_edit or user.id not in [ if not self.resp_can_edit or user.id not in [
resp.id for resp in self.responsables resp.id for resp in self.responsables
]: ]:
@ -897,6 +899,8 @@ class FormSemestre(models.ScoDocModel):
if not self.etat: if not self.etat:
return False # semestre verrouillé return False # semestre verrouillé
user = user or current_user user = user or current_user
if user.passwd_must_be_changed:
return False
if user.has_permission(Permission.EtudChangeGroups): if user.has_permission(Permission.EtudChangeGroups):
return True # typiquement admin, chef dept return True # typiquement admin, chef dept
return self.est_responsable(user) return self.est_responsable(user)
@ -906,11 +910,15 @@ class FormSemestre(models.ScoDocModel):
dans ce semestre: vérifie permission et verrouillage. dans ce semestre: vérifie permission et verrouillage.
""" """
user = user or current_user user = user or current_user
if user.passwd_must_be_changed:
return False
return self.etat and self.est_chef_or_diretud(user) return self.etat and self.est_chef_or_diretud(user)
def can_edit_pv(self, user: User = None): def can_edit_pv(self, user: User = None):
"Vrai si utilisateur (par def. current) peut editer un PV de jury de ce semestre" "Vrai si utilisateur (par def. current) peut editer un PV de jury de ce semestre"
user = user or current_user user = user or current_user
if user.passwd_must_be_changed:
return False
# Autorise les secrétariats, repérés via la permission EtudChangeAdr # Autorise les secrétariats, repérés via la permission EtudChangeAdr
return self.est_chef_or_diretud(user) or user.has_permission( return self.est_chef_or_diretud(user) or user.has_permission(
Permission.EtudChangeAdr Permission.EtudChangeAdr

View File

@ -199,6 +199,8 @@ class ModuleImpl(ScoDocModel):
"""True if this user can create, delete or edit and evaluation in this modimpl """True if this user can create, delete or edit and evaluation in this modimpl
(nb: n'implique pas le droit de saisir ou modifier des notes) (nb: n'implique pas le droit de saisir ou modifier des notes)
""" """
if user.passwd_must_be_changed:
return False
# acces pour resp. moduleimpl et resp. form semestre (dir etud) # acces pour resp. moduleimpl et resp. form semestre (dir etud)
if ( if (
user.has_permission(Permission.EditAllEvals) user.has_permission(Permission.EditAllEvals)
@ -222,6 +224,8 @@ class ModuleImpl(ScoDocModel):
# was sco_permissions_check.can_edit_notes # was sco_permissions_check.can_edit_notes
from app.scodoc import sco_cursus_dut from app.scodoc import sco_cursus_dut
if user.passwd_must_be_changed:
return False
if not self.formsemestre.etat: if not self.formsemestre.etat:
return False # semestre verrouillé return False # semestre verrouillé
is_dir_etud = user.id in (u.id for u in self.formsemestre.responsables) is_dir_etud = user.id in (u.id for u in self.formsemestre.responsables)
@ -247,6 +251,8 @@ class ModuleImpl(ScoDocModel):
if raise_exc: if raise_exc:
raise ScoLockedSemError("Modification impossible: semestre verrouille") raise ScoLockedSemError("Modification impossible: semestre verrouille")
return False return False
if user.passwd_must_be_changed:
return False
# -- check access # -- check access
# admin ou resp. semestre avec flag resp_can_change_resp # admin ou resp. semestre avec flag resp_can_change_resp
if user.has_permission(Permission.EditFormSemestre): if user.has_permission(Permission.EditFormSemestre):
@ -264,6 +270,8 @@ class ModuleImpl(ScoDocModel):
if user is None, current user. if user is None, current user.
""" """
user = current_user if user is None else user user = current_user if user is None else user
if user.passwd_must_be_changed:
return False
if not self.formsemestre.etat: if not self.formsemestre.etat:
if raise_exc: if raise_exc:
raise ScoLockedSemError("Modification impossible: semestre verrouille") raise ScoLockedSemError("Modification impossible: semestre verrouille")
@ -285,6 +293,8 @@ class ModuleImpl(ScoDocModel):
Autorise ScoEtudInscrit ou responsables semestre. Autorise ScoEtudInscrit ou responsables semestre.
""" """
user = current_user if user is None else user user = current_user if user is None else user
if user.passwd_must_be_changed:
return False
if not self.formsemestre.etat: if not self.formsemestre.etat:
if raise_exc: if raise_exc:
raise ScoLockedSemError("Modification impossible: semestre verrouille") raise ScoLockedSemError("Modification impossible: semestre verrouille")

View File

@ -54,9 +54,11 @@ class EtudsArchiver(sco_archives.BaseArchiver):
ETUDS_ARCHIVER = EtudsArchiver() ETUDS_ARCHIVER = EtudsArchiver()
def can_edit_etud_archive(authuser): def can_edit_etud_archive(user):
"""True si l'utilisateur peut modifier les archives etudiantes""" """True si l'utilisateur peut modifier les archives etudiantes"""
return authuser.has_permission(Permission.EtudAddAnnotations) if user.passwd_must_be_changed:
return False
return user.has_permission(Permission.EtudAddAnnotations)
def etud_list_archives_html(etud: Identite): def etud_list_archives_html(etud: Identite):

View File

@ -1001,6 +1001,8 @@ def formsemestre_bulletinetud(
def can_send_bulletin_by_mail(formsemestre_id): def can_send_bulletin_by_mail(formsemestre_id):
"""True if current user is allowed to send a bulletin (pdf) by mail""" """True if current user is allowed to send a bulletin (pdf) by mail"""
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
if current_user.passwd_must_be_changed:
return False
return ( return (
sco_preferences.get_preference("bul_mail_allowed_for_all", formsemestre_id) sco_preferences.get_preference("bul_mail_allowed_for_all", formsemestre_id)
or current_user.has_permission(Permission.EditFormSemestre) or current_user.has_permission(Permission.EditFormSemestre)

View File

@ -134,15 +134,6 @@ def formsemestre_editwithmodules(formsemestre_id: int):
) )
def can_edit_sem(formsemestre_id: int = None, sem=None):
"""Return sem if user can edit it, False otherwise"""
sem = sem or sco_formsemestre.get_formsemestre(formsemestre_id)
if not current_user.has_permission(Permission.EditFormSemestre): # pas chef
if not sem["resp_can_edit"] or current_user.id not in sem["responsables"]:
return False
return sem
RESP_FIELDS = [ RESP_FIELDS = [
"responsable_id", "responsable_id",
"responsable_id2", "responsable_id2",

View File

@ -17,6 +17,8 @@ def can_suppress_annotation(annotation_id):
Seuls l'auteur de l'annotation et le chef de dept peuvent supprimer Seuls l'auteur de l'annotation et le chef de dept peuvent supprimer
une annotation. une annotation.
""" """
if current_user.passwd_must_be_changed:
return False
annotation = ( annotation = (
EtudAnnotation.query.filter_by(id=annotation_id) EtudAnnotation.query.filter_by(id=annotation_id)
.join(Identite) .join(Identite)
@ -30,8 +32,10 @@ def can_suppress_annotation(annotation_id):
) )
def can_edit_suivi(): def can_edit_suivi() -> bool:
"""Vrai si l'utilisateur peut modifier les informations de suivi sur la page etud" """ """Vrai si l'utilisateur peut modifier les informations de suivi sur la page etud" """
if current_user.passwd_must_be_changed:
return False
return current_user.has_permission(Permission.EtudChangeAdr) return current_user.has_permission(Permission.EtudChangeAdr)

View File

@ -184,9 +184,11 @@ def list_users(
if not current_user.is_administrator(): if not current_user.is_administrator():
# si non super-admin, ne donne pas la date exacte de derniere connexion # si non super-admin, ne donne pas la date exacte de derniere connexion
d["last_seen"] = _approximate_date(u.last_seen) d["last_seen"] = _approximate_date(u.last_seen)
d["passwd_must_be_changed"] = "OUI" if d["passwd_must_be_changed"] else ""
else: else:
d["date_modif_passwd"] = "(non visible)" d["date_modif_passwd"] = "(non visible)"
d["non_migre"] = "" d["non_migre"] = ""
d["passwd_must_be_changed"] = ""
if detail_roles: if detail_roles:
d["roles_set"] = { d["roles_set"] = {
f"{r.role.name or ''}_{r.dept or ''}" for r in u.user_roles f"{r.role.name or ''}_{r.dept or ''}" for r in u.user_roles
@ -209,6 +211,7 @@ def list_users(
"roles_string", "roles_string",
"date_expiration", "date_expiration",
"date_modif_passwd", "date_modif_passwd",
"passwd_must_be_changed",
"non_migre", "non_migre",
"status_txt", "status_txt",
] ]
@ -240,6 +243,7 @@ def list_users(
"roles_string": "Rôles", "roles_string": "Rôles",
"date_expiration": "Expiration", "date_expiration": "Expiration",
"date_modif_passwd": "Modif. mot de passe", "date_modif_passwd": "Modif. mot de passe",
"passwd_must_be_changed": "À changer",
"last_seen": "Dernière cnx.", "last_seen": "Dernière cnx.",
"non_migre": "Non migré (!)", "non_migre": "Non migré (!)",
"status_txt": "Etat", "status_txt": "Etat",

View File

@ -0,0 +1,38 @@
<style>
div.msg-change-passwd {
border: 3px solid white;
border-radius: 8px;
padding: 16px;
width: fit-content;
margin-left: auto;
margin-right: auto;
margin-top: 28px;
margin-bottom: 28px;
}
div.msg-change-passwd, div.msg-change-passwd a {
font-size: 36px;
font-weight: bold;
background-color: red;
color: white;
}
div.msg-change-passwd a, div.msg-change-passwd a:visited {
text-decoration: underline;
}
</style>
<div class="msg-change-passwd">
Vous devez
{% if current_user.dept %}
<a class="nav-link" href="{{
url_for(
'users.form_change_password',
scodoc_dept=current_user.dept,
user_name=current_user.user_name
)
}}">
{% endif %}
changer votre mot de passe
{% if current_user.dept %}
</a>
{% endif %}
!
</div>

View File

@ -18,6 +18,9 @@
<br> <br>
<b>Nom :</b> {{user.nom or ""}}<br> <b>Nom :</b> {{user.nom or ""}}<br>
<b>Prénom :</b> {{user.prenom or ""}}<br> <b>Prénom :</b> {{user.prenom or ""}}<br>
{% if user.passwd_must_be_changed %}
<div style="color:white; background-color: red; padding:8px; margin-top: 4px; width: fit-content;">mot de passe à changer</div>
{% endif %}
<b>Mail :</b> {{user.email}}<br> <b>Mail :</b> {{user.email}}<br>
<b>Mail institutionnel:</b> {{user.email_institutionnel or ""}}<br> <b>Mail institutionnel:</b> {{user.email_institutionnel or ""}}<br>
<b>Identifiant EDT:</b> {{user.edt_id or ""}}<br> <b>Identifiant EDT:</b> {{user.edt_id or ""}}<br>

View File

@ -25,6 +25,9 @@
{% block navbar %} {% block navbar %}
{%- endblock navbar %} {%- endblock navbar %}
<div id="sco_msg" class="head_message"></div> <div id="sco_msg" class="head_message"></div>
{% if current_user and current_user.passwd_must_be_changed %}
{% include "auth/msg_change_password.j2" %}
{% endif %}
{% block content -%} {% block content -%}
{%- endblock content %} {%- endblock content %}

View File

@ -46,7 +46,8 @@
{% if current_user.is_anonymous %} {% if current_user.is_anonymous %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('auth.login') }}">connexion</a></li> <li class="nav-item"><a class="nav-link" href="{{ url_for('auth.login') }}">connexion</a></li>
{% else %} {% else %}
<li class="nav-item">{% if current_user.dept %} <li class="nav-item">
{% if current_user.dept %}
<a class="nav-link" href="{{ url_for('users.user_info_page', scodoc_dept=current_user.dept, user_name=current_user.user_name ) <a class="nav-link" href="{{ url_for('users.user_info_page', scodoc_dept=current_user.dept, user_name=current_user.user_name )
}}">{{current_user.user_name}} ({{current_user.dept}})</a> }}">{{current_user.user_name}} ({{current_user.dept}})</a>
{% else %} {% else %}
@ -89,7 +90,7 @@
<script> <script>
const SCO_URL = "{% if g.scodoc_dept %}{{ const SCO_URL = "{% if g.scodoc_dept %}{{
url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)}}{% endif %}"; url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)}}{% endif %}";
document.querySelector('.navbar-toggler').addEventListener('click', function() { document.querySelector('.navbar-toggler').addEventListener('click', function() {
document.querySelector('.navbar-collapse').classList.toggle('show'); document.querySelector('.navbar-collapse').classList.toggle('show');
}); });

View File

@ -37,7 +37,7 @@ def start_scodoc_request():
# current_app.logger.info(f"start_scodoc_request") # current_app.logger.info(f"start_scodoc_request")
ndb.open_db_connection() ndb.open_db_connection()
if current_user and current_user.is_authenticated: if current_user and current_user.is_authenticated:
current_user.last_seen = datetime.datetime.utcnow() current_user.last_seen = datetime.datetime.now()
db.session.commit() db.session.commit()
# caches locaux (durée de vie=la requête en cours) # caches locaux (durée de vie=la requête en cours)
g.stored_get_formsemestre = {} g.stored_get_formsemestre = {}

View File

@ -334,6 +334,16 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
}, },
) )
) )
descr.append(
(
"passwd_must_be_changed",
{
"title": "Force à changer le mot de passe",
"input_type": "boolcheckbox",
"explanation": """ à la première connexion.""",
},
)
)
if not edit: if not edit:
descr += [ descr += [
( (

View File

@ -0,0 +1,34 @@
"""passwd_must_be_changed
Revision ID: bcd959a23aea
Revises: 2640b7686de6
Create Date: 2024-11-01 09:51:01.299407
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "bcd959a23aea"
down_revision = "2640b7686de6"
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.add_column(
sa.Column(
"passwd_must_be_changed",
sa.Boolean(),
server_default="false",
nullable=False,
)
)
def downgrade():
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.drop_column("passwd_must_be_changed")

View File

@ -5,27 +5,13 @@
"rang": "1", "rang": "1",
"civilite_str": "Mme", "civilite_str": "Mme",
"nom_disp": "BONHOMME", "nom_disp": "BONHOMME",
"prenom": "MADELEINE", "prenom": "Madeleine",
"nom_short": "BONHOMME Ma.", "nom_short": "BONHOMME Ma.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "bonhomme;madeleine", "sort_key": "bonhomme;madeleine",
"moy_gen": "14.36", "moy_gen": "14.36",
"moy_ue_1": "14.94",
"moy_res_1_1": "~",
"moy_res_3_1": "11.97",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "15.71",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "10.66",
"moy_res_12_1": "12.50",
"moy_res_13_1": "~",
"moy_sae_2_1": "18.72",
"moy_sae_7_1": "14.69",
"moy_ue_2": "11.17", "moy_ue_2": "11.17",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -49,6 +35,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "17.83", "moy_sae_14_3": "17.83",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "14.94",
"moy_res_1_1": "~",
"moy_res_3_1": "11.97",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "15.71",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "10.66",
"moy_res_12_1": "12.50",
"moy_res_13_1": "~",
"moy_sae_2_1": "18.72",
"moy_sae_7_1": "14.69",
"ues_validables": "3/3", "ues_validables": "3/3",
"nbabs": 1, "nbabs": 1,
"nbabsjust": 0, "nbabsjust": 0,
@ -65,27 +65,13 @@
"rang": "2", "rang": "2",
"civilite_str": "M.", "civilite_str": "M.",
"nom_disp": "JAMES", "nom_disp": "JAMES",
"prenom": "JACQUES", "prenom": "Jacques",
"nom_short": "JAMES Ja.", "nom_short": "JAMES Ja.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "james;jacques", "sort_key": "james;jacques",
"moy_gen": "12.67", "moy_gen": "12.67",
"moy_ue_1": "13.51",
"moy_res_1_1": "~",
"moy_res_3_1": "03.27",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "13.05",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "04.35",
"moy_res_12_1": "18.85",
"moy_res_13_1": "~",
"moy_sae_2_1": "~",
"moy_sae_7_1": "17.07",
"moy_ue_2": "14.24", "moy_ue_2": "14.24",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -109,6 +95,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "10.74", "moy_sae_14_3": "10.74",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "13.51",
"moy_res_1_1": "~",
"moy_res_3_1": "03.27",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "13.05",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "04.35",
"moy_res_12_1": "18.85",
"moy_res_13_1": "~",
"moy_sae_2_1": "~",
"moy_sae_7_1": "17.07",
"ues_validables": "3/3", "ues_validables": "3/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -125,27 +125,13 @@
"rang": "3", "rang": "3",
"civilite_str": "", "civilite_str": "",
"nom_disp": "THIBAUD", "nom_disp": "THIBAUD",
"prenom": "MAXIME", "prenom": "Maxime",
"nom_short": "THIBAUD Ma.", "nom_short": "THIBAUD Ma.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "thibaud;maxime", "sort_key": "thibaud;maxime",
"moy_gen": "12.02", "moy_gen": "12.02",
"moy_ue_1": "14.34",
"moy_res_1_1": "~",
"moy_res_3_1": "17.68",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "18.31",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "18.97",
"moy_res_12_1": "05.46",
"moy_res_13_1": "~",
"moy_sae_2_1": "13.02",
"moy_sae_7_1": "14.11",
"moy_ue_2": "09.89", "moy_ue_2": "09.89",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -169,6 +155,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "05.70", "moy_sae_14_3": "05.70",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "14.34",
"moy_res_1_1": "~",
"moy_res_3_1": "17.68",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "18.31",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "18.97",
"moy_res_12_1": "05.46",
"moy_res_13_1": "~",
"moy_sae_2_1": "13.02",
"moy_sae_7_1": "14.11",
"ues_validables": "2/3", "ues_validables": "2/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -185,27 +185,13 @@
"rang": "4", "rang": "4",
"civilite_str": "", "civilite_str": "",
"nom_disp": "ROYER", "nom_disp": "ROYER",
"prenom": "CAMILLE", "prenom": "Camille",
"nom_short": "ROYER Ca.", "nom_short": "ROYER Ca.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "royer;camille", "sort_key": "royer;camille",
"moy_gen": "11.88", "moy_gen": "11.88",
"moy_ue_1": "07.09",
"moy_res_1_1": "~",
"moy_res_3_1": "04.07",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "17.62",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "16.57",
"moy_res_12_1": "18.61",
"moy_res_13_1": "~",
"moy_sae_2_1": "14.13",
"moy_sae_7_1": "00.53",
"moy_ue_2": "17.35", "moy_ue_2": "17.35",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -229,6 +215,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "10.52", "moy_sae_14_3": "10.52",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "07.09",
"moy_res_1_1": "~",
"moy_res_3_1": "04.07",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "17.62",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "16.57",
"moy_res_12_1": "18.61",
"moy_res_13_1": "~",
"moy_sae_2_1": "14.13",
"moy_sae_7_1": "00.53",
"ues_validables": "2/3", "ues_validables": "2/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -245,27 +245,13 @@
"rang": "5", "rang": "5",
"civilite_str": "M.", "civilite_str": "M.",
"nom_disp": "GODIN", "nom_disp": "GODIN",
"prenom": "CLAUDE", "prenom": "Claude",
"nom_short": "GODIN Cl.", "nom_short": "GODIN Cl.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "godin;claude", "sort_key": "godin;claude",
"moy_gen": "10.52", "moy_gen": "10.52",
"moy_ue_1": "08.93",
"moy_res_1_1": "~",
"moy_res_3_1": "07.77",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "00.48",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "08.95",
"moy_res_12_1": "18.10",
"moy_res_13_1": "~",
"moy_sae_2_1": "14.29",
"moy_sae_7_1": "06.89",
"moy_ue_2": "16.04", "moy_ue_2": "16.04",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -289,6 +275,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "11.09", "moy_sae_14_3": "11.09",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "08.93",
"moy_res_1_1": "~",
"moy_res_3_1": "07.77",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "00.48",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "08.95",
"moy_res_12_1": "18.10",
"moy_res_13_1": "~",
"moy_sae_2_1": "14.29",
"moy_sae_7_1": "06.89",
"ues_validables": "1/3", "ues_validables": "1/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -305,27 +305,13 @@
"rang": "6", "rang": "6",
"civilite_str": "M.", "civilite_str": "M.",
"nom_disp": "CONSTANT", "nom_disp": "CONSTANT",
"prenom": "PATRICK", "prenom": "Patrick",
"nom_short": "CONSTANT Pa.", "nom_short": "CONSTANT Pa.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "constant;patrick", "sort_key": "constant;patrick",
"moy_gen": "10.04", "moy_gen": "10.04",
"moy_ue_1": "13.06",
"moy_res_1_1": "~",
"moy_res_3_1": "05.84",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "11.44",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "14.04",
"moy_res_12_1": "13.28",
"moy_res_13_1": "~",
"moy_sae_2_1": "09.82",
"moy_sae_7_1": "17.46",
"moy_ue_2": "10.62", "moy_ue_2": "10.62",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -349,6 +335,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "01.55", "moy_sae_14_3": "01.55",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "13.06",
"moy_res_1_1": "~",
"moy_res_3_1": "05.84",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "11.44",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "14.04",
"moy_res_12_1": "13.28",
"moy_res_13_1": "~",
"moy_sae_2_1": "09.82",
"moy_sae_7_1": "17.46",
"ues_validables": "2/3", "ues_validables": "2/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -365,27 +365,13 @@
"rang": "7", "rang": "7",
"civilite_str": "", "civilite_str": "",
"nom_disp": "TOUSSAINT", "nom_disp": "TOUSSAINT",
"prenom": "ALIX", "prenom": "Alix",
"nom_short": "TOUSSAINT Al.", "nom_short": "TOUSSAINT Al.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "toussaint;alix", "sort_key": "toussaint;alix",
"moy_gen": "08.59", "moy_gen": "08.59",
"moy_ue_1": "07.24",
"moy_res_1_1": "~",
"moy_res_3_1": "11.90",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "00.47",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "18.66",
"moy_res_12_1": "18.02",
"moy_res_13_1": "~",
"moy_sae_2_1": "~",
"moy_sae_7_1": "04.46",
"moy_ue_2": "13.93", "moy_ue_2": "13.93",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -409,6 +395,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "05.17", "moy_sae_14_3": "05.17",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "07.24",
"moy_res_1_1": "~",
"moy_res_3_1": "11.90",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "00.47",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "18.66",
"moy_res_12_1": "18.02",
"moy_res_13_1": "~",
"moy_sae_2_1": "~",
"moy_sae_7_1": "04.46",
"ues_validables": "1/3", "ues_validables": "1/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -425,27 +425,13 @@
"rang": "8", "rang": "8",
"civilite_str": "", "civilite_str": "",
"nom_disp": "DENIS", "nom_disp": "DENIS",
"prenom": "MAXIME", "prenom": "Maxime",
"nom_short": "DENIS Ma.", "nom_short": "DENIS Ma.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "denis;maxime", "sort_key": "denis;maxime",
"moy_gen": "07.21", "moy_gen": "07.21",
"moy_ue_1": "06.86",
"moy_res_1_1": "~",
"moy_res_3_1": "~",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "10.06",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "11.75",
"moy_res_12_1": "01.88",
"moy_res_13_1": "~",
"moy_sae_2_1": "14.55",
"moy_sae_7_1": "03.02",
"moy_ue_2": "08.84", "moy_ue_2": "08.84",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -469,6 +455,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "03.32", "moy_sae_14_3": "03.32",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "06.86",
"moy_res_1_1": "~",
"moy_res_3_1": "~",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "10.06",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "11.75",
"moy_res_12_1": "01.88",
"moy_res_13_1": "~",
"moy_sae_2_1": "14.55",
"moy_sae_7_1": "03.02",
"ues_validables": "0/3", "ues_validables": "0/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -485,27 +485,13 @@
"rang": "9", "rang": "9",
"civilite_str": "Mme", "civilite_str": "Mme",
"nom_disp": "WALTER", "nom_disp": "WALTER",
"prenom": "SIMONE", "prenom": "Simone",
"nom_short": "WALTER Si.", "nom_short": "WALTER Si.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "walter;simone", "sort_key": "walter;simone",
"moy_gen": "07.02", "moy_gen": "07.02",
"moy_ue_1": "06.82",
"moy_res_1_1": "~",
"moy_res_3_1": "16.91",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "12.84",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "13.08",
"moy_res_12_1": "10.63",
"moy_res_13_1": "~",
"moy_sae_2_1": "06.28",
"moy_sae_7_1": "01.36",
"moy_ue_2": "07.96", "moy_ue_2": "07.96",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -529,6 +515,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "02.10", "moy_sae_14_3": "02.10",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "06.82",
"moy_res_1_1": "~",
"moy_res_3_1": "16.91",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "12.84",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "13.08",
"moy_res_12_1": "10.63",
"moy_res_13_1": "~",
"moy_sae_2_1": "06.28",
"moy_sae_7_1": "01.36",
"ues_validables": "0/3", "ues_validables": "0/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -545,27 +545,13 @@
"rang": "10", "rang": "10",
"civilite_str": "", "civilite_str": "",
"nom_disp": "GROSS", "nom_disp": "GROSS",
"prenom": "SACHA", "prenom": "Sacha",
"nom_short": "GROSS Sa.", "nom_short": "GROSS Sa.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "gross;sacha", "sort_key": "gross;sacha",
"moy_gen": "05.31", "moy_gen": "05.31",
"moy_ue_1": "03.73",
"moy_res_1_1": "~",
"moy_res_3_1": "~",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "03.04",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "04.89",
"moy_res_12_1": "09.88",
"moy_res_13_1": "~",
"moy_sae_2_1": "~",
"moy_sae_7_1": "02.85",
"moy_ue_2": "07.13", "moy_ue_2": "07.13",
"moy_res_1_2": "~", "moy_res_1_2": "~",
"moy_res_4_2": "~", "moy_res_4_2": "~",
@ -589,6 +575,20 @@
"moy_res_21_3": "~", "moy_res_21_3": "~",
"moy_sae_14_3": "07.17", "moy_sae_14_3": "07.17",
"moy_sae_15_3": "~", "moy_sae_15_3": "~",
"moy_ue_1": "03.73",
"moy_res_1_1": "~",
"moy_res_3_1": "~",
"moy_res_4_1": "~",
"moy_res_5_1": "~",
"moy_res_6_1": "~",
"moy_res_18_1": "03.04",
"moy_res_10_1": "~",
"moy_res_11_1": "~",
"moy_res_20_1": "04.89",
"moy_res_12_1": "09.88",
"moy_res_13_1": "~",
"moy_sae_2_1": "~",
"moy_sae_7_1": "02.85",
"ues_validables": "0/3", "ues_validables": "0/3",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -605,27 +605,13 @@
"rang": "11 ex", "rang": "11 ex",
"civilite_str": "M.", "civilite_str": "M.",
"nom_disp": "BARTHELEMY", "nom_disp": "BARTHELEMY",
"prenom": "G\u00c9RARD", "prenom": "G\u00e9rard",
"nom_short": "BARTHELEMY G\u00e9.", "nom_short": "BARTHELEMY G\u00e9.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "barthelemy;gerard", "sort_key": "barthelemy;gerard",
"moy_gen": "", "moy_gen": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"moy_ue_2": "", "moy_ue_2": "",
"moy_res_1_2": "", "moy_res_1_2": "",
"moy_res_4_2": "", "moy_res_4_2": "",
@ -649,6 +635,20 @@
"moy_res_21_3": "", "moy_res_21_3": "",
"moy_sae_14_3": "", "moy_sae_14_3": "",
"moy_sae_15_3": "", "moy_sae_15_3": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"ues_validables": "", "ues_validables": "",
"nbabs": 2, "nbabs": 2,
"nbabsjust": 0, "nbabsjust": 0,
@ -665,27 +665,13 @@
"rang": "11 ex", "rang": "11 ex",
"civilite_str": "Mme", "civilite_str": "Mme",
"nom_disp": "MILLOT", "nom_disp": "MILLOT",
"prenom": "FRAN\u00c7OISE", "prenom": "Fran\u00e7oise",
"nom_short": "MILLOT Fr.", "nom_short": "MILLOT Fr.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "millot;francoise", "sort_key": "millot;francoise",
"moy_gen": "", "moy_gen": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"moy_ue_2": "", "moy_ue_2": "",
"moy_res_1_2": "", "moy_res_1_2": "",
"moy_res_4_2": "", "moy_res_4_2": "",
@ -709,6 +695,20 @@
"moy_res_21_3": "", "moy_res_21_3": "",
"moy_sae_14_3": "", "moy_sae_14_3": "",
"moy_sae_15_3": "", "moy_sae_15_3": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"ues_validables": "", "ues_validables": "",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -725,27 +725,13 @@
"rang": "11 ex", "rang": "11 ex",
"civilite_str": "M.", "civilite_str": "M.",
"nom_disp": "BENOIT", "nom_disp": "BENOIT",
"prenom": "EMMANUEL", "prenom": "Emmanuel",
"nom_short": "BENOIT Em.", "nom_short": "BENOIT Em.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "benoit;emmanuel", "sort_key": "benoit;emmanuel",
"moy_gen": "", "moy_gen": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"moy_ue_2": "", "moy_ue_2": "",
"moy_res_1_2": "", "moy_res_1_2": "",
"moy_res_4_2": "", "moy_res_4_2": "",
@ -769,6 +755,20 @@
"moy_res_21_3": "", "moy_res_21_3": "",
"moy_sae_14_3": "", "moy_sae_14_3": "",
"moy_sae_15_3": "", "moy_sae_15_3": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"ues_validables": "", "ues_validables": "",
"nbabs": 2, "nbabs": 2,
"nbabsjust": 0, "nbabsjust": 0,
@ -785,27 +785,13 @@
"rang": "11 ex", "rang": "11 ex",
"civilite_str": "Mme", "civilite_str": "Mme",
"nom_disp": "LECOCQ", "nom_disp": "LECOCQ",
"prenom": "MARGUERITE", "prenom": "Marguerite",
"nom_short": "LECOCQ Ma.", "nom_short": "LECOCQ Ma.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "lecocq;marguerite", "sort_key": "lecocq;marguerite",
"moy_gen": "", "moy_gen": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"moy_ue_2": "", "moy_ue_2": "",
"moy_res_1_2": "", "moy_res_1_2": "",
"moy_res_4_2": "", "moy_res_4_2": "",
@ -829,6 +815,20 @@
"moy_res_21_3": "", "moy_res_21_3": "",
"moy_sae_14_3": "", "moy_sae_14_3": "",
"moy_sae_15_3": "", "moy_sae_15_3": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"ues_validables": "", "ues_validables": "",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -845,27 +845,13 @@
"rang": "11 ex", "rang": "11 ex",
"civilite_str": "M.", "civilite_str": "M.",
"nom_disp": "ROUSSET", "nom_disp": "ROUSSET",
"prenom": "DERC'HEN", "prenom": "Derc'hen",
"nom_short": "ROUSSET De.", "nom_short": "ROUSSET De.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "rousset;derchen", "sort_key": "rousset;derchen",
"moy_gen": "", "moy_gen": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"moy_ue_2": "", "moy_ue_2": "",
"moy_res_1_2": "", "moy_res_1_2": "",
"moy_res_4_2": "", "moy_res_4_2": "",
@ -889,6 +875,20 @@
"moy_res_21_3": "", "moy_res_21_3": "",
"moy_sae_14_3": "", "moy_sae_14_3": "",
"moy_sae_15_3": "", "moy_sae_15_3": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"ues_validables": "", "ues_validables": "",
"nbabs": 0, "nbabs": 0,
"nbabsjust": 0, "nbabsjust": 0,
@ -905,27 +905,13 @@
"rang": "11 ex", "rang": "11 ex",
"civilite_str": "", "civilite_str": "",
"nom_disp": "MORAND", "nom_disp": "MORAND",
"prenom": "CAMILLE", "prenom": "Camille",
"nom_short": "MORAND Ca.", "nom_short": "MORAND Ca.",
"partitions": { "partitions": {
"1": 1 "1": 1
}, },
"sort_key": "morand;camille", "sort_key": "morand;camille",
"moy_gen": "", "moy_gen": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"moy_ue_2": "", "moy_ue_2": "",
"moy_res_1_2": "", "moy_res_1_2": "",
"moy_res_4_2": "", "moy_res_4_2": "",
@ -949,6 +935,20 @@
"moy_res_21_3": "", "moy_res_21_3": "",
"moy_sae_14_3": "", "moy_sae_14_3": "",
"moy_sae_15_3": "", "moy_sae_15_3": "",
"moy_ue_1": "",
"moy_res_1_1": "",
"moy_res_3_1": "",
"moy_res_4_1": "",
"moy_res_5_1": "",
"moy_res_6_1": "",
"moy_res_18_1": "",
"moy_res_10_1": "",
"moy_res_11_1": "",
"moy_res_20_1": "",
"moy_res_12_1": "",
"moy_res_13_1": "",
"moy_sae_2_1": "",
"moy_sae_7_1": "",
"ues_validables": "", "ues_validables": "",
"nbabs": 1, "nbabs": 1,
"nbabsjust": 0, "nbabsjust": 0,