Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
10 changed files with 158 additions and 37 deletions
Showing only changes of commit f540f78cd5 - Show all commits

View File

@ -594,6 +594,11 @@ def _create_singular(
desc: str = data.get("desc", None) desc: str = data.get("desc", None)
external_data = data.get("external_data", False)
if external_data is not False:
if not isinstance(external_data, dict):
errors.append("param 'external_data' : n'est pas un objet JSON")
if errors: if errors:
err: str = ", ".join(errors) err: str = ", ".join(errors)
return (404, err) return (404, err)
@ -608,6 +613,7 @@ def _create_singular(
moduleimpl=moduleimpl, moduleimpl=moduleimpl,
description=desc, description=desc,
user_id=current_user.id, user_id=current_user.id,
external_data=external_data,
) )
db.session.add(nouv_assiduite) db.session.add(nouv_assiduite)
@ -738,6 +744,13 @@ def assiduite_edit(assiduite_id: int):
else: else:
assiduite_unique.est_just = est_just assiduite_unique.est_just = est_just
external_data = data.get("external_data")
if external_data is not None:
if not isinstance(external_data, dict):
errors.append("param 'external_data' : n'est pas un objet JSON")
else:
assiduite_unique.external_data = external_data
if errors: if errors:
err: str = ", ".join(errors) err: str = ", ".join(errors)
return json_error(404, err) return json_error(404, err)

View File

@ -248,6 +248,13 @@ def _create_singular(
raison: str = data.get("raison", None) raison: str = data.get("raison", None)
external_data = data.get("external_data")
if external_data is not None:
if not isinstance(external_data, dict):
errors.append("param 'external_data' : n'est pas un objet JSON")
else:
assiduite_unique.external_data = external_data
if errors: if errors:
err: str = ", ".join(errors) err: str = ", ".join(errors)
return (404, err, None) return (404, err, None)
@ -262,6 +269,7 @@ def _create_singular(
etud=etud, etud=etud,
raison=raison, raison=raison,
user_id=current_user.id, user_id=current_user.id,
external_data=external_data,
) )
db.session.add(nouv_justificatif) db.session.add(nouv_justificatif)
@ -346,6 +354,13 @@ def justif_edit(justif_id: int):
deb = deb if deb is not None else justificatif_unique.date_debut deb = deb if deb is not None else justificatif_unique.date_debut
fin = fin if fin is not None else justificatif_unique.date_fin fin = fin if fin is not None else justificatif_unique.date_fin
external_data = data.get("external_data")
if external_data is not None:
if not isinstance(external_data, dict):
errors.append("param 'external_data' : n'est pas un objet JSON")
else:
justificatif_unique.external_data = external_data
if fin <= deb: if fin <= deb:
errors.append("param 'dates' : Date de début après date de fin") errors.append("param 'dates' : Date de début après date de fin")

View File

