Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into table

This commit is contained in:
Emmanuel Viennet 2023-02-01 04:17:56 -03:00
commit 27b98be125
13 changed files with 107 additions and 51 deletions

View File

@ -64,7 +64,7 @@ class Formation(db.Model):
return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}""" return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}"""
def to_dict(self, with_refcomp_attrs=False): def to_dict(self, with_refcomp_attrs=False):
""" "as a dict. """As a dict.
Si with_refcomp_attrs, ajoute attributs permettant de retrouver le ref. de comp. Si with_refcomp_attrs, ajoute attributs permettant de retrouver le ref. de comp.
""" """
e = dict(self.__dict__) e = dict(self.__dict__)

View File

@ -43,6 +43,7 @@ Pour chaque étudiant commun:
comparer les résultats comparer les résultats
""" """
from flask import g, url_for
from app import log from app import log
from app.scodoc import sco_apogee_csv from app.scodoc import sco_apogee_csv
@ -72,11 +73,11 @@ def apo_compare_csv_form():
""" """
<div class="apo_compare_csv_form_but"> <div class="apo_compare_csv_form_but">
Fichier Apogée A: Fichier Apogée A:
<input type="file" size="30" name="A_file"/> <input type="file" size="30" name="file_a"/>
</div> </div>
<div class="apo_compare_csv_form_but"> <div class="apo_compare_csv_form_but">
Fichier Apogée B: Fichier Apogée B:
<input type="file" size="30" name="B_file"/> <input type="file" size="30" name="file_b"/>
</div> </div>
<input type="checkbox" name="autodetect" checked/>autodétecter encodage</input> <input type="checkbox" name="autodetect" checked/>autodétecter encodage</input>
<div class="apo_compare_csv_form_submit"> <div class="apo_compare_csv_form_submit">
@ -88,17 +89,36 @@ def apo_compare_csv_form():
return "\n".join(H) return "\n".join(H)
def apo_compare_csv(A_file, B_file, autodetect=True): def apo_compare_csv(file_a, file_b, autodetect=True):
"""Page comparing 2 Apogee CSV files""" """Page comparing 2 Apogee CSV files"""
A = _load_apo_data(A_file, autodetect=autodetect) try:
B = _load_apo_data(B_file, autodetect=autodetect) apo_data_a = _load_apo_data(file_a, autodetect=autodetect)
apo_data_b = _load_apo_data(file_b, autodetect=autodetect)
except (UnicodeDecodeError, UnicodeEncodeError) as exc:
dest_url = url_for("notes.semset_page", scodoc_dept=g.scodoc_dept)
if autodetect:
raise ScoValueError(
"""
Erreur: l'encodage de l'un des fichiers est mal détecté.
Essayez sans auto-détection, ou vérifiez le codage et le contenu
des fichiers.
""",
dest_url=dest_url,
) from exc
else:
raise ScoValueError(
f"""
Erreur: l'encodage de l'un des fichiers est incorrect.
Vérifiez qu'il est bien en {sco_apogee_csv.APO_INPUT_ENCODING}
""",
dest_url=dest_url,
) from exc
H = [ H = [
html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"), html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"),
"<h2>Comparaison de fichiers Apogée</h2>", "<h2>Comparaison de fichiers Apogée</h2>",
_help_txt, _help_txt,
'<div class="apo_compare_csv">', '<div class="apo_compare_csv">',
_apo_compare_csv(A, B), _apo_compare_csv(apo_data_a, apo_data_b),
"</div>", "</div>",
"""<p><a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a></p>""", """<p><a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a></p>""",
html_sco_header.sco_footer(), html_sco_header.sco_footer(),
@ -112,9 +132,9 @@ def _load_apo_data(csvfile, autodetect=True):
if autodetect: if autodetect:
data_b, message = sco_apogee_csv.fix_data_encoding(data_b) data_b, message = sco_apogee_csv.fix_data_encoding(data_b)
if message: if message:
log("apo_compare_csv: %s" % message) log(f"apo_compare_csv: {message}")
if not data_b: if not data_b:
raise ScoValueError("apo_compare_csv: no data") raise ScoValueError("fichier vide ? (apo_compare_csv: no data)")
data = data_b.decode(sco_apogee_csv.APO_INPUT_ENCODING) data = data_b.decode(sco_apogee_csv.APO_INPUT_ENCODING)
apo_data = sco_apogee_csv.ApoData(data, orig_filename=csvfile.filename) apo_data = sco_apogee_csv.ApoData(data, orig_filename=csvfile.filename)
return apo_data return apo_data

View File

@ -155,28 +155,25 @@ def fix_data_encoding(
text: bytes, text: bytes,
default_source_encoding=APO_INPUT_ENCODING, default_source_encoding=APO_INPUT_ENCODING,
dest_encoding=APO_INPUT_ENCODING, dest_encoding=APO_INPUT_ENCODING,
) -> bytes: ) -> tuple[bytes, str]:
"""Try to ensure that text is using dest_encoding """Try to ensure that text is using dest_encoding
returns converted text, and a message describing the conversion. returns converted text, and a message describing the conversion.
Raises UnicodeEncodeError en cas de problème, en général liée à
une auto-détection errornée.
""" """
message = "" message = ""
detected_encoding = guess_data_encoding(text) detected_encoding = guess_data_encoding(text)
if not detected_encoding: if not detected_encoding:
if default_source_encoding != dest_encoding: if default_source_encoding != dest_encoding:
message = "converting from %s to %s" % ( message = f"converting from {default_source_encoding} to {dest_encoding}"
default_source_encoding, text = text.decode(default_source_encoding).encode(dest_encoding)
dest_encoding,
)
text = text.decode(default_source_encoding).encode(
dest_encoding
) # XXX #py3 #sco8 à tester
else: else:
if detected_encoding != dest_encoding: if detected_encoding != dest_encoding:
message = "converting from detected %s to %s" % ( message = (
detected_encoding, f"converting from detected {default_source_encoding} to {dest_encoding}"
dest_encoding,
) )
text = text.decode(detected_encoding).encode(dest_encoding) # XXX text = text.decode(detected_encoding).encode(dest_encoding)
return text, message return text, message

