forked from ScoDoc/ScoDoc
Import admission: mode avec etudid
This commit is contained in:
parent
8a49d99292
commit
af557f9c93
@ -28,7 +28,6 @@
|
|||||||
""" Importation des étudiants à partir de fichiers CSV
|
""" Importation des étudiants à partir de fichiers CSV
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import collections
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -64,6 +63,7 @@ import app.scodoc.sco_utils as scu
|
|||||||
FORMAT_FILE = "format_import_etudiants.txt"
|
FORMAT_FILE = "format_import_etudiants.txt"
|
||||||
|
|
||||||
# Champs modifiables via "Import données admission"
|
# Champs modifiables via "Import données admission"
|
||||||
|
# (nom/prénom modifiables en mode "avec etudid")
|
||||||
ADMISSION_MODIFIABLE_FIELDS = (
|
ADMISSION_MODIFIABLE_FIELDS = (
|
||||||
"code_nip",
|
"code_nip",
|
||||||
"code_ine",
|
"code_ine",
|
||||||
@ -132,19 +132,27 @@ def sco_import_format(with_codesemestre=True):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def sco_import_format_dict(with_codesemestre=True):
|
def sco_import_format_dict(with_codesemestre=True, use_etudid: bool = False):
|
||||||
"""Attribut: { 'type': , 'table', 'allow_nulls' , 'description' }"""
|
"""Attribut: { 'type': , 'table', 'allow_nulls' , 'description' }"""
|
||||||
fmt = sco_import_format(with_codesemestre=with_codesemestre)
|
fmt = sco_import_format(with_codesemestre=with_codesemestre)
|
||||||
R = collections.OrderedDict()
|
formats = {}
|
||||||
for l in fmt:
|
for l in fmt:
|
||||||
R[l[0]] = {
|
formats[l[0]] = {
|
||||||
"type": l[1],
|
"type": l[1],
|
||||||
"table": l[2],
|
"table": l[2],
|
||||||
"allow_nulls": l[3],
|
"allow_nulls": l[3],
|
||||||
"description": l[4],
|
"description": l[4],
|
||||||
"aliases": l[5],
|
"aliases": l[5],
|
||||||
}
|
}
|
||||||
return R
|
if use_etudid:
|
||||||
|
formats["etudid"] = {
|
||||||
|
"type": "int",
|
||||||
|
"table": "identite",
|
||||||
|
"allow_nulls": False,
|
||||||
|
"description": "",
|
||||||
|
"aliases": ["etudid", "id"],
|
||||||
|
}
|
||||||
|
return formats
|
||||||
|
|
||||||
|
|
||||||
def sco_import_generate_excel_sample(
|
def sco_import_generate_excel_sample(
|
||||||
@ -188,8 +196,7 @@ def sco_import_generate_excel_sample(
|
|||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||||
members = groups_infos.members
|
members = groups_infos.members
|
||||||
log(
|
log(
|
||||||
"sco_import_generate_excel_sample: group_ids=%s %d members"
|
f"sco_import_generate_excel_sample: group_ids={group_ids}, {len(members)} members"
|
||||||
% (group_ids, len(members))
|
|
||||||
)
|
)
|
||||||
titles = ["etudid"] + titles
|
titles = ["etudid"] + titles
|
||||||
titles_styles = [style] + titles_styles
|
titles_styles = [style] + titles_styles
|
||||||
@ -234,21 +241,26 @@ def students_import_excel(
|
|||||||
exclude_cols=["photo_filename"],
|
exclude_cols=["photo_filename"],
|
||||||
)
|
)
|
||||||
if return_html:
|
if return_html:
|
||||||
if formsemestre_id:
|
dest_url = (
|
||||||
dest = url_for(
|
url_for(
|
||||||
"notes.formsemestre_status",
|
"notes.formsemestre_status",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre_id,
|
||||||
)
|
)
|
||||||
else:
|
if formsemestre_id
|
||||||
dest = url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
|
else url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
|
||||||
|
)
|
||||||
H = [html_sco_header.sco_header(page_title="Import etudiants")]
|
H = [html_sco_header.sco_header(page_title="Import etudiants")]
|
||||||
H.append("<ul>")
|
H.append("<ul>")
|
||||||
for d in diag:
|
for d in diag:
|
||||||
H.append("<li>%s</li>" % d)
|
H.append(f"<li>{d}</li>")
|
||||||
H.append("</ul>")
|
H.append(
|
||||||
H.append("<p>Import terminé !</p>")
|
f"""
|
||||||
H.append('<p><a class="stdlink" href="%s">Continuer</a></p>' % dest)
|
</ul>)
|
||||||
|
<p>Import terminé !</p>
|
||||||
|
<p><a class="stdlink" href="{dest_url}">Continuer</a></p>
|
||||||
|
"""
|
||||||
|
)
|
||||||
return "\n".join(H) + html_sco_header.sco_footer()
|
return "\n".join(H) + html_sco_header.sco_footer()
|
||||||
|
|
||||||
|
|
||||||
@ -308,13 +320,13 @@ def scolars_import_excel_file(
|
|||||||
titleslist = []
|
titleslist = []
|
||||||
for t in fs:
|
for t in fs:
|
||||||
if t not in titles:
|
if t not in titles:
|
||||||
raise ScoValueError('Colonne invalide: "%s"' % t)
|
raise ScoValueError(f'Colonne invalide: "{t}"')
|
||||||
titleslist.append(t) #
|
titleslist.append(t) #
|
||||||
# ok, same titles
|
# ok, same titles
|
||||||
# Start inserting data, abort whole transaction in case of error
|
# Start inserting data, abort whole transaction in case of error
|
||||||
created_etudids = []
|
created_etudids = []
|
||||||
np_imported_homonyms = 0
|
np_imported_homonyms = 0
|
||||||
GroupIdInferers = {}
|
group_id_inferer = {}
|
||||||
try: # --- begin DB transaction
|
try: # --- begin DB transaction
|
||||||
linenum = 0
|
linenum = 0
|
||||||
for line in data[1:]:
|
for line in data[1:]:
|
||||||
@ -429,7 +441,7 @@ def scolars_import_excel_file(
|
|||||||
_import_one_student(
|
_import_one_student(
|
||||||
formsemestre_id,
|
formsemestre_id,
|
||||||
values,
|
values,
|
||||||
GroupIdInferers,
|
group_id_inferer,
|
||||||
annee_courante,
|
annee_courante,
|
||||||
created_etudids,
|
created_etudids,
|
||||||
linenum,
|
linenum,
|
||||||
@ -496,13 +508,14 @@ def scolars_import_excel_file(
|
|||||||
|
|
||||||
|
|
||||||
def students_import_admission(
|
def students_import_admission(
|
||||||
csvfile, type_admission="", formsemestre_id=None, return_html=True
|
csvfile, type_admission="", formsemestre_id=None, return_html=True, use_etudid=False
|
||||||
):
|
) -> str:
|
||||||
"import donnees admission from Excel file (v2016)"
|
"import donnees admission from Excel file (v2016)"
|
||||||
diag = scolars_import_admission(
|
diag = scolars_import_admission(
|
||||||
csvfile,
|
csvfile,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre_id,
|
||||||
type_admission=type_admission,
|
type_admission=type_admission,
|
||||||
|
use_etudid=use_etudid,
|
||||||
)
|
)
|
||||||
if return_html:
|
if return_html:
|
||||||
H = [html_sco_header.sco_header(page_title="Import données admissions")]
|
H = [html_sco_header.sco_header(page_title="Import données admissions")]
|
||||||
@ -524,6 +537,7 @@ def students_import_admission(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return "\n".join(H) + html_sco_header.sco_footer()
|
return "\n".join(H) + html_sco_header.sco_footer()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _import_one_student(
|
def _import_one_student(
|
||||||
@ -599,13 +613,15 @@ def _is_new_ine(cnx, code_ine):
|
|||||||
|
|
||||||
|
|
||||||
# ------ Fonction ré-écrite en nov 2016 pour lire des fichiers sans etudid (fichiers APB)
|
# ------ Fonction ré-écrite en nov 2016 pour lire des fichiers sans etudid (fichiers APB)
|
||||||
def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None):
|
def scolars_import_admission(
|
||||||
|
datafile, formsemestre_id=None, type_admission=None, use_etudid=False
|
||||||
|
):
|
||||||
"""Importe données admission depuis un fichier Excel quelconque
|
"""Importe données admission depuis un fichier Excel quelconque
|
||||||
par exemple ceux utilisés avec APB
|
par exemple ceux utilisés avec APB, avec ou sans etudid
|
||||||
|
|
||||||
Cherche dans ce fichier les étudiants qui correspondent à des inscrits du
|
Cherche dans ce fichier les étudiants qui correspondent à des inscrits du
|
||||||
semestre formsemestre_id.
|
semestre formsemestre_id.
|
||||||
Le fichier n'a pas l'INE ni le NIP ni l'etudid, la correspondance se fait
|
Si le fichier n'a pas d'etudid (use_etudid faux), la correspondance se fait
|
||||||
via les noms/prénoms qui doivent être égaux (la casse, les accents et caractères spéciaux
|
via les noms/prénoms qui doivent être égaux (la casse, les accents et caractères spéciaux
|
||||||
étant ignorés).
|
étant ignorés).
|
||||||
|
|
||||||
@ -617,23 +633,24 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
dans le fichier importé) du champ type_admission.
|
dans le fichier importé) du champ type_admission.
|
||||||
Si une valeur existe ou est présente dans le fichier importé, ce paramètre est ignoré.
|
Si une valeur existe ou est présente dans le fichier importé, ce paramètre est ignoré.
|
||||||
|
|
||||||
TODO:
|
|
||||||
- choix onglet du classeur
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log(f"scolars_import_admission: formsemestre_id={formsemestre_id}")
|
log(f"scolars_import_admission: formsemestre_id={formsemestre_id}")
|
||||||
|
diag: list[str] = []
|
||||||
members = sco_groups.get_group_members(
|
members = sco_groups.get_group_members(
|
||||||
sco_groups.get_default_group(formsemestre_id)
|
sco_groups.get_default_group(formsemestre_id)
|
||||||
)
|
)
|
||||||
etuds_by_nomprenom = {} # { nomprenom : etud }
|
etuds_by_nomprenom = {} # { nomprenom : etud }
|
||||||
diag = []
|
etuds_by_etudid = {} # { etudid : etud }
|
||||||
for m in members:
|
if use_etudid:
|
||||||
np = (adm_normalize_string(m["nom"]), adm_normalize_string(m["prenom"]))
|
etuds_by_etudid = {m["etudid"]: m for m in members}
|
||||||
if np in etuds_by_nomprenom:
|
else:
|
||||||
msg = "Attention: hononymie pour %s %s" % (m["nom"], m["prenom"])
|
for m in members:
|
||||||
log(msg)
|
np = (adm_normalize_string(m["nom"]), adm_normalize_string(m["prenom"]))
|
||||||
diag.append(msg)
|
if np in etuds_by_nomprenom:
|
||||||
etuds_by_nomprenom[np] = m
|
msg = f"""Attention: hononymie pour {m["nom"]} {m["prenom"]}"""
|
||||||
|
log(msg)
|
||||||
|
diag.append(msg)
|
||||||
|
etuds_by_nomprenom[np] = m
|
||||||
|
|
||||||
exceldata = datafile.read()
|
exceldata = datafile.read()
|
||||||
diag2, data = sco_excel.excel_bytes_to_list(exceldata)
|
diag2, data = sco_excel.excel_bytes_to_list(exceldata)
|
||||||
@ -644,19 +661,29 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
|
|
||||||
titles = data[0]
|
titles = data[0]
|
||||||
# idx -> ('field', convertor)
|
# idx -> ('field', convertor)
|
||||||
fields = adm_get_fields(titles, formsemestre_id)
|
fields = adm_get_fields(titles, formsemestre_id, use_etudid=use_etudid)
|
||||||
idx_nom = None
|
idx_nom = idx_prenom = idx_etudid = None
|
||||||
idx_prenom = None
|
|
||||||
for idx, field in fields.items():
|
for idx, field in fields.items():
|
||||||
if field[0] == "nom":
|
match field[0]:
|
||||||
idx_nom = idx
|
case "nom":
|
||||||
if field[0] == "prenom":
|
idx_nom = idx
|
||||||
idx_prenom = idx
|
case "prenom":
|
||||||
if (idx_nom is None) or (idx_prenom is None):
|
idx_prenom = idx
|
||||||
|
case "etudid":
|
||||||
|
idx_etudid = idx
|
||||||
|
|
||||||
|
if (not use_etudid and ((idx_nom is None) or (idx_prenom is None))) or (
|
||||||
|
use_etudid and idx_etudid is None
|
||||||
|
):
|
||||||
log("fields indices=" + ", ".join([str(x) for x in fields]))
|
log("fields indices=" + ", ".join([str(x) for x in fields]))
|
||||||
log("fields titles =" + ", ".join([fields[x][0] for x in fields]))
|
log("fields titles =" + ", ".join([x[0] for x in fields.values()]))
|
||||||
raise ScoFormatError(
|
raise ScoFormatError(
|
||||||
"scolars_import_admission: colonnes nom et prenom requises",
|
(
|
||||||
|
"""colonne etudid requise
|
||||||
|
(si l'option "Utiliser l'identifiant d'étudiant ScoDoc" est cochée)"""
|
||||||
|
if use_etudid
|
||||||
|
else "colonnes nom et prenom requises"
|
||||||
|
),
|
||||||
dest_url=url_for(
|
dest_url=url_for(
|
||||||
"scolar.form_students_import_infos_admissions",
|
"scolar.form_students_import_infos_admissions",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
@ -665,18 +692,31 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
)
|
)
|
||||||
|
|
||||||
modifiable_fields = set(ADMISSION_MODIFIABLE_FIELDS)
|
modifiable_fields = set(ADMISSION_MODIFIABLE_FIELDS)
|
||||||
|
if use_etudid:
|
||||||
|
modifiable_fields |= {"nom", "prenom"}
|
||||||
|
|
||||||
nline = 2 # la premiere ligne de donnees du fichier excel est 2
|
nline = 2 # la premiere ligne de donnees du fichier excel est 2
|
||||||
n_import = 0
|
n_import = 0
|
||||||
for line in data[1:]:
|
for line in data[1:]:
|
||||||
# Retrouve l'étudiant parmi ceux du semestre par (nom, prenom)
|
if use_etudid:
|
||||||
nom = adm_normalize_string(line[idx_nom])
|
try:
|
||||||
prenom = adm_normalize_string(line[idx_prenom])
|
etud = etuds_by_etudid.get(int(line[idx_etudid]))
|
||||||
if (nom, prenom) not in etuds_by_nomprenom:
|
except ValueError:
|
||||||
msg = f"""Étudiant <b>{line[idx_nom]} {line[idx_prenom]} inexistant</b>"""
|
etud = None
|
||||||
diag.append(msg)
|
if not etud:
|
||||||
|
msg = f"""Étudiant avec code etudid=<b>{line[idx_etudid]}</b> inexistant"""
|
||||||
|
diag.append(msg)
|
||||||
else:
|
else:
|
||||||
etud = etuds_by_nomprenom[(nom, prenom)]
|
# Retrouve l'étudiant parmi ceux du semestre par (nom, prenom)
|
||||||
|
nom = adm_normalize_string(line[idx_nom])
|
||||||
|
prenom = adm_normalize_string(line[idx_prenom])
|
||||||
|
etud = etuds_by_nomprenom.get((nom, prenom))
|
||||||
|
if not etud:
|
||||||
|
msg = (
|
||||||
|
f"""Étudiant <b>{line[idx_nom]} {line[idx_prenom]}</b> inexistant"""
|
||||||
|
)
|
||||||
|
diag.append(msg)
|
||||||
|
if etud:
|
||||||
cur_adm = sco_etud.admission_list(cnx, args={"id": etud["admission_id"]})[0]
|
cur_adm = sco_etud.admission_list(cnx, args={"id": etud["admission_id"]})[0]
|
||||||
# peuple les champs presents dans le tableau
|
# peuple les champs presents dans le tableau
|
||||||
args = {}
|
args = {}
|
||||||
@ -758,19 +798,19 @@ def adm_normalize_string(s):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def adm_get_fields(titles, formsemestre_id):
|
def adm_get_fields(titles, formsemestre_id: int, use_etudid: bool = False):
|
||||||
"""Cherche les colonnes importables dans les titres (ligne 1) du fichier excel
|
"""Cherche les colonnes importables dans les titres (ligne 1) du fichier excel
|
||||||
return: { idx : (field_name, convertor) }
|
return: { idx : (field_name, convertor) }
|
||||||
"""
|
"""
|
||||||
format_dict = sco_import_format_dict()
|
format_dict = sco_import_format_dict(use_etudid=use_etudid)
|
||||||
fields = {}
|
fields = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
for title in titles:
|
for title in titles:
|
||||||
title_n = adm_normalize_string(title)
|
title_n = adm_normalize_string(title)
|
||||||
for k in format_dict:
|
for k, fmt in format_dict.items():
|
||||||
for v in format_dict[k]["aliases"]:
|
for v in fmt["aliases"]:
|
||||||
if adm_normalize_string(v) == title_n:
|
if adm_normalize_string(v) == title_n:
|
||||||
typ = format_dict[k]["type"]
|
typ = fmt["type"]
|
||||||
if typ == "real":
|
if typ == "real":
|
||||||
convertor = adm_convert_real
|
convertor = adm_convert_real
|
||||||
elif typ == "integer" or typ == "int":
|
elif typ == "integer" or typ == "int":
|
||||||
|
@ -2365,28 +2365,36 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
|||||||
Les données sont affichées sur les fiches individuelles des étudiants.
|
Les données sont affichées sur les fiches individuelles des étudiants.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<div class="help">
|
||||||
Importer ici la feuille excel utilisée pour envoyer le classement Parcoursup.
|
<p>
|
||||||
Seuls les étudiants actuellement inscrits dans ce semestre ScoDoc seront affectés,
|
Vous pouvez importer ici la feuille excel utilisée pour envoyer
|
||||||
les autres lignes de la feuille seront ignorées.
|
le classement Parcoursup.
|
||||||
Et seules les colonnes intéressant ScoDoc
|
Seuls les étudiants actuellement inscrits dans ce semestre ScoDoc seront affectés,
|
||||||
seront importées: il est inutile d'éliminer les autres.
|
les autres lignes de la feuille seront ignorées.
|
||||||
<br>
|
Et seules les colonnes intéressant ScoDoc
|
||||||
<em>Seules les données "admission" seront modifiées
|
seront importées: il est inutile d'éliminer les autres.
|
||||||
(et pas l'identité de l'étudiant).</em>
|
</p>
|
||||||
<br>
|
<p>
|
||||||
<em>Les colonnes "nom" et "prenom" sont requises, ou bien une colonne "etudid".</em>
|
<em>Seules les données "admission" seront modifiées
|
||||||
</p>
|
(et pas l'identité de l'étudiant).</em>
|
||||||
<p>
|
</p>
|
||||||
Avant d'importer vos données, il est recommandé d'enregistrer
|
<p>
|
||||||
les informations actuelles:
|
<em>Les colonnes "nom" et "prenom" sont requises,
|
||||||
<a class="stdlink" href="{
|
ou bien une colonne "etudid" si la case
|
||||||
url_for("scolar.import_generate_admission_sample",
|
"Utiliser l'identifiant d'étudiant ScoDoc" est cochée.
|
||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
|
</em>
|
||||||
}">exporter les données actuelles de ScoDoc</a>
|
</p>
|
||||||
(ce fichier peut être ré-importé après d'éventuelles modifications)
|
<p>
|
||||||
</p>
|
Avant d'importer vos données, il est recommandé d'enregistrer
|
||||||
""",
|
les informations actuelles:
|
||||||
|
<a class="stdlink" href="{
|
||||||
|
url_for("scolar.import_generate_admission_sample",
|
||||||
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
|
||||||
|
}">exporter les données actuelles de ScoDoc</a>
|
||||||
|
(ce fichier peut être ré-importé après d'éventuelles modifications)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
@ -2397,6 +2405,15 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
|||||||
"csvfile",
|
"csvfile",
|
||||||
{"title": "Fichier Excel:", "input_type": "file", "size": 40},
|
{"title": "Fichier Excel:", "input_type": "file", "size": 40},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"use_etudid",
|
||||||
|
{
|
||||||
|
"input_type": "boolcheckbox",
|
||||||
|
"title": "Utiliser l'identifiant d'étudiant ScoDoc (<tt>etudid</tt>)",
|
||||||
|
"explanation": """si cochée, utilise le code pour retrouver dans ScoDoc
|
||||||
|
les étudiants du fichier excel. Sinon, utilise les noms/prénoms.""",
|
||||||
|
},
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"type_admission",
|
"type_admission",
|
||||||
{
|
{
|
||||||
@ -2436,6 +2453,7 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
|||||||
tf[2]["csvfile"],
|
tf[2]["csvfile"],
|
||||||
type_admission=tf[2]["type_admission"],
|
type_admission=tf[2]["type_admission"],
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre_id,
|
||||||
|
use_etudid=tf[2]["use_etudid"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.6.976"
|
SCOVERSION = "9.6.977"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user