@ -59,6 +59,8 @@ class Assiduite(db.Model):
est_just = db.Column(db.Boolean, server_default="false", nullable=False) est_just = db.Column(db.Boolean, server_default="false", nullable=False)
external_data = db.Column(db.JSON, nullable=True)
# Déclare la relation "joined" car on va très souvent vouloir récupérer # Déclare la relation "joined" car on va très souvent vouloir récupérer
# l'étudiant en même tant que l'assiduité (perf.: évite nouvelle requete SQL) # l'étudiant en même tant que l'assiduité (perf.: évite nouvelle requete SQL)
etudiant = db.relationship("Identite", back_populates="assiduites", lazy="joined") etudiant = db.relationship("Identite", back_populates="assiduites", lazy="joined")
@ -88,6 +90,7 @@ class Assiduite(db.Model):
"entry_date": self.entry_date, "entry_date": self.entry_date,
"user_id": username, "user_id": username,
"est_just": self.est_just, "est_just": self.est_just,
"external_data": self.external_data,
} }
return data return data
@ -117,6 +120,7 @@ class Assiduite(db.Model):
entry_date: datetime = None, entry_date: datetime = None,
user_id: int = None, user_id: int = None,
est_just: bool = False, est_just: bool = False,
external_data: dict = None,
) -> object or int: ) -> object or int:
"""Créer une nouvelle assiduité pour l'étudiant""" """Créer une nouvelle assiduité pour l'étudiant"""
# Vérification de non duplication des périodes # Vérification de non duplication des périodes
@ -138,6 +142,7 @@ class Assiduite(db.Model):
entry_date=entry_date, entry_date=entry_date,
user_id=user_id, user_id=user_id,
est_just=est_just, est_just=est_just,
external_data=external_data,
) )
else: else:
raise ScoValueError("L'étudiant n'est pas inscrit au moduleimpl") raise ScoValueError("L'étudiant n'est pas inscrit au moduleimpl")
@ -151,8 +156,9 @@ class Assiduite(db.Model):
entry_date=entry_date, entry_date=entry_date,
user_id=user_id, user_id=user_id,
est_just=est_just, est_just=est_just,
external_data=external_data,
) )
db.session.add(nouv_assiduite)
log(f"create_assiduite: {etud.id} {nouv_assiduite}") log(f"create_assiduite: {etud.id} {nouv_assiduite}")
Scolog.logdb( Scolog.logdb(
method="create_assiduite", method="create_assiduite",
@ -207,8 +213,13 @@ class Justificatif(db.Model):
# Archive_id -> sco_archives_justificatifs.py # Archive_id -> sco_archives_justificatifs.py
fichier = db.Column(db.Text()) fichier = db.Column(db.Text())
# XXX Faudrait-il le déclarer "joined" comme dans Assiduite ? # Déclare la relation "joined" car on va très souvent vouloir récupérer
etudiant = db.relationship("Identite", back_populates="justificatifs") # l'étudiant en même tant que le justificatif (perf.: évite nouvelle requete SQL)
etudiant = db.relationship(
"Identite", back_populates="justificatifs", lazy="joined"
)
external_data = db.Column(db.JSON, nullable=True)
def to_dict(self, format_api: bool = False) -> dict: def to_dict(self, format_api: bool = False) -> dict:
"""transformation de l'objet en dictionnaire sérialisable""" """transformation de l'objet en dictionnaire sérialisable"""
@ -228,6 +239,7 @@ class Justificatif(db.Model):
data = { data = {
"justif_id": self.justif_id, "justif_id": self.justif_id,
"etudid": self.etudid, "etudid": self.etudid,
"code_nip": self.etudiant.code_nip,
"date_debut": self.date_debut, "date_debut": self.date_debut,
"date_fin": self.date_fin, "date_fin": self.date_fin,
"etat": etat, "etat": etat,
@ -235,6 +247,7 @@ class Justificatif(db.Model):
"fichier": self.fichier, "fichier": self.fichier,
"entry_date": self.entry_date, "entry_date": self.entry_date,
"user_id": username, "user_id": username,
"external_data": self.external_data,
} }
return data return data
@ -260,6 +273,7 @@ class Justificatif(db.Model):
raison: str = None, raison: str = None,
entry_date: datetime = None, entry_date: datetime = None,
user_id: int = None, user_id: int = None,
external_data: dict = None,
) -> object or int: ) -> object or int:
"""Créer un nouveau justificatif pour l'étudiant""" """Créer un nouveau justificatif pour l'étudiant"""
nouv_justificatif = Justificatif( nouv_justificatif = Justificatif(
@ -270,7 +284,11 @@ class Justificatif(db.Model):
raison=raison, raison=raison,
entry_date=entry_date, entry_date=entry_date,
user_id=user_id, user_id=user_id,
external_data=external_data,
) )
db.session.add(nouv_justificatif)
log(f"create_justificatif: {etud.id} {nouv_justificatif}") log(f"create_justificatif: {etud.id} {nouv_justificatif}")
Scolog.logdb( Scolog.logdb(
method="create_justificatif", method="create_justificatif",

View File

@ -269,7 +269,7 @@ function executeMassActionQueue() {
}; };
assiduite = setModuleImplId(assiduite); assiduite = setModuleImplId(assiduite);
if (assiduite.moduleimpl_id == null && window.forceModule) { if (!hasModuleImpl(assiduite) && window.forceModule) {
const html = ` const html = `
<h3>Aucun module n'a été spécifié</h3> <h3>Aucun module n'a été spécifié</h3>
`; `;
@ -868,7 +868,7 @@ function createAssiduite(etat, etudid) {
assiduite = setModuleImplId(assiduite); assiduite = setModuleImplId(assiduite);
if (assiduite.moduleimpl_id == null && window.forceModule) { if (!hasModuleImpl(assiduite) && window.forceModule) {
const html = ` const html = `
<h3>Aucun module n'a été spécifié</h3> <h3>Aucun module n'a été spécifié</h3>
`; `;
@ -922,6 +922,18 @@ function deleteAssiduite(assiduite_id) {
return true; return true;
} }
function hasModuleImpl(assiduite) {
if (assiduite.moduleimpl_id != null) return true;
if (
"external_data" in assiduite &&
assiduite.external_data instanceof Object &&
"module" in assiduite.external_data
)
return true;
return false;
}
/** /**
* *
* @param {String | Number} assiduite_id l'identifiant d'une assiduité * @param {String | Number} assiduite_id l'identifiant d'une assiduité
@ -929,14 +941,14 @@ function deleteAssiduite(assiduite_id) {
* @returns {boolean} si l'édition a fonctionné * @returns {boolean} si l'édition a fonctionné
* TODO : Rendre asynchrone * TODO : Rendre asynchrone
*/ */
function editAssiduite(assiduite_id, etat) { function editAssiduite(assiduite_id, etat, assi) {
let assiduite = { let assiduite = {
etat: etat, etat: etat,
moduleimpl_id: getModuleImplId(), external_data: assi ? assi.external_data : null,
}; };
assiduite = setModuleImplId(assiduite); assiduite = setModuleImplId(assiduite);
if (assiduite.moduleimpl_id == null && window.forceModule) { if (!hasModuleImpl(assiduite) && window.forceModule) {
const html = ` const html = `
<h3>Aucun module n'a été spécifié</h3> <h3>Aucun module n'a été spécifié</h3>
`; `;
@ -1121,7 +1133,13 @@ function assiduiteAction(element) {
if (etat === "remove") { if (etat === "remove") {
done = deleteAssiduite(assiduite_id); done = deleteAssiduite(assiduite_id);
} else { } else {
done = editAssiduite(assiduite_id, etat); done = editAssiduite(
assiduite_id,
etat,
assiduites[etudid].reduce((a) => {
if (a.assiduite_id == assiduite_id) return a;
})
);
} }
break; break;
case "conflit": case "conflit":
@ -1393,16 +1411,23 @@ function getModuleImplId() {
function setModuleImplId(assiduite, module = null) { function setModuleImplId(assiduite, module = null) {
const moduleimpl = module == null ? getModuleImplId() : module; const moduleimpl = module == null ? getModuleImplId() : module;
if (moduleimpl === "autre") { if (moduleimpl === "autre") {
if ("desc" in assiduite && assiduite["desc"] != null) { if ("external_data" in assiduite && assiduite.external_data != undefined) {
if (assiduite["desc"].indexOf("Module:Autre") == -1) { if ("module" in assiduite.external_data) {
assiduite["desc"] = "Module:Autre\n" + assiduite["desc"]; assiduite.external_data.module = "Autre";
} else {
assiduite["external_data"] = { module: "Autre" };
} }
} else { } else {
assiduite["desc"] = "Module:Autre"; assiduite["external_data"] = { module: "Autre" };
} }
assiduite["moduleimpl_id"] = null; assiduite.moduleimpl_id = null;
} else { } else {
assiduite["moduleimpl_id"] = moduleimpl; assiduite["moduleimpl_id"] = moduleimpl;
if ("external_data" in assiduite && assiduite.external_data != undefined) {
if ("module" in assiduite.external_data) {
delete assiduite.external_data.module;
}
}
} }
return assiduite; return assiduite;
} }
@ -1451,11 +1476,11 @@ function getCurrentAssiduiteModuleImplId() {
let mod = currentAssiduites[0].moduleimpl_id; let mod = currentAssiduites[0].moduleimpl_id;
if ( if (
mod == null && mod == null &&
"desc" in currentAssiduites[0] && "external_data" in currentAssiduites[0] &&
currentAssiduites[0].desc != null && currentAssiduites[0].external_data instanceof Object &&
currentAssiduites[0].desc.indexOf("Module:Autre") != -1 "module" in currentAssiduites[0].external_data
) { ) {
mod = "autre"; mod = currentAssiduites[0].external_data.module;
} }
return mod == null ? "" : mod; return mod == null ? "" : mod;
} }
@ -1665,11 +1690,11 @@ function getModuleImpl(assiduite) {
if (id == null || id == undefined) { if (id == null || id == undefined) {
if ( if (
"desc" in assiduite && "external_data" in assiduite &&
assiduite.desc != null && assiduite.external_data instanceof Object &&
assiduite.desc.indexOf("Module:Autre") != -1 "module" in assiduite.external_data
) { ) {
return "Autre"; return assiduite.external_data.module;
} else { } else {
return "Pas de module"; return "Pas de module";
} }

View File

@ -116,7 +116,7 @@
const entry_date = moment.tz(data.entry_date, TIMEZONE).format("DD/MM/YYYY HH:mm"); const entry_date = moment.tz(data.entry_date, TIMEZONE).format("DD/MM/YYYY HH:mm");
const etat = data.etat.capitalize(); const etat = data.etat.capitalize();
const desc = data.desc == null ? "" : data.desc.replace("Module:Autre\n", ""); const desc = data.desc == null ? "" : data.desc;
const id = data.assiduite_id; const id = data.assiduite_id;
const est_just = data.est_just ? "Oui" : "Non"; const est_just = data.est_just ? "Oui" : "Non";
@ -185,12 +185,12 @@
(data) => { (data) => {
let module = data.moduleimpl_id; let module = data.moduleimpl_id;
if (module == null && "external_data" in data && "module" in data.external_data) {
module = data.external_data.module.toLowerCase();
}
const etat = data.etat; const etat = data.etat;
let desc = data.desc == null ? "" : data.desc; let desc = data.desc == null ? "" : data.desc;
if (desc.indexOf("Module:Autre\n") != -1) {
desc = data.desc.replace("Module:Autre\n", "");
module = "autre";
}
const html = ` const html = `
<div class="assi-edit"> <div class="assi-edit">
<div class="assi-edit-part"> <div class="assi-edit-part">
@ -230,6 +230,7 @@
let edit = { let edit = {
"etat": etat, "etat": etat,
"desc": desc, "desc": desc,
"external_data": data.external_data
} }
edit = setModuleImplId(edit, module); edit = setModuleImplId(edit, module);

View File

@ -102,7 +102,12 @@
td.textContent = `${e.prenom.capitalize()} ${e.nom.toUpperCase()}`; td.textContent = `${e.prenom.capitalize()} ${e.nom.toUpperCase()}`;
} }
else { else {
td.textContent = `${justificatif[k]}`.capitalize() if (justificatif[k] != null) {
td.textContent = `${justificatif[k]}`.capitalize()
}
else {
td.textContent = "";
}
} }
row.appendChild(td) row.appendChild(td)
@ -295,7 +300,7 @@
</div> </div>
` `
const desc = data.raison const desc = data.raison == null ? "" : data.raison;
const fichier = data.fichier != null ? "Oui" : "Non"; const fichier = data.fichier != null ? "Oui" : "Non";
@ -304,7 +309,7 @@
const assiEdit = el.firstElementChild; const assiEdit = el.firstElementChild;
assiEdit.querySelector('#justi_etat').value = data.etat.toLowerCase(); assiEdit.querySelector('#justi_etat').value = data.etat.toLowerCase();
assiEdit.querySelector('#justi_raison').value = desc != null ? desc : ""; assiEdit.querySelector('#justi_raison').value = desc;
assiEdit.querySelector('#justi_date_debut').value = moment.tz(data.date_debut, TIMEZONE).format("YYYY-MM-DDTHH:MM") assiEdit.querySelector('#justi_date_debut').value = moment.tz(data.date_debut, TIMEZONE).format("YYYY-MM-DDTHH:MM")
assiEdit.querySelector('#justi_date_fin').value = moment.tz(data.date_fin, TIMEZONE).format("YYYY-MM-DDTHH:MM") assiEdit.querySelector('#justi_date_fin').value = moment.tz(data.date_fin, TIMEZONE).format("YYYY-MM-DDTHH:MM")

View File

@ -0,0 +1,33 @@
"""assiduites_external_data
Revision ID: 45e0a855b8eb
Revises: 50f7e0b6229f
Create Date: 2023-07-31 07:32:18.674345
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "45e0a855b8eb"
down_revision = "50f7e0b6229f"
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table("assiduites", schema=None) as batch_op:
batch_op.add_column(sa.Column("external_data", sa.JSON(), nullable=True))
with op.batch_alter_table("justificatifs", schema=None) as batch_op:
batch_op.add_column(sa.Column("external_data", sa.JSON(), nullable=True))
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("justificatifs", schema=None) as batch_op:
batch_op.drop_column("external_data")
with op.batch_alter_table("assiduites", schema=None) as batch_op:
batch_op.drop_column("external_data")

View File

@ -33,6 +33,7 @@ ASSIDUITES_FIELDS = {
"entry_date": str, "entry_date": str,
"user_id": str, "user_id": str,
"est_just": bool, "est_just": bool,
"external_data": dict,
} }
CREATE_FIELD = {"assiduite_id": int} CREATE_FIELD = {"assiduite_id": int}
@ -56,10 +57,14 @@ def check_fields(data: dict, fields: dict = None):
fields = ASSIDUITES_FIELDS fields = ASSIDUITES_FIELDS
assert set(data.keys()) == set(fields.keys()) assert set(data.keys()) == set(fields.keys())
for key in data: for key in data:
if key in ("moduleimpl_id", "desc", "user_id"): if key in ("moduleimpl_id", "desc", "user_id", "external_data"):
assert isinstance(data[key], fields[key]) or data[key] is None assert (
isinstance(data[key], fields[key]) or data[key] is None
), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]"
else: else:
assert isinstance(data[key], fields[key]) assert isinstance(
data[key], fields[key]
), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]"
def check_failure_get(path: str, headers: dict, err: str = None): def check_failure_get(path: str, headers: dict, err: str = None):

View File

@ -25,6 +25,7 @@ FAUX = 42069
JUSTIFICATIFS_FIELDS = { JUSTIFICATIFS_FIELDS = {
"justif_id": int, "justif_id": int,
"etudid": int, "etudid": int,
"code_nip": str,
"date_debut": str, "date_debut": str,
"date_fin": str, "date_fin": str,
"etat": str, "etat": str,
@ -32,6 +33,7 @@ JUSTIFICATIFS_FIELDS = {
"entry_date": str, "entry_date": str,
"fichier": str, "fichier": str,
"user_id": int, "user_id": int,
"external_data": dict,
} }
CREATE_FIELD = {"justif_id": int, "couverture": list} CREATE_FIELD = {"justif_id": int, "couverture": list}
@ -53,10 +55,14 @@ def check_fields(data, fields: dict = None):
fields = JUSTIFICATIFS_FIELDS fields = JUSTIFICATIFS_FIELDS
assert set(data.keys()) == set(fields.keys()) assert set(data.keys()) == set(fields.keys())
for key in data: for key in data:
if key in ("raison", "fichier", "user_id"): if key in ("raison", "fichier", "user_id", "external_data"):
assert isinstance(data[key], fields[key]) or data[key] is None assert (
isinstance(data[key], fields[key]) or data[key] is None
), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]"
else: else:
assert isinstance(data[key], fields[key]) assert isinstance(
data[key], fields[key]
), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]"
def check_failure_get(path, headers, err=None): def check_failure_get(path, headers, err=None):

View File

@ -648,7 +648,7 @@ def verifier_filtrage_justificatifs(etud: Identite, justificatifs: list[Justific
# Justifications des assiduites # Justifications des assiduites
assert len(scass.justifies(justificatifs[2])) == 2, "Justifications mauvais" assert len(scass.justifies(justificatifs[2])) == 1, "Justifications mauvais"
assert len(scass.justifies(justificatifs[0])) == 0, "Justifications mauvais" assert len(scass.justifies(justificatifs[0])) == 0, "Justifications mauvais"