View File

@ -591,19 +591,45 @@ def view_apo_csv_store(semset_id="", csvfile=None, data: bytes = "", autodetect=
if not semset_id: if not semset_id:
raise ValueError("invalid null semset_id") raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(semset_id=semset_id) semset = sco_semset.SemSet(semset_id=semset_id)
try:
if csvfile: if csvfile:
data = csvfile.read() # bytes data = csvfile.read() # bytes
if autodetect: if autodetect:
# check encoding (although documentation states that users SHOULD upload LATIN1) # check encoding (although documentation states that users SHOULD upload LATIN1)
data, message = sco_apogee_csv.fix_data_encoding(data) data, message = sco_apogee_csv.fix_data_encoding(data)
if message: if message:
log("view_apo_csv_store: %s" % message) log(f"view_apo_csv_store: {message}")
else: else:
log("view_apo_csv_store: autodetection of encoding disabled by user") log("view_apo_csv_store: autodetection of encoding disabled by user")
if not data: if not data:
raise ScoValueError("view_apo_csv_store: no data") raise ScoValueError("view_apo_csv_store: no data")
# data est du bytes, encodé en APO_INPUT_ENCODING # data est du bytes, encodé en APO_INPUT_ENCODING
data_str = data.decode(APO_INPUT_ENCODING) data_str = data.decode(APO_INPUT_ENCODING)
except (UnicodeDecodeError, UnicodeEncodeError) as exc:
dest_url = url_for(
"notes.apo_semset_maq_status",
scodoc_dept=g.scodoc_dept,
semset_id=semset_id,
)
if autodetect:
raise ScoValueError(
f"""
Erreur: l'encodage du fichier est mal détecté.
Essayez sans auto-détection, ou vérifiez le codage et le contenu
du fichier (qui doit être en {sco_apogee_csv.APO_INPUT_ENCODING}).
""",
dest_url=dest_url,
) from exc
else:
raise ScoValueError(
f"""
Erreur: l'encodage du fichier est incorrect.
Vérifiez qu'il est bien en {sco_apogee_csv.APO_INPUT_ENCODING}
""",
dest_url=dest_url,
) from exc
# check si etape maquette appartient bien au semset # check si etape maquette appartient bien au semset
apo_data = sco_apogee_csv.ApoData( apo_data = sco_apogee_csv.ApoData(
data_str, periode=semset["sem_id"] data_str, periode=semset["sem_id"]

View File

@ -119,6 +119,7 @@ def formation_export(
formation: Formation = Formation.query.get_or_404(formation_id) formation: Formation = Formation.query.get_or_404(formation_id)
f_dict = formation.to_dict(with_refcomp_attrs=True) f_dict = formation.to_dict(with_refcomp_attrs=True)
if not export_ids: if not export_ids:
del f_dict["id"]
del f_dict["formation_id"] del f_dict["formation_id"]
del f_dict["dept_id"] del f_dict["dept_id"]
ues = formation.ues ues = formation.ues

View File

@ -326,9 +326,9 @@ div.logo-logo img {
box-sizing: content-box; box-sizing: content-box;
margin-top: 10px; margin-top: 10px;
/* -10px */ /* -10px */
width: 80px; width: 130px;
/* adapter suivant image */ /* adapter suivant image */
padding-right: 5px; /* padding-right: 5px; */
} }
div.sidebar-bottom { div.sidebar-bottom {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -6,7 +6,7 @@
<h2>Système de gestion scolarité</h2> <h2>Système de gestion scolarité</h2>
<p>&copy; Emmanuel Viennet 2021</p> <p>&copy; Emmanuel Viennet 2023</p>
<p>Version {{ version }}</p> <p>Version {{ version }}</p>

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.4.35" SCOVERSION = "9.4.36"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"

View File

@ -114,21 +114,18 @@ def test_formation_export(api_headers):
assert r.status_code == 200 assert r.status_code == 200
export_formation = r.json() export_formation = r.json()
assert verify_fields(export_formation, FORMATION_EXPORT_FIELDS) is True assert verify_fields(export_formation, FORMATION_EXPORT_FIELDS) is True
assert isinstance(export_formation["dept_id"], int)
assert isinstance(export_formation["acronyme"], str) assert isinstance(export_formation["acronyme"], str)
assert isinstance(export_formation["titre_officiel"], str) assert isinstance(export_formation["titre_officiel"], str)
assert isinstance(export_formation["formation_code"], str) assert isinstance(export_formation["formation_code"], str)
assert export_formation["code_specialite"] is None or isinstance( assert export_formation["code_specialite"] is None or isinstance(
export_formation["code_specialite"], str export_formation["code_specialite"], str
) )
assert isinstance(export_formation["id"], int)
assert isinstance(export_formation["titre"], str) assert isinstance(export_formation["titre"], str)
assert isinstance(export_formation["version"], int) assert isinstance(export_formation["version"], int)
assert isinstance(export_formation["type_parcours"], int) assert isinstance(export_formation["type_parcours"], int)
assert export_formation["referentiel_competence_id"] is None or isinstance( assert export_formation["referentiel_competence_id"] is None or isinstance(
export_formation["referentiel_competence_id"], int export_formation["referentiel_competence_id"], int
) )
assert isinstance(export_formation["formation_id"], int)
assert isinstance(export_formation["ue"], list) assert isinstance(export_formation["ue"], list)
ues = export_formation["ue"] ues = export_formation["ue"]
@ -192,6 +189,23 @@ def test_formation_export(api_headers):
assert r_error.status_code == 404 assert r_error.status_code == 404
def test_formation_export_with_ids(api_headers):
"""
Route: /formation/<int:formation_id>/export_with_ids
"""
r = requests.get(
API_URL + "/formation/1/export_with_ids",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
export_formation = r.json()
assert verify_fields(export_formation, FORMATION_EXPORT_FIELDS) is True
assert isinstance(export_formation["id"], int)
assert isinstance(export_formation["dept_id"], int)
assert isinstance(export_formation["formation_id"], int)
def test_moduleimpl(api_headers): def test_moduleimpl(api_headers):
""" """
Route: /formation/moduleimpl/<int:moduleimpl_id> Route: /formation/moduleimpl/<int:moduleimpl_id>

View File

@ -78,10 +78,10 @@ def test_edit_users(api_admin_headers):
nb_users = len(GET("/users/query", headers=admin_h)) nb_users = len(GET("/users/query", headers=admin_h))
user = POST_JSON( user = POST_JSON(
"/user/create", "/user/create",
{"user_name": "toto", "nom": "Toto"}, {"user_name": "test_edit_users", "nom": "Toto"},
headers=admin_h, headers=admin_h,
) )
assert user["user_name"] == "toto" assert user["user_name"] == "test_edit_users"
assert user["dept"] is None assert user["dept"] is None
assert user["active"] is True assert user["active"] is True
assert (nb_users + 1) == len(GET("/users/query", headers=admin_h)) assert (nb_users + 1) == len(GET("/users/query", headers=admin_h))

View File

@ -77,18 +77,16 @@ FORMATION_FIELDS = {
} }
FORMATION_EXPORT_FIELDS = { FORMATION_EXPORT_FIELDS = {
"dept_id", # "dept_id" "id" "formation_id", pas présents sauf si export_with_ids
"acronyme", "acronyme",
"titre_officiel",
"formation_code",
"code_specialite", "code_specialite",
"id", "formation_code",
"titre",
"version",
"type_parcours",
"referentiel_competence_id", "referentiel_competence_id",
"formation_id", "titre_officiel",
"titre",
"type_parcours",
"ue", "ue",
"version",
} }
FORMATION_EXPORT_UE_FIELDS = { FORMATION_EXPORT_UE_FIELDS = {

View File

@ -178,7 +178,7 @@ def test_formations(test_client):
# --- Export de formation_id # --- Export de formation_id
exp = sco_formations.formation_export( exp = sco_formations.formation_export(
formation_id=formation_id, format="json" formation_id=formation_id, format="json", export_ids=True
).get_data(as_text=True) ).get_data(as_text=True)
assert isinstance(exp, str) assert isinstance(exp, str)
load_exp = json.loads(exp) load_exp = json.loads(exp)