formsemestre header sans notes_table

This commit is contained in:
Emmanuel Viennet 2022-01-07 15:11:24 +01:00
parent 5e144d8745
commit 3eb038b491
18 changed files with 57 additions and 58 deletions

View File

@ -8,6 +8,7 @@ from functools import cached_property
import flask_sqlalchemy import flask_sqlalchemy
from app import db from app import db
from app import log
from app.models import APO_CODE_STR_LEN from app.models import APO_CODE_STR_LEN
from app.models import SHORT_STR_LEN from app.models import SHORT_STR_LEN
from app.models import CODE_STR_LEN from app.models import CODE_STR_LEN
@ -158,6 +159,24 @@ class FormSemestre(db.Model):
""" """
return (self.date_debut <= date_debut) and (date_fin <= self.date_fin) return (self.date_debut <= date_debut) and (date_fin <= self.date_fin)
def est_sur_une_annee(self):
"""Test si sem est entièrement sur la même année scolaire.
(ce n'est pas obligatoire mais si ce n'est pas le
cas les exports Apogée risquent de mal fonctionner)
Pivot au 1er août.
"""
if self.date_debut > self.date_fin:
log(f"Warning: semestre {self.id} begins after ending !")
annee_debut = self.date_debut.year
if self.date_debut.month < 8: # août
# considere que debut sur l'anne scolaire precedente
annee_debut -= 1
annee_fin = self.date_fin.year
if self.date_fin.month < 9:
# 9 (sept) pour autoriser un début en sept et une fin en aout
annee_fin -= 1
return annee_debut == annee_fin
def est_decale(self): def est_decale(self):
"""Vrai si semestre "décalé" """Vrai si semestre "décalé"
c'est à dire semestres impairs commençant entre janvier et juin c'est à dire semestres impairs commençant entre janvier et juin

View File

@ -313,7 +313,7 @@ def sco_footer():
def html_sem_header( def html_sem_header(
title, sem=None, with_page_header=True, with_h2=True, page_title=None, **args title, with_page_header=True, with_h2=True, page_title=None, **args
): ):
"Titre d'une page semestre avec lien vers tableau de bord" "Titre d'une page semestre avec lien vers tableau de bord"
# sem now unused and thus optional... # sem now unused and thus optional...

View File

@ -405,7 +405,6 @@ def formsemestre_archive(formsemestre_id, group_ids=[]):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Archiver les PV et résultats du semestre", "Archiver les PV et résultats du semestre",
sem=sem,
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES, cssstyles=sco_groups_view.CSSSTYLES,
init_qtip=True, init_qtip=True,
@ -524,7 +523,7 @@ def formsemestre_list_archives(formsemestre_id):
} }
L.append(a) L.append(a)
H = [html_sco_header.html_sem_header("Archive des PV et résultats ", sem)] H = [html_sco_header.html_sem_header("Archive des PV et résultats ")]
if not L: if not L:
H.append("<p>aucune archive enregistrée</p>") H.append("<p>aucune archive enregistrée</p>")
else: else:

View File

