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
|
||||
"""
|
||||
|
||||
import collections
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
@ -64,6 +63,7 @@ import app.scodoc.sco_utils as scu
|
||||
FORMAT_FILE = "format_import_etudiants.txt"
|
||||
|
||||
# Champs modifiables via "Import données admission"
|
||||
# (nom/prénom modifiables en mode "avec etudid")
|
||||
ADMISSION_MODIFIABLE_FIELDS = (
|
||||
"code_nip",
|
||||
"code_ine",
|
||||
@ -132,19 +132,27 @@ def sco_import_format(with_codesemestre=True):
|
||||
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' }"""
|
||||
fmt = sco_import_format(with_codesemestre=with_codesemestre)
|
||||
R = collections.OrderedDict()
|
||||
formats = {}
|
||||
for l in fmt:
|
||||
R[l[0]] = {
|
||||
formats[l[0]] = {
|
||||
"type": l[1],
|
||||
"table": l[2],
|
||||
"allow_nulls": l[3],
|
||||
"description": l[4],
|
||||
"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(
|
||||
@ -188,8 +196,7 @@ def sco_import_generate_excel_sample(
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
members = groups_infos.members
|
||||
log(
|
||||
"sco_import_generate_excel_sample: group_ids=%s %d members"
|
||||
% (group_ids, len(members))
|
||||
f"sco_import_generate_excel_sample: group_ids={group_ids}, {len(members)} members"
|
||||
)
|
||||
titles = ["etudid"] + titles
|
||||
titles_styles = [style] + titles_styles
|
||||
@ -234,21 +241,26 @@ def students_import_excel(
|
||||
exclude_cols=["photo_filename"],
|
||||
)
|
||||
if return_html:
|
||||
if formsemestre_id:
|
||||
dest = url_for(
|
||||
dest_url = (
|
||||
url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
else:
|
||||
dest = url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
|
||||
if formsemestre_id
|
||||
else url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
|
||||
)
|
||||
H = [html_sco_header.sco_header(page_title="Import etudiants")]
|
||||
H.append("<ul>")
|
||||
for d in diag:
|
||||
H.append("<li>%s</li>" % d)
|
||||
H.append("</ul>")
|
||||
H.append("<p>Import terminé !</p>")
|
||||
H.append('<p><a class="stdlink" href="%s">Continuer</a></p>' % dest)
|
||||
H.append(f"<li>{d}</li>")
|
||||
H.append(
|
||||
f"""
|
||||
</ul>)
|
||||
<p>Import terminé !</p>
|
||||
<p><a class="stdlink" href="{dest_url}">Continuer</a></p>
|
||||
"""
|
||||
)
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
@ -308,13 +320,13 @@ def scolars_import_excel_file(
|
||||
titleslist = []
|
||||
for t in fs:
|
||||
if t not in titles:
|
||||
raise ScoValueError('Colonne invalide: "%s"' % t)
|
||||
raise ScoValueError(f'Colonne invalide: "{t}"')
|
||||
titleslist.append(t) #
|
||||
# ok, same titles
|
||||
# Start inserting data, abort whole transaction in case of error
|
||||
created_etudids = []
|
||||
np_imported_homonyms = 0
|
||||
GroupIdInferers = {}
|
||||
group_id_inferer = {}
|
||||
try: # --- begin DB transaction
|
||||
linenum = 0
|
||||
for line in data[1:]:
|
||||
@ -429,7 +441,7 @@ def scolars_import_excel_file(
|
||||
_import_one_student(
|
||||
formsemestre_id,
|
||||
values,
|
||||
GroupIdInferers,
|
||||
group_id_inferer,
|
||||
annee_courante,
|
||||
created_etudids,
|
||||
linenum,
|
||||
@ -496,13 +508,14 @@ def scolars_import_excel_file(
|
||||
|
||||
|
||||
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)"
|
||||
diag = scolars_import_admission(
|
||||
csvfile,
|
||||
formsemestre_id=formsemestre_id,
|
||||
type_admission=type_admission,
|
||||
use_etudid=use_etudid,
|
||||
)
|
||||
if return_html:
|
||||
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 ""
|
||||
|
||||
|
||||
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)
|
||||
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
|
||||
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
|
||||
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
|
||||
étant ignorés).
|
||||
|
||||
@ -617,20 +633,21 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
||||
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é.
|
||||
|
||||
TODO:
|
||||
- choix onglet du classeur
|
||||
"""
|
||||
|
||||
log(f"scolars_import_admission: formsemestre_id={formsemestre_id}")
|
||||
diag: list[str] = []
|
||||
members = sco_groups.get_group_members(
|
||||
sco_groups.get_default_group(formsemestre_id)
|
||||
)
|
||||
etuds_by_nomprenom = {} # { nomprenom : etud }
|
||||
diag = []
|
||||
etuds_by_etudid = {} # { etudid : etud }
|
||||
if use_etudid:
|
||||
etuds_by_etudid = {m["etudid"]: m for m in members}
|
||||
else:
|
||||
for m in members:
|
||||
np = (adm_normalize_string(m["nom"]), adm_normalize_string(m["prenom"]))
|
||||
if np in etuds_by_nomprenom:
|
||||
msg = "Attention: hononymie pour %s %s" % (m["nom"], m["prenom"])
|
||||
msg = f"""Attention: hononymie pour {m["nom"]} {m["prenom"]}"""
|
||||
log(msg)
|
||||
diag.append(msg)
|
||||
etuds_by_nomprenom[np] = m
|
||||
@ -644,19 +661,29 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
||||
|
||||
titles = data[0]
|
||||
# idx -> ('field', convertor)
|
||||
fields = adm_get_fields(titles, formsemestre_id)
|
||||
idx_nom = None
|
||||
idx_prenom = None
|
||||
fields = adm_get_fields(titles, formsemestre_id, use_etudid=use_etudid)
|
||||
idx_nom = idx_prenom = idx_etudid = None
|
||||
for idx, field in fields.items():
|
||||
if field[0] == "nom":
|
||||
match field[0]:
|
||||
case "nom":
|
||||
idx_nom = idx
|
||||
if field[0] == "prenom":
|
||||
case "prenom":
|
||||
idx_prenom = idx
|
||||
if (idx_nom is None) or (idx_prenom is None):
|
||||
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 titles =" + ", ".join([fields[x][0] for x in fields]))
|
||||
log("fields titles =" + ", ".join([x[0] for x in fields.values()]))
|
||||
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(
|
||||
"scolar.form_students_import_infos_admissions",
|
||||
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)
|
||||
if use_etudid:
|
||||
modifiable_fields |= {"nom", "prenom"}
|
||||
|
||||
nline = 2 # la premiere ligne de donnees du fichier excel est 2
|
||||
n_import = 0
|
||||
for line in data[1:]:
|
||||
if use_etudid:
|
||||
try:
|
||||
etud = etuds_by_etudid.get(int(line[idx_etudid]))
|
||||
except ValueError:
|
||||
etud = None
|
||||
if not etud:
|
||||
msg = f"""Étudiant avec code etudid=<b>{line[idx_etudid]}</b> inexistant"""
|
||||
diag.append(msg)
|
||||
else:
|
||||
# Retrouve l'étudiant parmi ceux du semestre par (nom, prenom)
|
||||
nom = adm_normalize_string(line[idx_nom])
|
||||
prenom = adm_normalize_string(line[idx_prenom])
|
||||
if (nom, prenom) not in etuds_by_nomprenom:
|
||||
msg = f"""Étudiant <b>{line[idx_nom]} {line[idx_prenom]} inexistant</b>"""
|
||||
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)
|
||||
else:
|
||||
etud = etuds_by_nomprenom[(nom, prenom)]
|
||||
if etud:
|
||||
cur_adm = sco_etud.admission_list(cnx, args={"id": etud["admission_id"]})[0]
|
||||
# peuple les champs presents dans le tableau
|
||||
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
|
||||
return: { idx : (field_name, convertor) }
|
||||
"""
|
||||
format_dict = sco_import_format_dict()
|
||||
format_dict = sco_import_format_dict(use_etudid=use_etudid)
|
||||
fields = {}
|
||||
idx = 0
|
||||
for title in titles:
|
||||
title_n = adm_normalize_string(title)
|
||||
for k in format_dict:
|
||||
for v in format_dict[k]["aliases"]:
|
||||
for k, fmt in format_dict.items():
|
||||
for v in fmt["aliases"]:
|
||||
if adm_normalize_string(v) == title_n:
|
||||
typ = format_dict[k]["type"]
|
||||
typ = fmt["type"]
|
||||
if typ == "real":
|
||||
convertor = adm_convert_real
|
||||
elif typ == "integer" or typ == "int":
|
||||
|
@ -2365,17 +2365,24 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
||||
Les données sont affichées sur les fiches individuelles des étudiants.
|
||||
</p>
|
||||
</div>
|
||||
<div class="help">
|
||||
<p>
|
||||
Importer ici la feuille excel utilisée pour envoyer le classement Parcoursup.
|
||||
Vous pouvez importer ici la feuille excel utilisée pour envoyer
|
||||
le classement Parcoursup.
|
||||
Seuls les étudiants actuellement inscrits dans ce semestre ScoDoc seront affectés,
|
||||
les autres lignes de la feuille seront ignorées.
|
||||
Et seules les colonnes intéressant ScoDoc
|
||||
seront importées: il est inutile d'éliminer les autres.
|
||||
<br>
|
||||
</p>
|
||||
<p>
|
||||
<em>Seules les données "admission" seront modifiées
|
||||
(et pas l'identité de l'étudiant).</em>
|
||||
<br>
|
||||
<em>Les colonnes "nom" et "prenom" sont requises, ou bien une colonne "etudid".</em>
|
||||
</p>
|
||||
<p>
|
||||
<em>Les colonnes "nom" et "prenom" sont requises,
|
||||
ou bien une colonne "etudid" si la case
|
||||
"Utiliser l'identifiant d'étudiant ScoDoc" est cochée.
|
||||
</em>
|
||||
</p>
|
||||
<p>
|
||||
Avant d'importer vos données, il est recommandé d'enregistrer
|
||||
@ -2386,6 +2393,7 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
||||
}">exporter les données actuelles de ScoDoc</a>
|
||||
(ce fichier peut être ré-importé après d'éventuelles modifications)
|
||||
</p>
|
||||
</div>
|
||||
""",
|
||||
]
|
||||
|
||||
@ -2397,6 +2405,15 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
||||
"csvfile",
|
||||
{"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",
|
||||
{
|
||||
@ -2436,6 +2453,7 @@ def form_students_import_infos_admissions(formsemestre_id=None):
|
||||
tf[2]["csvfile"],
|
||||
type_admission=tf[2]["type_admission"],
|
||||
formsemestre_id=formsemestre_id,
|
||||
use_etudid=tf[2]["use_etudid"],
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.976"
|
||||
SCOVERSION = "9.6.977"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user