@ -222,7 +222,6 @@ def formsemestre_check_absences_html(formsemestre_id):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Vérification absences aux évaluations de ce semestre", "Vérification absences aux évaluations de ce semestre",
sem,
), ),
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées. """<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.
Sont listés tous les modules avec des évaluations.<br/>Aucune action n'est effectuée: Sont listés tous les modules avec des évaluations.<br/>Aucune action n'est effectuée:

View File

@ -467,7 +467,6 @@ def formsemestre_evaluations_cal(formsemestre_id):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Evaluations du semestre", "Evaluations du semestre",
sem,
cssstyles=["css/calabs.css"], cssstyles=["css/calabs.css"],
), ),
'<div class="cal_evaluations">', '<div class="cal_evaluations">',

View File

@ -450,7 +450,7 @@ def sem_in_annee_scolaire(sem, year=False):
) )
def sem_une_annee(sem): def sem_une_annee(sem): # XXX deprecated: use FormSemestre.est_sur_une_annee()
"""Test si sem est entièrement sur la même année scolaire. """Test si sem est entièrement sur la même année scolaire.
(ce n'est pas obligatoire mais si ce n'est pas le cas les exports Apogée ne vont pas fonctionner) (ce n'est pas obligatoire mais si ce n'est pas le cas les exports Apogée ne vont pas fonctionner)
pivot au 1er août. pivot au 1er août.

View File

@ -84,7 +84,7 @@ def formsemestre_custommenu_edit(formsemestre_id):
scu.NotesURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id scu.NotesURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id
) )
H = [ H = [
html_sco_header.html_sem_header("Modification du menu du semestre ", sem), html_sco_header.html_sem_header("Modification du menu du semestre "),
"""<p class="help">Ce menu, spécifique à chaque semestre, peut être utilisé pour placer des liens vers vos applications préférées.</p> """<p class="help">Ce menu, spécifique à chaque semestre, peut être utilisé pour placer des liens vers vos applications préférées.</p>
<p class="help">Procédez en plusieurs fois si vous voulez ajouter plusieurs items.</p>""", <p class="help">Procédez en plusieurs fois si vous voulez ajouter plusieurs items.</p>""",
] ]

View File

@ -96,7 +96,6 @@ def formsemestre_editwithmodules(formsemestre_id):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Modification du semestre", "Modification du semestre",
sem,
javascripts=["libjs/AutoSuggest.js"], javascripts=["libjs/AutoSuggest.js"],
cssstyles=["css/autosuggest_inquisitor.css"], cssstyles=["css/autosuggest_inquisitor.css"],
bodyOnLoad="init_tf_form('')", bodyOnLoad="init_tf_form('')",
@ -900,7 +899,6 @@ def formsemestre_clone(formsemestre_id):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Copie du semestre", "Copie du semestre",
sem,
javascripts=["libjs/AutoSuggest.js"], javascripts=["libjs/AutoSuggest.js"],
cssstyles=["css/autosuggest_inquisitor.css"], cssstyles=["css/autosuggest_inquisitor.css"],
bodyOnLoad="init_tf_form('')", bodyOnLoad="init_tf_form('')",
@ -1240,7 +1238,7 @@ def formsemestre_delete(formsemestre_id):
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
H = [ H = [
html_sco_header.html_sem_header("Suppression du semestre", sem), html_sco_header.html_sem_header("Suppression du semestre"),
"""<div class="ue_warning"><span>Attention !</span> """<div class="ue_warning"><span>Attention !</span>
<p class="help">A n'utiliser qu'en cas d'erreur lors de la saisie d'une formation. Normalement, <p class="help">A n'utiliser qu'en cas d'erreur lors de la saisie d'une formation. Normalement,
<b>un semestre ne doit jamais être supprimé</b> (on perd la mémoire des notes et de tous les événements liés à ce semestre !).</p> <b>un semestre ne doit jamais être supprimé</b> (on perd la mémoire des notes et de tous les événements liés à ce semestre !).</p>
@ -1519,7 +1517,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
</p> </p>
""" """
H = [ H = [
html_sco_header.html_sem_header("Coefficients des UE du semestre", sem), html_sco_header.html_sem_header("Coefficients des UE du semestre"),
help, help,
] ]
# #
@ -1631,7 +1629,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
) # > modif coef UE cap (modifs notes de _certains_ etudiants) ) # > modif coef UE cap (modifs notes de _certains_ etudiants)
header = html_sco_header.html_sem_header("Coefficients des UE du semestre", sem) header = html_sco_header.html_sem_header("Coefficients des UE du semestre")
return ( return (
header header
+ "\n".join(z) + "\n".join(z)

View File

@ -374,7 +374,6 @@ def formsemestre_inscription_with_modules(
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Inscription de %s dans ce semestre" % etud["nomprenom"], "Inscription de %s dans ce semestre" % etud["nomprenom"],
sem,
) )
] ]
F = html_sco_header.sco_footer() F = html_sco_header.sco_footer()
@ -802,7 +801,6 @@ def formsemestre_inscrits_ailleurs(formsemestre_id):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Inscriptions multiples parmi les étudiants du semestre ", "Inscriptions multiples parmi les étudiants du semestre ",
sem,
) )
] ]
insd = list_inscrits_ailleurs(formsemestre_id) insd = list_inscrits_ailleurs(formsemestre_id)

View File

@ -36,6 +36,7 @@ from flask_login import current_user
from app import log from app import log
from app.models import Module from app.models import Module
from app.models.formsemestre import FormSemestre
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
@ -722,7 +723,7 @@ def formsemestre_description_table(formsemestre_id, with_evals=False):
% (request.base_url, formsemestre_id, with_evals), % (request.base_url, formsemestre_id, with_evals),
page_title=title, page_title=title,
html_title=html_sco_header.html_sem_header( html_title=html_sco_header.html_sem_header(
"Description du semestre", sem, with_page_header=False "Description du semestre", with_page_header=False
), ),
pdf_title=title, pdf_title=title,
preferences=sco_preferences.SemPreferences(formsemestre_id), preferences=sco_preferences.SemPreferences(formsemestre_id),
@ -915,34 +916,35 @@ def html_expr_diagnostic(diagnostics):
def formsemestre_status_head(formsemestre_id=None, page_title=None): def formsemestre_status_head(formsemestre_id=None, page_title=None):
"""En-tête HTML des pages "semestre" """ """En-tête HTML des pages "semestre" """
semlist = sco_formsemestre.do_formsemestre_list( sem = FormSemestre.query.get(formsemestre_id)
args={"formsemestre_id": formsemestre_id} if not sem:
) raise ScoValueError("Semestre inexistant (il a peut être été supprimé ?)")
if not semlist: formation = sem.formation
raise ScoValueError("Session inexistante (elle a peut être été supprimée ?)") parcours = formation.get_parcours()
sem = semlist[0]
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
page_title = page_title or "Modules de " page_title = page_title or "Modules de "
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
page_title, sem, with_page_header=False, with_h2=False page_title, with_page_header=False, with_h2=False
), ),
f"""<table> f"""<table>
<tr><td class="fichetitre2">Formation: </td><td> <tr><td class="fichetitre2">Formation: </td><td>
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=F['formation_id'])}" <a href="{url_for('notes.ue_table',
class="discretelink" title="Formation {F['acronyme']}, v{F['version']}">{F['titre']}</a>""", scodoc_dept=g.scodoc_dept, formation_id=sem.formation.id)}"
class="discretelink" title="Formation {
formation.acronyme}, v{formation.version}">{formation.titre}</a>
""",
] ]
if sem["semestre_id"] >= 0: if sem.semestre_id >= 0:
H.append(", %s %s" % (parcours.SESSION_NAME, sem["semestre_id"])) H.append(", %s %s" % (parcours.SESSION_NAME, sem.semestre_id))
if sem["modalite"]: if sem.modalite:
H.append("&nbsp;en %(modalite)s" % sem) H.append(f"&nbsp;en {sem.modalite}")
if sem["etapes"]: if sem.etapes:
H.append( H.append(
"&nbsp;&nbsp;&nbsp;(étape <b><tt>%s</tt></b>)" f"""&nbsp;&nbsp;&nbsp;(étape <b><tt>{
% (sem["etapes_apo_str"] or "-") sem.etapes_apo_str() or "-"
}</tt></b>)"""
) )
H.append("</td></tr>") H.append("</td></tr>")
@ -965,18 +967,16 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
) )
H.append("</table>") H.append("</table>")
sem_warning = "" sem_warning = ""
if sem["bul_hide_xml"]: if sem.bul_hide_xml:
sem_warning += "Bulletins non publiés sur le portail. " sem_warning += "Bulletins non publiés sur le portail. "
if sem["block_moyennes"]: if sem.block_moyennes:
sem_warning += "Calcul des moyennes bloqué !" sem_warning += "Calcul des moyennes bloqué !"
if sem_warning: if sem_warning:
H.append('<p class="fontorange"><em>' + sem_warning + "</em></p>") H.append('<p class="fontorange"><em>' + sem_warning + "</em></p>")
if sem["semestre_id"] >= 0 and not sco_formsemestre.sem_une_annee(sem): if sem.semestre_id >= 0 and not sem.est_sur_une_annee():
H.append( H.append(
'<p class="fontorange"><em>Attention: ce semestre couvre plusieurs années scolaires !</em></p>' '<p class="fontorange"><em>Attention: ce semestre couvre plusieurs années scolaires !</em></p>'
) )
# elif sco_preferences.get_preference( 'bul_display_publication', formsemestre_id):
# H.append('<p><em>Bulletins publiés sur le portail</em></p>')
return "".join(H) return "".join(H)

View File

@ -847,9 +847,7 @@ def formsemestre_validation_auto(formsemestre_id):
"Formulaire saisie automatisee des decisions d'un semestre" "Formulaire saisie automatisee des decisions d'un semestre"
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header("Saisie automatique des décisions du semestre"),
"Saisie automatique des décisions du semestre", sem
),
""" """
<ul> <ul>
<li>Seuls les étudiants qui obtiennent le semestre seront affectés (code ADM, moyenne générale et <li>Seuls les étudiants qui obtiennent le semestre seront affectés (code ADM, moyenne générale et

View File

@ -418,7 +418,7 @@ def build_page(
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Passages dans le semestre", sem, with_page_header=False "Passages dans le semestre", with_page_header=False
), ),
"""<form method="post" action="%s">""" % request.base_url, """<form method="post" action="%s">""" % request.base_url,
"""<input type="hidden" name="formsemestre_id" value="%(formsemestre_id)s"/> """<input type="hidden" name="formsemestre_id" value="%(formsemestre_id)s"/>

View File

@ -2150,7 +2150,7 @@ class SemPreferences(object):
) # a bug ! ) # a bug !
sem = sco_formsemestre.get_formsemestre(self.formsemestre_id) sem = sco_formsemestre.get_formsemestre(self.formsemestre_id)
H = [ H = [
html_sco_header.html_sem_header("Préférences du semestre", sem), html_sco_header.html_sem_header("Préférences du semestre"),
""" """
<p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p> <p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p>
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p> <p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>

View File

@ -541,7 +541,6 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Décisions du jury pour le semestre", "Décisions du jury pour le semestre",
sem,
init_qtip=True, init_qtip=True,
javascripts=["js/etud_info.js"], javascripts=["js/etud_info.js"],
), ),
@ -627,7 +626,6 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Edition du PV de jury %s" % etuddescr, "Edition du PV de jury %s" % etuddescr,
sem=sem,
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES, cssstyles=sco_groups_view.CSSSTYLES,
init_qtip=True, init_qtip=True,
@ -804,7 +802,6 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[]):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Édition des lettres individuelles", "Édition des lettres individuelles",
sem=sem,
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES, cssstyles=sco_groups_view.CSSSTYLES,
init_qtip=True, init_qtip=True,

View File

@ -220,7 +220,6 @@ def external_ue_create_form(formsemestre_id, etudid):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Ajout d'une UE externe pour %(nomprenom)s" % etud, "Ajout d'une UE externe pour %(nomprenom)s" % etud,
sem,
javascripts=["js/sco_ue_external.js"], javascripts=["js/sco_ue_external.js"],
), ),
"""<p class="help">Cette page permet d'indiquer que l'étudiant a suivi une UE """<p class="help">Cette page permet d'indiquer que l'étudiant a suivi une UE

View File

@ -940,9 +940,7 @@ def EtatAbsencesGr(
init_qtip=True, init_qtip=True,
javascripts=["js/etud_info.js"], javascripts=["js/etud_info.js"],
), ),
html_title=html_sco_header.html_sem_header( html_title=html_sco_header.html_sem_header("%s" % title, with_page_header=False)
"%s" % title, sem, with_page_header=False
)
+ "<p>Période du %s au %s (nombre de <b>demi-journées</b>)<br/>" % (debut, fin), + "<p>Période du %s au %s (nombre de <b>demi-journées</b>)<br/>" % (debut, fin),
base_url="%s&formsemestre_id=%s&debut=%s&fin=%s" base_url="%s&formsemestre_id=%s&debut=%s&fin=%s"
% (groups_infos.base_url, formsemestre_id, debut, fin), % (groups_infos.base_url, formsemestre_id, debut, fin),

View File

@ -969,7 +969,6 @@ def edit_moduleimpl_resp(moduleimpl_id):
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
'Modification du responsable du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>' 'Modification du responsable du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
% (moduleimpl_id, M["module"]["titre"]), % (moduleimpl_id, M["module"]["titre"]),
sem,
javascripts=["libjs/AutoSuggest.js"], javascripts=["libjs/AutoSuggest.js"],
cssstyles=["css/autosuggest_inquisitor.css"], cssstyles=["css/autosuggest_inquisitor.css"],
bodyOnLoad="init_tf_form('')", bodyOnLoad="init_tf_form('')",
@ -1090,7 +1089,6 @@ def edit_moduleimpl_expr(moduleimpl_id):
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
'Modification règle de calcul du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>' 'Modification règle de calcul du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
% (moduleimpl_id, M["module"]["titre"]), % (moduleimpl_id, M["module"]["titre"]),
sem,
), ),
_EXPR_HELP _EXPR_HELP
% { % {
@ -1197,7 +1195,6 @@ def view_module_abs(moduleimpl_id, format="html"):
'Absences du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>' 'Absences du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
% (moduleimpl_id, M["module"]["titre"]), % (moduleimpl_id, M["module"]["titre"]),
page_title="Absences du module %s" % (M["module"]["titre"]), page_title="Absences du module %s" % (M["module"]["titre"]),
sem=sem,
) )
] ]
if not T and format == "html": if not T and format == "html":
@ -1246,7 +1243,6 @@ def edit_ue_expr(formsemestre_id, ue_id):
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Modification règle de calcul de l'UE %s (%s)" "Modification règle de calcul de l'UE %s (%s)"
% (ue["acronyme"], ue["titre"]), % (ue["acronyme"], ue["titre"]),
sem,
), ),
_EXPR_HELP % {"target": "de l'UE", "objs": "modules", "ordre": ""}, _EXPR_HELP % {"target": "de l'UE", "objs": "modules", "ordre": ""},
] ]
@ -1384,7 +1380,7 @@ def formsemestre_enseignants_list(formsemestre_id, format="html"):
html_class="table_leftalign", html_class="table_leftalign",
filename=scu.make_filename("Enseignants-" + sem["titreannee"]), filename=scu.make_filename("Enseignants-" + sem["titreannee"]),
html_title=html_sco_header.html_sem_header( html_title=html_sco_header.html_sem_header(
"Enseignants du semestre", sem, with_page_header=False "Enseignants du semestre", with_page_header=False
), ),
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id), base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
caption="Tous les enseignants (responsables ou associés aux modules de ce semestre) apparaissent. Le nombre de saisies d'absences est le nombre d'opérations d'ajout effectuées sur ce semestre, sans tenir compte des annulations ou double saisies.", caption="Tous les enseignants (responsables ou associés aux modules de ce semestre) apparaissent. Le nombre de saisies d'absences est le nombre d'opérations d'ajout effectuées sur ce semestre, sans tenir compte des annulations ou double saisies.",
@ -1897,7 +1893,7 @@ def formsemestre_bulletins_choice(
"""Choix d'une version de bulletin""" """Choix d'une version de bulletin"""
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [ H = [
html_sco_header.html_sem_header(title, sem), html_sco_header.html_sem_header(title),
""" """
<form name="f" method="GET" action="%s"> <form name="f" method="GET" action="%s">
<input type="hidden" name="formsemestre_id" value="%s"></input> <input type="hidden" name="formsemestre_id" value="%s"></input>

View File

@ -1701,8 +1701,7 @@ def check_group_apogee(group_id, etat=None, fix=False, fixmail=False):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Etudiants du %s" % (group["group_name"] or "semestre"), "Etudiants du %s" % (group["group_name"] or "semestre")
sem,
), ),
'<table class="sortable" id="listegroupe">', '<table class="sortable" id="listegroupe">',
"<tr><th>Nom</th><th>Nom usuel</th><th>Prénom</th><th>Mail</th><th>NIP (ScoDoc)</th><th>Apogée</th></tr>", "<tr><th>Nom</th><th>Nom usuel</th><th>Prénom</th><th>Mail</th><th>NIP (ScoDoc)</th><th>Apogée</th></tr>",