forked from ScoDoc/ScoDoc
Merge branch 'modif' of https://scodoc.org/git/iziram/ScoDoc into iziram-rev
This commit is contained in:
commit
8ebe3fa6f3
@ -526,7 +526,7 @@ def _count_manager(requested) -> tuple[str, dict]:
|
|||||||
return (metric, filtered)
|
return (metric, filtered)
|
||||||
|
|
||||||
|
|
||||||
def _filter_manager(requested, assiduites_query):
|
def _filter_manager(requested, assiduites_query: Assiduite):
|
||||||
"""
|
"""
|
||||||
Retourne les assiduites entrées filtrées en fonction de la request
|
Retourne les assiduites entrées filtrées en fonction de la request
|
||||||
"""
|
"""
|
||||||
@ -538,19 +538,14 @@ def _filter_manager(requested, assiduites_query):
|
|||||||
# cas 2 : date de début
|
# cas 2 : date de début
|
||||||
deb = requested.args.get("date_debut")
|
deb = requested.args.get("date_debut")
|
||||||
deb: datetime = scu.is_iso_formated(deb, True)
|
deb: datetime = scu.is_iso_formated(deb, True)
|
||||||
if deb is not None:
|
|
||||||
|
|
||||||
assiduites_query = scass.filter_assiduites_by_date(
|
|
||||||
assiduites_query, deb, sup=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# cas 3 : date de fin
|
# cas 3 : date de fin
|
||||||
fin = requested.args.get("date_fin")
|
fin = requested.args.get("date_fin")
|
||||||
fin = scu.is_iso_formated(fin, True)
|
fin = scu.is_iso_formated(fin, True)
|
||||||
|
|
||||||
if fin is not None:
|
if (deb, fin) != (None, None):
|
||||||
assiduites_query = scass.filter_assiduites_by_date(
|
assiduites_query: Assiduite = scass.filter_by_date(
|
||||||
assiduites_query, fin, sup=False
|
assiduites_query, Assiduite, deb, fin
|
||||||
)
|
)
|
||||||
|
|
||||||
# cas 4 : moduleimpl_id
|
# cas 4 : moduleimpl_id
|
||||||
|
@ -384,7 +384,8 @@ def justif_import(justif_id: int = None):
|
|||||||
|
|
||||||
archiver: JustificatifArchiver = JustificatifArchiver()
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
try:
|
try:
|
||||||
archive_name: str = archiver.save_justificatif(
|
fname: str
|
||||||
|
archive_name, fname = archiver.save_justificatif(
|
||||||
etudid=justificatif_unique.etudid,
|
etudid=justificatif_unique.etudid,
|
||||||
filename=file.filename,
|
filename=file.filename,
|
||||||
data=file.stream.read(),
|
data=file.stream.read(),
|
||||||
@ -396,7 +397,7 @@ def justif_import(justif_id: int = None):
|
|||||||
db.session.add(justificatif_unique)
|
db.session.add(justificatif_unique)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return jsonify({"response": "imported"})
|
return jsonify({"filename": fname})
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
return json_error(404, err.args[0])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
@ -527,7 +528,6 @@ def justif_list(justif_id: int = None):
|
|||||||
|
|
||||||
|
|
||||||
# Partie justification
|
# Partie justification
|
||||||
# TODO: justificatif-justified
|
|
||||||
@bp.route("/justificatif/justified/<int:justif_id>", methods=["GET"])
|
@bp.route("/justificatif/justified/<int:justif_id>", methods=["GET"])
|
||||||
@api_web_bp.route("/justificatif/justified/<int:justif_id>", methods=["GET"])
|
@api_web_bp.route("/justificatif/justified/<int:justif_id>", methods=["GET"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@ -567,19 +567,14 @@ def _filter_manager(requested, justificatifs_query):
|
|||||||
# cas 2 : date de début
|
# cas 2 : date de début
|
||||||
deb = requested.args.get("date_debut")
|
deb = requested.args.get("date_debut")
|
||||||
deb: datetime = scu.is_iso_formated(deb, True)
|
deb: datetime = scu.is_iso_formated(deb, True)
|
||||||
if deb is not None:
|
|
||||||
|
|
||||||
justificatifs_query = scass.filter_justificatifs_by_date(
|
|
||||||
justificatifs_query, deb, sup=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# cas 3 : date de fin
|
# cas 3 : date de fin
|
||||||
fin = requested.args.get("date_fin")
|
fin = requested.args.get("date_fin")
|
||||||
fin = scu.is_iso_formated(fin, True)
|
fin = scu.is_iso_formated(fin, True)
|
||||||
|
|
||||||
if fin is not None:
|
if (deb, fin) != (None, None):
|
||||||
justificatifs_query = scass.filter_justificatifs_by_date(
|
justificatifs_query: Justificatif = scass.filter_by_date(
|
||||||
justificatifs_query, fin, sup=False
|
justificatifs_query, Justificatif, deb, fin
|
||||||
)
|
)
|
||||||
|
|
||||||
return justificatifs_query
|
return justificatifs_query
|
||||||
|
@ -15,8 +15,10 @@ class Absence(db.Model):
|
|||||||
db.Integer, db.ForeignKey("identite.id", ondelete="CASCADE"), index=True
|
db.Integer, db.ForeignKey("identite.id", ondelete="CASCADE"), index=True
|
||||||
)
|
)
|
||||||
jour = db.Column(db.Date)
|
jour = db.Column(db.Date)
|
||||||
|
# absent / justifié / absent+ justifié
|
||||||
estabs = db.Column(db.Boolean())
|
estabs = db.Column(db.Boolean())
|
||||||
estjust = db.Column(db.Boolean())
|
estjust = db.Column(db.Boolean())
|
||||||
|
|
||||||
matin = db.Column(db.Boolean())
|
matin = db.Column(db.Boolean())
|
||||||
# motif de l'absence:
|
# motif de l'absence:
|
||||||
description = db.Column(db.Text())
|
description = db.Column(db.Text())
|
||||||
|
@ -77,16 +77,15 @@ class Assiduite(db.Model):
|
|||||||
etat: EtatAssiduite,
|
etat: EtatAssiduite,
|
||||||
moduleimpl: ModuleImpl = None,
|
moduleimpl: ModuleImpl = None,
|
||||||
description: str = None,
|
description: str = None,
|
||||||
|
entry_date: datetime = 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
|
||||||
assiduites: list[Assiduite] = etud.assiduites.all()
|
assiduites: list[Assiduite] = etud.assiduites
|
||||||
|
if is_period_conflicting(date_debut, date_fin, assiduites, Assiduite):
|
||||||
if is_period_conflicting(date_debut, date_fin, assiduites):
|
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
|
"Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
|
||||||
)
|
)
|
||||||
|
|
||||||
if moduleimpl is not None:
|
if moduleimpl is not None:
|
||||||
# Vérification de l'existence du module pour l'étudiant
|
# Vérification de l'existence du module pour l'étudiant
|
||||||
if moduleimpl.est_inscrit(etud):
|
if moduleimpl.est_inscrit(etud):
|
||||||
@ -97,6 +96,7 @@ class Assiduite(db.Model):
|
|||||||
etudiant=etud,
|
etudiant=etud,
|
||||||
moduleimpl_id=moduleimpl.id,
|
moduleimpl_id=moduleimpl.id,
|
||||||
desc=description,
|
desc=description,
|
||||||
|
entry_date=entry_date,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ScoValueError("L'étudiant n'est pas inscrit au moduleimpl")
|
raise ScoValueError("L'étudiant n'est pas inscrit au moduleimpl")
|
||||||
@ -107,10 +107,37 @@ class Assiduite(db.Model):
|
|||||||
etat=etat,
|
etat=etat,
|
||||||
etudiant=etud,
|
etudiant=etud,
|
||||||
desc=description,
|
desc=description,
|
||||||
|
entry_date=entry_date,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nouv_assiduite
|
return nouv_assiduite
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fast_create_assiduite(
|
||||||
|
cls,
|
||||||
|
etudid: int,
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
etat: EtatAssiduite,
|
||||||
|
moduleimpl_id: int = None,
|
||||||
|
description: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
|
) -> object or int:
|
||||||
|
"""Créer une nouvelle assiduité pour l'étudiant"""
|
||||||
|
# Vérification de non duplication des périodes
|
||||||
|
|
||||||
|
nouv_assiduite = Assiduite(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudid=etudid,
|
||||||
|
moduleimpl_id=moduleimpl_id,
|
||||||
|
desc=description,
|
||||||
|
entry_date=entry_date,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nouv_assiduite
|
||||||
|
|
||||||
|
|
||||||
class Justificatif(db.Model):
|
class Justificatif(db.Model):
|
||||||
"""
|
"""
|
||||||
@ -178,11 +205,12 @@ class Justificatif(db.Model):
|
|||||||
date_fin: datetime,
|
date_fin: datetime,
|
||||||
etat: EtatJustificatif,
|
etat: EtatJustificatif,
|
||||||
raison: str = None,
|
raison: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
) -> object or int:
|
) -> object or int:
|
||||||
"""Créer un nouveau justificatif pour l'étudiant"""
|
"""Créer un nouveau justificatif pour l'étudiant"""
|
||||||
# Vérification de non duplication des périodes
|
# Vérification de non duplication des périodes
|
||||||
justificatifs: list[Justificatif] = etud.justificatifs.all()
|
justificatifs: list[Justificatif] = etud.justificatifs
|
||||||
if is_period_conflicting(date_debut, date_fin, justificatifs):
|
if is_period_conflicting(date_debut, date_fin, justificatifs, Justificatif):
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
|
"Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
|
||||||
)
|
)
|
||||||
@ -193,6 +221,30 @@ class Justificatif(db.Model):
|
|||||||
etat=etat,
|
etat=etat,
|
||||||
etudiant=etud,
|
etudiant=etud,
|
||||||
raison=raison,
|
raison=raison,
|
||||||
|
entry_date=entry_date,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nouv_justificatif
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fast_create_justificatif(
|
||||||
|
cls,
|
||||||
|
etudid: int,
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
etat: EtatJustificatif,
|
||||||
|
raison: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
|
) -> object or int:
|
||||||
|
"""Créer un nouveau justificatif pour l'étudiant"""
|
||||||
|
|
||||||
|
nouv_justificatif = Justificatif(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudid=etudid,
|
||||||
|
raison=raison,
|
||||||
|
entry_date=entry_date,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nouv_justificatif
|
return nouv_justificatif
|
||||||
@ -202,6 +254,7 @@ def is_period_conflicting(
|
|||||||
date_debut: datetime,
|
date_debut: datetime,
|
||||||
date_fin: datetime,
|
date_fin: datetime,
|
||||||
collection: list[Assiduite or Justificatif],
|
collection: list[Assiduite or Justificatif],
|
||||||
|
collection_cls: Assiduite or Justificatif,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Vérifie si une date n'entre pas en collision
|
Vérifie si une date n'entre pas en collision
|
||||||
@ -210,13 +263,15 @@ def is_period_conflicting(
|
|||||||
|
|
||||||
date_debut = localize_datetime(date_debut)
|
date_debut = localize_datetime(date_debut)
|
||||||
date_fin = localize_datetime(date_fin)
|
date_fin = localize_datetime(date_fin)
|
||||||
unified = [
|
|
||||||
uni
|
|
||||||
for uni in collection
|
|
||||||
if is_period_overlapping(
|
|
||||||
(date_debut, date_fin),
|
|
||||||
(uni.date_debut, uni.date_fin),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
return len(unified) != 0
|
if (
|
||||||
|
collection.filter_by(date_debut=date_debut, date_fin=date_fin).first()
|
||||||
|
is not None
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
count: int = collection.filter(
|
||||||
|
collection_cls.date_debut < date_fin, collection_cls.date_fin > date_debut
|
||||||
|
).count()
|
||||||
|
|
||||||
|
return count > 0
|
||||||
|
43
app/profiler.py
Normal file
43
app/profiler.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from time import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class Profiler:
|
||||||
|
OUTPUT: str = "/tmp/scodoc.profiler.csv"
|
||||||
|
|
||||||
|
def __init__(self, tag: str) -> None:
|
||||||
|
self.tag: str = tag
|
||||||
|
self.start_time: time = None
|
||||||
|
self.stop_time: time = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.start_time = time()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.stop_time = time()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def elapsed(self) -> float:
|
||||||
|
return self.stop_time - self.start_time
|
||||||
|
|
||||||
|
def dates(self) -> tuple[datetime, datetime]:
|
||||||
|
return datetime.fromtimestamp(self.start_time), datetime.fromtimestamp(
|
||||||
|
self.stop_time
|
||||||
|
)
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
with open(Profiler.OUTPUT, "a") as file:
|
||||||
|
dates: tuple = self.dates()
|
||||||
|
date_str = (dates[0].isoformat(), dates[1].isoformat())
|
||||||
|
file.write(f"\n{self.tag},{self.elapsed() : .2}")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def write_in(cls, msg: str):
|
||||||
|
with open(cls.OUTPUT, "a") as file:
|
||||||
|
file.write(f"\n# {msg}")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear(cls):
|
||||||
|
with open(cls.OUTPUT, "w") as file:
|
||||||
|
file.write("")
|
@ -42,6 +42,8 @@ from app.scodoc import sco_cache
|
|||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
|
from app.models import Assiduite
|
||||||
|
import app.scodoc.sco_assiduites as scass
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
# --- Misc tools.... ------------------
|
# --- Misc tools.... ------------------
|
||||||
@ -1026,7 +1028,7 @@ def get_abs_count(etudid, sem):
|
|||||||
"""
|
"""
|
||||||
return get_abs_count_in_interval(etudid, sem["date_debut_iso"], sem["date_fin_iso"])
|
return get_abs_count_in_interval(etudid, sem["date_debut_iso"], sem["date_fin_iso"])
|
||||||
|
|
||||||
# TODO: relier avec module assiduites
|
|
||||||
def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso):
|
def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso):
|
||||||
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
||||||
tuple (nb abs, nb abs justifiées)
|
tuple (nb abs, nb abs justifiées)
|
||||||
@ -1052,6 +1054,36 @@ def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def get_assiduites_count_in_interval(etudid, date_debut_iso, date_fin_iso):
|
||||||
|
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
||||||
|
tuple (nb abs, nb abs justifiées)
|
||||||
|
Utilise un cache.
|
||||||
|
"""
|
||||||
|
key = str(etudid) + "_" + date_debut_iso + "_" + date_fin_iso
|
||||||
|
r = sco_cache.AbsSemEtudCache.get(key)
|
||||||
|
if not r:
|
||||||
|
|
||||||
|
date_debut: datetime.datetime = scu.is_iso_formated(date_debut_iso, True)
|
||||||
|
date_fin: datetime.datetime = scu.is_iso_formated(date_debut_iso, True)
|
||||||
|
|
||||||
|
assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid)
|
||||||
|
|
||||||
|
assiduites = scass.filter_assiduites_by_date(assiduites, date_debut, sup=True)
|
||||||
|
assiduites = scass.filter_assiduites_by_date(assiduites, date_fin, sup=False)
|
||||||
|
|
||||||
|
nb_abs = scass.get_count(assiduites)["demi"]
|
||||||
|
nb_abs_just = count_abs_just(
|
||||||
|
etudid=etudid,
|
||||||
|
debut=date_debut_iso,
|
||||||
|
fin=date_fin_iso,
|
||||||
|
)
|
||||||
|
r = (nb_abs, nb_abs_just)
|
||||||
|
ans = sco_cache.AbsSemEtudCache.set(key, r)
|
||||||
|
if not ans:
|
||||||
|
log("warning: get_abs_count failed to cache")
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
def invalidate_abs_count(etudid, sem):
|
def invalidate_abs_count(etudid, sem):
|
||||||
"""Invalidate (clear) cached counts"""
|
"""Invalidate (clear) cached counts"""
|
||||||
date_debut = sem["date_debut_iso"]
|
date_debut = sem["date_debut_iso"]
|
||||||
|
@ -35,7 +35,6 @@ class JustificatifArchiver(BaseArchiver):
|
|||||||
"""
|
"""
|
||||||
Ajoute un fichier dans une archive "justificatif" pour l'etudid donné
|
Ajoute un fichier dans une archive "justificatif" pour l'etudid donné
|
||||||
Retourne l'archive_name utilisé
|
Retourne l'archive_name utilisé
|
||||||
TODO: renvoie archive_name + filename
|
|
||||||
"""
|
"""
|
||||||
self._set_dept(etudid)
|
self._set_dept(etudid)
|
||||||
if archive_name is None:
|
if archive_name is None:
|
||||||
@ -45,9 +44,9 @@ class JustificatifArchiver(BaseArchiver):
|
|||||||
else:
|
else:
|
||||||
archive_id: str = self.get_id_from_name(etudid, archive_name)
|
archive_id: str = self.get_id_from_name(etudid, archive_name)
|
||||||
|
|
||||||
self.store(archive_id, filename, data)
|
fname: str = self.store(archive_id, filename, data)
|
||||||
|
|
||||||
return self.get_archive_name(archive_id)
|
return self.get_archive_name(archive_id), fname
|
||||||
|
|
||||||
def delete_justificatif(self, etudid: int, archive_name: str, filename: str = None):
|
def delete_justificatif(self, etudid: int, archive_name: str, filename: str = None):
|
||||||
"""
|
"""
|
||||||
|
@ -5,7 +5,142 @@ from app.models.assiduites import Assiduite, Justificatif
|
|||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.formsemestre import FormSemestre, FormSemestreInscription
|
from app.models.formsemestre import FormSemestre, FormSemestreInscription
|
||||||
|
|
||||||
# TOTALK: Réfléchir sur le fractionnement d'une assiduite prolongée
|
|
||||||
|
class CountCalculator:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
morning: time = time(8, 0),
|
||||||
|
noon: time = time(12, 0),
|
||||||
|
after_noon: time = time(14, 00),
|
||||||
|
evening: time = time(18, 0),
|
||||||
|
skip_saturday: bool = True,
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
self.morning: time = morning
|
||||||
|
self.noon: time = noon
|
||||||
|
self.after_noon: time = after_noon
|
||||||
|
self.evening: time = evening
|
||||||
|
self.skip_saturday: bool = skip_saturday
|
||||||
|
|
||||||
|
delta_total: timedelta = datetime.combine(date.min, evening) - datetime.combine(
|
||||||
|
date.min, morning
|
||||||
|
)
|
||||||
|
delta_lunch: timedelta = datetime.combine(
|
||||||
|
date.min, after_noon
|
||||||
|
) - datetime.combine(date.min, noon)
|
||||||
|
|
||||||
|
self.hour_per_day: float = (delta_total - delta_lunch).total_seconds() / 3600
|
||||||
|
|
||||||
|
self.days: list[date] = []
|
||||||
|
self.half_days: list[tuple[date, bool]] = [] # tuple -> (date, morning:bool)
|
||||||
|
self.hours: float = 0.0
|
||||||
|
|
||||||
|
self.count: int = 0
|
||||||
|
|
||||||
|
def add_half_day(self, day: date, is_morning: bool = True):
|
||||||
|
key: tuple[date, bool] = (day, is_morning)
|
||||||
|
if key not in self.half_days:
|
||||||
|
self.half_days.append(key)
|
||||||
|
|
||||||
|
def add_day(self, day: date):
|
||||||
|
if day not in self.days:
|
||||||
|
self.days.append(day)
|
||||||
|
|
||||||
|
def check_in_morning(self, period: tuple[datetime, datetime]) -> bool:
|
||||||
|
|
||||||
|
interval_morning: tuple[datetime, datetime] = (
|
||||||
|
scu.localize_datetime(datetime.combine(period[0].date(), self.morning)),
|
||||||
|
scu.localize_datetime(datetime.combine(period[0].date(), self.noon)),
|
||||||
|
)
|
||||||
|
|
||||||
|
in_morning: bool = scu.is_period_overlapping(period, interval_morning)
|
||||||
|
return in_morning
|
||||||
|
|
||||||
|
def check_in_evening(self, period: tuple[datetime, datetime]) -> bool:
|
||||||
|
|
||||||
|
interval_evening: tuple[datetime, datetime] = (
|
||||||
|
scu.localize_datetime(datetime.combine(period[0].date(), self.after_noon)),
|
||||||
|
scu.localize_datetime(datetime.combine(period[0].date(), self.evening)),
|
||||||
|
)
|
||||||
|
|
||||||
|
in_evening: bool = scu.is_period_overlapping(period, interval_evening)
|
||||||
|
|
||||||
|
return in_evening
|
||||||
|
|
||||||
|
def compute_long_assiduite(self, assi: Assiduite):
|
||||||
|
|
||||||
|
pointer_date: date = assi.date_debut.date() + timedelta(days=1)
|
||||||
|
start_hours: timedelta = assi.date_debut - scu.localize_datetime(
|
||||||
|
datetime.combine(assi.date_debut, self.morning)
|
||||||
|
)
|
||||||
|
finish_hours: timedelta = assi.date_fin - scu.localize_datetime(
|
||||||
|
datetime.combine(assi.date_fin, self.morning)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_day(assi.date_debut.date())
|
||||||
|
self.add_day(assi.date_fin.date())
|
||||||
|
|
||||||
|
start_period: tuple[datetime, datetime] = (
|
||||||
|
assi.date_debut,
|
||||||
|
scu.localize_datetime(
|
||||||
|
datetime.combine(assi.date_debut.date(), self.evening)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
finish_period: tuple[datetime, datetime] = (
|
||||||
|
scu.localize_datetime(datetime.combine(assi.date_fin.date(), self.morning)),
|
||||||
|
assi.date_fin,
|
||||||
|
)
|
||||||
|
hours = 0.0
|
||||||
|
for period in (start_period, finish_period):
|
||||||
|
if self.check_in_evening(period):
|
||||||
|
self.add_half_day(period[0].date(), False)
|
||||||
|
if self.check_in_morning(period):
|
||||||
|
self.add_half_day(period[0].date())
|
||||||
|
|
||||||
|
while pointer_date < assi.date_fin.date():
|
||||||
|
if pointer_date.weekday() < (6 - self.skip_saturday):
|
||||||
|
self.add_day(pointer_date)
|
||||||
|
self.add_half_day(pointer_date)
|
||||||
|
self.add_half_day(pointer_date, False)
|
||||||
|
self.hours += self.hour_per_day
|
||||||
|
hours += self.hour_per_day
|
||||||
|
|
||||||
|
pointer_date += timedelta(days=1)
|
||||||
|
|
||||||
|
self.hours += finish_hours.total_seconds() / 3600
|
||||||
|
self.hours += self.hour_per_day - (start_hours.total_seconds() / 3600)
|
||||||
|
|
||||||
|
def compute_assiduites(self, assiduites: Assiduite):
|
||||||
|
assi: Assiduite
|
||||||
|
for assi in assiduites.all():
|
||||||
|
self.count += 1
|
||||||
|
delta: timedelta = assi.date_fin - assi.date_debut
|
||||||
|
|
||||||
|
if delta.days > 0:
|
||||||
|
# raise Exception(self.hours)
|
||||||
|
self.compute_long_assiduite(assi)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
period: tuple[datetime, datetime] = (assi.date_debut, assi.date_fin)
|
||||||
|
deb_date: date = assi.date_debut.date()
|
||||||
|
if self.check_in_morning(period):
|
||||||
|
self.add_half_day(deb_date)
|
||||||
|
if self.check_in_evening(period):
|
||||||
|
self.add_half_day(deb_date, False)
|
||||||
|
|
||||||
|
self.add_day(deb_date)
|
||||||
|
|
||||||
|
self.hours += delta.total_seconds() / 3600
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, object]:
|
||||||
|
return {
|
||||||
|
"compte": self.count,
|
||||||
|
"journee": len(self.days),
|
||||||
|
"demi": len(self.half_days),
|
||||||
|
"heure": round(self.hours, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_assiduites_stats(
|
def get_assiduites_stats(
|
||||||
@ -13,23 +148,24 @@ def get_assiduites_stats(
|
|||||||
) -> Assiduite:
|
) -> Assiduite:
|
||||||
|
|
||||||
if filtered is not None:
|
if filtered is not None:
|
||||||
|
deb, fin = None, None
|
||||||
for key in filtered:
|
for key in filtered:
|
||||||
if key == "etat":
|
if key == "etat":
|
||||||
assiduites = filter_assiduites_by_etat(assiduites, filtered[key])
|
assiduites = filter_assiduites_by_etat(assiduites, filtered[key])
|
||||||
elif key == "date_fin":
|
elif key == "date_fin":
|
||||||
assiduites = filter_assiduites_by_date(
|
fin = filtered[key]
|
||||||
assiduites, filtered[key], sup=False
|
|
||||||
)
|
|
||||||
elif key == "date_debut":
|
elif key == "date_debut":
|
||||||
assiduites = filter_assiduites_by_date(
|
deb = filtered[key]
|
||||||
assiduites, filtered[key], sup=True
|
|
||||||
)
|
|
||||||
elif key == "moduleimpl_id":
|
elif key == "moduleimpl_id":
|
||||||
assiduites = filter_by_module_impl(assiduites, filtered[key])
|
assiduites = filter_by_module_impl(assiduites, filtered[key])
|
||||||
elif key == "formsemestre":
|
elif key == "formsemestre":
|
||||||
assiduites = filter_by_formsemestre(assiduites, filtered[key])
|
assiduites = filter_by_formsemestre(assiduites, filtered[key])
|
||||||
|
if (deb, fin) != (None, None):
|
||||||
|
assiduites = filter_by_date(assiduites, Assiduite, deb, fin)
|
||||||
|
|
||||||
count: dict = get_count(assiduites)
|
calculator: CountCalculator = CountCalculator()
|
||||||
|
calculator.compute_assiduites(assiduites)
|
||||||
|
count: dict = calculator.to_dict()
|
||||||
|
|
||||||
metrics: list[str] = metric.split(",")
|
metrics: list[str] = metric.split(",")
|
||||||
|
|
||||||
@ -41,43 +177,104 @@ def get_assiduites_stats(
|
|||||||
return output if output else count
|
return output if output else count
|
||||||
|
|
||||||
|
|
||||||
def get_count(assiduites: Assiduite) -> dict[str, int or float]:
|
# def big_counter(
|
||||||
|
# interval: tuple[datetime],
|
||||||
|
# pref_time: time = time(12, 0),
|
||||||
|
# ):
|
||||||
|
# curr_date: datetime
|
||||||
|
|
||||||
output: dict[str, int or float] = {}
|
# if interval[0].time() >= pref_time:
|
||||||
compte: int = assiduites.count()
|
# curr_date = scu.localize_datetime(
|
||||||
heure: float = 0.0
|
# datetime.combine(interval[0].date(), pref_time)
|
||||||
journee: int = 0
|
# )
|
||||||
demi: int = 0
|
# else:
|
||||||
|
# curr_date = scu.localize_datetime(
|
||||||
|
# datetime.combine(interval[0].date(), time(0, 0))
|
||||||
|
# )
|
||||||
|
|
||||||
all_assiduites: list[Assiduite] = assiduites.order_by(Assiduite.date_debut).all()
|
# def next_(curr: datetime, journee):
|
||||||
|
# if curr.time() != pref_time:
|
||||||
|
# next_time = scu.localize_datetime(datetime.combine(curr.date(), pref_time))
|
||||||
|
# else:
|
||||||
|
# next_time = scu.localize_datetime(
|
||||||
|
# datetime.combine(curr.date() + timedelta(days=1), time(0, 0))
|
||||||
|
# )
|
||||||
|
# journee += 1
|
||||||
|
# return next_time, journee
|
||||||
|
|
||||||
current_day: date = None
|
# demi: int = 0
|
||||||
current_time: str = None
|
# j: int = 0
|
||||||
|
# while curr_date <= interval[1]:
|
||||||
|
# next_time: datetime
|
||||||
|
# next_time, j = next_(curr_date, j)
|
||||||
|
# if scu.is_period_overlapping((curr_date, next_time), interval, True):
|
||||||
|
# demi += 1
|
||||||
|
# curr_date = next_time
|
||||||
|
|
||||||
midnight: time = time(hour=0)
|
# delta: timedelta = interval[1] - interval[0]
|
||||||
noon: time = time(hour=12)
|
# heures: float = delta.total_seconds() / 3600
|
||||||
|
|
||||||
def time_check(dtime):
|
# if delta.days >= 1:
|
||||||
return midnight <= dtime.time() <= noon
|
# heures -= delta.days * 16
|
||||||
|
|
||||||
for ass in all_assiduites:
|
# return (demi, j, heures)
|
||||||
delta: timedelta = ass.date_fin - ass.date_debut
|
|
||||||
heure += delta.total_seconds() / 3600
|
|
||||||
|
|
||||||
ass_time: str = time_check(ass.date_debut)
|
|
||||||
|
|
||||||
if current_day != ass.date_debut.date():
|
# def get_count(
|
||||||
current_day = ass.date_debut.date()
|
# assiduites: Assiduite, noon: time = time(hour=12)
|
||||||
current_time = ass_time
|
# ) -> dict[str, int or float]:
|
||||||
demi += 1
|
# """Fonction permettant de compter les assiduites
|
||||||
journee += 1
|
# -> seul "compte" est correcte lorsque les assiduites viennent de plusieurs étudiants
|
||||||
|
# """
|
||||||
|
# # TODO: Comptage demi journée / journée d'assiduité longue
|
||||||
|
# output: dict[str, int or float] = {}
|
||||||
|
# compte: int = assiduites.count()
|
||||||
|
# heure: float = 0.0
|
||||||
|
# journee: int = 0
|
||||||
|
# demi: int = 0
|
||||||
|
|
||||||
if current_time != ass_time:
|
# all_assiduites: list[Assiduite] = assiduites.order_by(Assiduite.date_debut).all()
|
||||||
current_time = ass_time
|
|
||||||
demi += 1
|
|
||||||
|
|
||||||
heure = round(heure, 2)
|
# current_day: date = None
|
||||||
return {"compte": compte, "journee": journee, "heure": heure, "demi": demi}
|
# current_time: str = None
|
||||||
|
|
||||||
|
# midnight: time = time(hour=0)
|
||||||
|
|
||||||
|
# def time_check(dtime):
|
||||||
|
# return midnight <= dtime.time() <= noon
|
||||||
|
|
||||||
|
# for ass in all_assiduites:
|
||||||
|
# delta: timedelta = ass.date_fin - ass.date_debut
|
||||||
|
|
||||||
|
# if delta.days > 0:
|
||||||
|
|
||||||
|
# computed_values: tuple[int, int, float] = big_counter(
|
||||||
|
# (ass.date_debut, ass.date_fin), noon
|
||||||
|
# )
|
||||||
|
|
||||||
|
# demi += computed_values[0] - 1
|
||||||
|
# journee += computed_values[1] - 1
|
||||||
|
# heure += computed_values[2]
|
||||||
|
|
||||||
|
# current_day = ass.date_fin.date()
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# heure += delta.total_seconds() / 3600
|
||||||
|
|
||||||
|
# ass_time: str = time_check(ass.date_debut)
|
||||||
|
|
||||||
|
# if current_day != ass.date_debut.date():
|
||||||
|
# current_day = ass.date_debut.date()
|
||||||
|
# current_time = ass_time
|
||||||
|
# demi += 1
|
||||||
|
# journee += 1
|
||||||
|
|
||||||
|
# if current_time != ass_time:
|
||||||
|
# current_time = ass_time
|
||||||
|
# demi += 1
|
||||||
|
|
||||||
|
# heure = round(heure, 2)
|
||||||
|
# return {"compte": compte, "journee": journee, "heure": heure, "demi": demi}
|
||||||
|
|
||||||
|
|
||||||
def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
|
def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
|
||||||
@ -89,25 +286,30 @@ def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
|
|||||||
return assiduites.filter(Assiduite.etat.in_(etats))
|
return assiduites.filter(Assiduite.etat.in_(etats))
|
||||||
|
|
||||||
|
|
||||||
def filter_assiduites_by_date(
|
def filter_by_date(
|
||||||
assiduites: Assiduite, date_: datetime, sup: bool = True
|
collection: Assiduite or Justificatif,
|
||||||
) -> Assiduite:
|
collection_cls: Assiduite or Justificatif,
|
||||||
|
date_deb: datetime = None,
|
||||||
|
date_fin: datetime = None,
|
||||||
|
strict: bool = False,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Filtrage d'une collection d'assiduites en fonction d'une date
|
Filtrage d'une collection d'assiduites en fonction d'une date
|
||||||
|
|
||||||
Sup == True -> les assiduites doivent débuter après 'date'\n
|
|
||||||
Sup == False -> les assiduites doivent finir avant 'date'
|
|
||||||
"""
|
"""
|
||||||
|
if date_deb is None:
|
||||||
|
date_deb = datetime.min
|
||||||
|
if date_fin is None:
|
||||||
|
date_fin = datetime.max
|
||||||
|
|
||||||
if date_.tzinfo is None:
|
date_deb = scu.localize_datetime(date_deb)
|
||||||
first_assiduite: Assiduite = assiduites.first()
|
date_fin = scu.localize_datetime(date_fin)
|
||||||
if first_assiduite is not None:
|
if not strict:
|
||||||
date_: datetime = date_.replace(tzinfo=first_assiduite.date_debut.tzinfo)
|
return collection.filter(
|
||||||
|
collection_cls.date_debut <= date_fin, collection_cls.date_fin >= date_deb
|
||||||
if sup:
|
)
|
||||||
return assiduites.filter(Assiduite.date_debut >= date_)
|
return collection.filter(
|
||||||
|
collection_cls.date_debut < date_fin, collection_cls.date_fin > date_deb
|
||||||
return assiduites.filter(Assiduite.date_fin <= date_)
|
)
|
||||||
|
|
||||||
|
|
||||||
def filter_justificatifs_by_etat(
|
def filter_justificatifs_by_etat(
|
||||||
@ -190,11 +392,8 @@ def justifies(justi: Justificatif) -> list[int]:
|
|||||||
Justificatif, Assiduite.etudid == Justificatif.etudid
|
Justificatif, Assiduite.etudid == Justificatif.etudid
|
||||||
).filter(Assiduite.etat != scu.EtatAssiduite.PRESENT)
|
).filter(Assiduite.etat != scu.EtatAssiduite.PRESENT)
|
||||||
|
|
||||||
assiduites_query = filter_assiduites_by_date(
|
assiduites_query = filter_by_date(
|
||||||
assiduites_query, justi.date_debut, True
|
assiduites_query, Assiduite, justi.date_debut, justi.date_fin
|
||||||
)
|
|
||||||
assiduites_query = filter_assiduites_by_date(
|
|
||||||
assiduites_query, justi.date_fin, False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
justified = [assi.id for assi in assiduites_query.all()]
|
justified = [assi.id for assi in assiduites_query.all()]
|
||||||
|
@ -39,6 +39,7 @@ from hashlib import md5
|
|||||||
import numbers
|
import numbers
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from shutil import get_terminal_size
|
||||||
import _thread
|
import _thread
|
||||||
import time
|
import time
|
||||||
import unicodedata
|
import unicodedata
|
||||||
@ -88,6 +89,60 @@ ETATS_INSCRIPTION = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def printProgressBar(
|
||||||
|
iteration,
|
||||||
|
total,
|
||||||
|
prefix="",
|
||||||
|
suffix="",
|
||||||
|
finish_msg="",
|
||||||
|
decimals=1,
|
||||||
|
length=100,
|
||||||
|
fill="█",
|
||||||
|
autosize=False,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Affiche une progress bar à un point donné (mettre dans une boucle pour rendre dynamique)
|
||||||
|
@params:
|
||||||
|
iteration - Required : index du point donné (Int)
|
||||||
|
total - Required : nombre total avant complétion (eg: len(List))
|
||||||
|
prefix - Optional : Préfix -> écrit à gauche de la barre (Str)
|
||||||
|
suffix - Optional : Suffix -> écrit à droite de la barre (Str)
|
||||||
|
decimals - Optional : nombres de chiffres après la virgule (Int)
|
||||||
|
length - Optional : taille de la barre en nombre de caractères (Int)
|
||||||
|
fill - Optional : charactère de remplissange de la barre (Str)
|
||||||
|
autosize - Optional : Choisir automatiquement la taille de la barre en fonction du terminal (Bool)
|
||||||
|
"""
|
||||||
|
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||||
|
color = ProgressBarColors.RED
|
||||||
|
if 50 >= float(percent) > 25:
|
||||||
|
color = ProgressBarColors.MAGENTA
|
||||||
|
if 75 >= float(percent) > 50:
|
||||||
|
color = ProgressBarColors.BLUE
|
||||||
|
if 90 >= float(percent) > 75:
|
||||||
|
color = ProgressBarColors.CYAN
|
||||||
|
if 100 >= float(percent) > 90:
|
||||||
|
color = ProgressBarColors.GREEN
|
||||||
|
styling = f"{prefix} |{fill}| {percent}% {suffix}"
|
||||||
|
if autosize:
|
||||||
|
cols, _ = get_terminal_size(fallback=(length, 1))
|
||||||
|
length = cols - len(styling)
|
||||||
|
filledLength = int(length * iteration // total)
|
||||||
|
bar = fill * filledLength + "-" * (length - filledLength)
|
||||||
|
print(f"\r{color}{styling.replace(fill, bar)}{ProgressBarColors.RESET}", end="\r")
|
||||||
|
# Affiche une nouvelle ligne vide
|
||||||
|
if iteration == total:
|
||||||
|
print(f"\n{finish_msg}")
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressBarColors:
|
||||||
|
BLUE = "\033[94m"
|
||||||
|
CYAN = "\033[96m"
|
||||||
|
GREEN = "\033[92m"
|
||||||
|
MAGENTA = "\033[95m"
|
||||||
|
RED = "\033[91m"
|
||||||
|
RESET = "\033[0m"
|
||||||
|
|
||||||
|
|
||||||
class BiDirectionalEnum(Enum):
|
class BiDirectionalEnum(Enum):
|
||||||
"""Permet la recherche inverse d'un enum
|
"""Permet la recherche inverse d'un enum
|
||||||
Condition : les clés et les valeurs doivent être uniques
|
Condition : les clés et les valeurs doivent être uniques
|
||||||
@ -158,43 +213,31 @@ def localize_datetime(date: datetime.datetime or str) -> datetime.datetime:
|
|||||||
date = is_iso_formated(date, convert=True)
|
date = is_iso_formated(date, convert=True)
|
||||||
|
|
||||||
new_date: datetime.datetime = date
|
new_date: datetime.datetime = date
|
||||||
if date.tzinfo is None:
|
if date is not None and date.tzinfo is None:
|
||||||
from app.models.assiduites import Assiduite
|
# TOTALK: Paramètre scodoc pour avoir la timezone du serveur/ timezone paramétrée
|
||||||
|
time_zone: datetime.timezone = datetime.timezone(
|
||||||
first_assiduite = Assiduite.query.first()
|
datetime.timedelta(seconds=3600), "default"
|
||||||
if first_assiduite is not None:
|
)
|
||||||
new_date = date.replace(tzinfo=first_assiduite.date_debut.tzinfo)
|
new_date = date.replace(tzinfo=time_zone)
|
||||||
else:
|
|
||||||
# TOTALK: Paramètre permettant d'avoir l'UTC par défaut
|
|
||||||
tmp = is_iso_formated("2022-01-01T08:00:00+01:00", True)
|
|
||||||
new_date = date.replace(tzinfo=tmp.tzinfo)
|
|
||||||
return new_date
|
return new_date
|
||||||
|
|
||||||
|
|
||||||
def is_period_overlapping(
|
def is_period_overlapping(
|
||||||
periode: tuple[datetime.datetime, datetime.datetime],
|
periode: tuple[datetime.datetime, datetime.datetime],
|
||||||
interval: tuple[datetime.datetime, datetime.datetime],
|
interval: tuple[datetime.datetime, datetime.datetime],
|
||||||
|
bornes: bool = True,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Vérifie si la période et l'interval s'intersectent
|
Vérifie si la période et l'interval s'intersectent
|
||||||
|
si strict == True : les extrémitées ne comptes pas
|
||||||
Retourne Vrai si c'est le cas, faux sinon
|
Retourne Vrai si c'est le cas, faux sinon
|
||||||
"""
|
"""
|
||||||
p_deb, p_fin = periode
|
p_deb, p_fin = periode
|
||||||
i_deb, i_fin = interval
|
i_deb, i_fin = interval
|
||||||
|
|
||||||
# i = intervalmap()
|
if bornes:
|
||||||
# p = intervalmap()
|
return p_deb <= i_fin and p_fin >= i_deb
|
||||||
# i[:] = 0
|
return p_deb < i_fin and p_fin > i_deb
|
||||||
# p[:] = 0
|
|
||||||
# i[i_deb:i_fin] = 1
|
|
||||||
# p[p_deb:p_fin] = 1
|
|
||||||
|
|
||||||
# # TOTALK: Vérification des bornes de la période dans l'interval et inversement
|
|
||||||
# res: int = sum((i[p_deb], i[p_fin], p[i_deb], p[i_fin]))
|
|
||||||
|
|
||||||
# return res > 0
|
|
||||||
return p_deb <= i_fin and p_fin >= i_deb
|
|
||||||
|
|
||||||
|
|
||||||
# Types de modules
|
# Types de modules
|
||||||
|
33
scodoc.py
33
scodoc.py
@ -470,6 +470,39 @@ def migrate_scodoc7_dept_archives(dept: str): # migrate-scodoc7-dept-archives
|
|||||||
tools.migrate_scodoc7_dept_archives(dept)
|
tools.migrate_scodoc7_dept_archives(dept)
|
||||||
|
|
||||||
|
|
||||||
|
@app.cli.command()
|
||||||
|
@click.option(
|
||||||
|
"-d", "--dept", help="Restreint la migration au dept sélectionné (ACRONYME)"
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-m",
|
||||||
|
"--morning",
|
||||||
|
help="Spécifie l'heure de début des cours format `hh:mm`",
|
||||||
|
default="08h00",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-n",
|
||||||
|
"--noon",
|
||||||
|
help="Spécifie l'heure de fin du matin (et donc début de l'après-midi) format `hh:mm`",
|
||||||
|
default="12h00",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-e",
|
||||||
|
"--evening",
|
||||||
|
help="Spécifie l'heure de fin des cours format `hh:mm`",
|
||||||
|
default="18h00",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@with_appcontext
|
||||||
|
def migrate_abs_to_assiduites(
|
||||||
|
dept: str = None, morning: str = None, noon: str = None, evening: str = None
|
||||||
|
): # migrate-abs-to-assiduites
|
||||||
|
"""Permet de migrer les absences vers le nouveau module d'assiduités"""
|
||||||
|
tools.migrate_abs_to_assiduites(dept, morning, noon, evening)
|
||||||
|
|
||||||
|
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
@click.argument("dept", default="")
|
@click.argument("dept", default="")
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
|
@ -310,13 +310,13 @@ def test_import_justificatif(api_headers):
|
|||||||
filename: str = "tests/api/test_api_justificatif.txt"
|
filename: str = "tests/api/test_api_justificatif.txt"
|
||||||
|
|
||||||
resp: dict = send_file(1, filename, api_headers)
|
resp: dict = send_file(1, filename, api_headers)
|
||||||
assert "response" in resp
|
assert "filename" in resp
|
||||||
assert resp["response"] == "imported"
|
assert resp["filename"] == "test_api_justificatif.txt"
|
||||||
|
|
||||||
filename: str = "tests/api/test_api_justificatif2.txt"
|
filename: str = "tests/api/test_api_justificatif2.txt"
|
||||||
resp: dict = send_file(1, filename, api_headers)
|
resp: dict = send_file(1, filename, api_headers)
|
||||||
assert "response" in resp
|
assert "filename" in resp
|
||||||
assert resp["response"] == "imported"
|
assert resp["filename"] == "test_api_justificatif2.txt"
|
||||||
|
|
||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
|
|
||||||
|
@ -258,54 +258,59 @@ def verifier_filtrage_justificatifs(etud: Identite, justificatifs: list[Justific
|
|||||||
scass.filter_justificatifs_by_etat(etud.justificatifs, "autre").count() == 0
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "autre").count() == 0
|
||||||
), "Filtrage de l'état 'autre' mauvais"
|
), "Filtrage de l'état 'autre' mauvais"
|
||||||
|
|
||||||
# Date début
|
# Dates
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
|
||||||
assert (
|
assert (
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif).count() == 5
|
||||||
== 5
|
), "Filtrage 'Toute Date' mauvais 1"
|
||||||
), "Filtrage 'Date début' mauvais 1"
|
|
||||||
date = scu.localize_datetime("2022-09-03T08:00:00+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
|
||||||
== 5
|
|
||||||
), "Filtrage 'Date début' mauvais 2"
|
|
||||||
date = scu.localize_datetime("2022-09-03T09:00:00+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
|
||||||
== 4
|
|
||||||
), "Filtrage 'Date début' mauvais 3"
|
|
||||||
date = scu.localize_datetime("2022-09-03T09:00:02+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
|
||||||
== 4
|
|
||||||
), "Filtrage 'Date début' mauvais 4"
|
|
||||||
|
|
||||||
# Date fin
|
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
== 0
|
== 5
|
||||||
), "Filtrage 'Date fin' mauvais 1"
|
), "Filtrage 'Toute Date' mauvais 2"
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:00+01:00")
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T08:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
|
== 5
|
||||||
|
), "Filtrage 'date début' mauvais 3"
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T08:00:01+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
|
== 5
|
||||||
|
), "Filtrage 'date début' mauvais 4"
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T10:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
|
== 4
|
||||||
|
), "Filtrage 'date début' mauvais 5"
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
|
== 0
|
||||||
|
), "Filtrage 'Toute Date' mauvais 6"
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T08:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 1
|
== 1
|
||||||
), "Filtrage 'Date fin' mauvais 2"
|
), "Filtrage 'date début' mauvais 7"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:01+01:00")
|
date = scu.localize_datetime("2022-09-03T10:00:01+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 1
|
== 2
|
||||||
), "Filtrage 'Date fin' mauvais 3"
|
), "Filtrage 'date début' mauvais 8"
|
||||||
date = scu.localize_datetime("2023-01-04T13:00:01+01:00")
|
|
||||||
|
date = scu.localize_datetime("2023-01-03T12:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 5
|
== 5
|
||||||
), "Filtrage 'Date fin' mauvais 4"
|
), "Filtrage 'date début' mauvais 9"
|
||||||
date = scu.localize_datetime("2023-01-03T11:00:01+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
|
||||||
== 4
|
|
||||||
), "Filtrage 'Date fin' mauvais 5"
|
|
||||||
|
|
||||||
# Justifications des assiduites
|
# Justifications des assiduites
|
||||||
|
|
||||||
@ -371,7 +376,7 @@ def editer_supprimer_assiduites(etuds: list[Identite], moduleimpls: list[int]):
|
|||||||
|
|
||||||
# Vérification du changement
|
# Vérification du changement
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_etat(etuds[0].assiduites, "retard").count() == 3
|
scass.filter_assiduites_by_etat(etuds[0].assiduites, "retard").count() == 4
|
||||||
), "Edition d'assiduité mauvais"
|
), "Edition d'assiduité mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_module_impl(etuds[1].assiduites, moduleimpls[0].id).count() == 2
|
scass.filter_by_module_impl(etuds[1].assiduites, moduleimpls[0].id).count() == 2
|
||||||
@ -382,7 +387,7 @@ def editer_supprimer_assiduites(etuds: list[Identite], moduleimpls: list[int]):
|
|||||||
db.session.delete(ass3)
|
db.session.delete(ass3)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
assert etuds[2].assiduites.count() == 5, "Supression d'assiduité mauvais"
|
assert etuds[2].assiduites.count() == 6, "Supression d'assiduité mauvais"
|
||||||
|
|
||||||
|
|
||||||
def ajouter_assiduites(
|
def ajouter_assiduites(
|
||||||
@ -441,6 +446,13 @@ def ajouter_assiduites(
|
|||||||
"moduleimpl": moduleimpls[3],
|
"moduleimpl": moduleimpls[3],
|
||||||
"desc": "Description",
|
"desc": "Description",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"etat": scu.EtatAssiduite.RETARD,
|
||||||
|
"deb": "2022-11-04T11:00:01+01:00",
|
||||||
|
"fin": "2022-12-05T12:00+01:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
"desc": "Description",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
assiduites = [
|
assiduites = [
|
||||||
@ -514,10 +526,16 @@ def verifier_comptage_et_filtrage_assiduites(
|
|||||||
# Vérification du comptage classique
|
# Vérification du comptage classique
|
||||||
comptage = scass.get_assiduites_stats(etu1.assiduites)
|
comptage = scass.get_assiduites_stats(etu1.assiduites)
|
||||||
|
|
||||||
assert comptage["compte"] == 6, "la métrique 'Comptage' n'est pas bien calculée"
|
assert comptage["compte"] == 6 + 1, "la métrique 'Comptage' n'est pas bien calculée"
|
||||||
assert comptage["journee"] == 3, "la métrique 'Journée' n'est pas bien calculée"
|
assert (
|
||||||
assert comptage["demi"] == 4, "la métrique 'Demi-Journée' n'est pas bien calculée"
|
comptage["journee"] == 3 + 22
|
||||||
assert comptage["heure"] == 8, "la métrique 'Heure' n'est pas bien calculée"
|
), "la métrique 'Journée' n'est pas bien calculée"
|
||||||
|
assert (
|
||||||
|
comptage["demi"] == 4 + 43
|
||||||
|
), "la métrique 'Demi-Journée' n'est pas bien calculée"
|
||||||
|
assert comptage["heure"] == float(
|
||||||
|
8 + 169
|
||||||
|
), "la métrique 'Heure' n'est pas bien calculée"
|
||||||
|
|
||||||
# Vérification du filtrage classique
|
# Vérification du filtrage classique
|
||||||
|
|
||||||
@ -526,19 +544,19 @@ def verifier_comptage_et_filtrage_assiduites(
|
|||||||
scass.filter_assiduites_by_etat(etu2.assiduites, "present").count() == 2
|
scass.filter_assiduites_by_etat(etu2.assiduites, "present").count() == 2
|
||||||
), "Filtrage de l'état 'présent' mauvais"
|
), "Filtrage de l'état 'présent' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_etat(etu2.assiduites, "retard").count() == 2
|
scass.filter_assiduites_by_etat(etu2.assiduites, "retard").count() == 3
|
||||||
), "Filtrage de l'état 'retard' mauvais"
|
), "Filtrage de l'état 'retard' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_etat(etu2.assiduites, "absent").count() == 2
|
scass.filter_assiduites_by_etat(etu2.assiduites, "absent").count() == 2
|
||||||
), "Filtrage de l'état 'absent' mauvais"
|
), "Filtrage de l'état 'absent' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_etat(etu2.assiduites, "absent,retard").count() == 4
|
scass.filter_assiduites_by_etat(etu2.assiduites, "absent,retard").count() == 5
|
||||||
), "Filtrage de l'état 'absent,retard' mauvais"
|
), "Filtrage de l'état 'absent,retard' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_etat(
|
scass.filter_assiduites_by_etat(
|
||||||
etu2.assiduites, "absent,retard,present"
|
etu2.assiduites, "absent,retard,present"
|
||||||
).count()
|
).count()
|
||||||
== 6
|
== 7
|
||||||
), "Filtrage de l'état 'absent,retard,present' mauvais"
|
), "Filtrage de l'état 'absent,retard,present' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_etat(etu2.assiduites, "autre").count() == 0
|
scass.filter_assiduites_by_etat(etu2.assiduites, "autre").count() == 0
|
||||||
@ -558,7 +576,7 @@ def verifier_comptage_et_filtrage_assiduites(
|
|||||||
scass.filter_by_module_impl(etu3.assiduites, mod22.id).count() == 2
|
scass.filter_by_module_impl(etu3.assiduites, mod22.id).count() == 2
|
||||||
), "Filtrage par 'Moduleimpl' mauvais"
|
), "Filtrage par 'Moduleimpl' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_module_impl(etu3.assiduites, None).count() == 1
|
scass.filter_by_module_impl(etu3.assiduites, None).count() == 2
|
||||||
), "Filtrage par 'Moduleimpl' mauvais"
|
), "Filtrage par 'Moduleimpl' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_module_impl(etu3.assiduites, 152).count() == 0
|
scass.filter_by_module_impl(etu3.assiduites, 152).count() == 0
|
||||||
@ -569,7 +587,7 @@ def verifier_comptage_et_filtrage_assiduites(
|
|||||||
FormSemestre.query.filter_by(id=fms["id"]).first() for fms in formsemestres
|
FormSemestre.query.filter_by(id=fms["id"]).first() for fms in formsemestres
|
||||||
]
|
]
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_formsemestre(etu1.assiduites, formsemestres[0]).count() == 3
|
scass.filter_by_formsemestre(etu1.assiduites, formsemestres[0]).count() == 4
|
||||||
), "Filtrage 'Formsemestre' mauvais"
|
), "Filtrage 'Formsemestre' mauvais"
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_formsemestre(etu1.assiduites, formsemestres[1]).count() == 3
|
scass.filter_by_formsemestre(etu1.assiduites, formsemestres[1]).count() == 3
|
||||||
@ -579,41 +597,48 @@ def verifier_comptage_et_filtrage_assiduites(
|
|||||||
), "Filtrage 'Formsemestre' mauvais"
|
), "Filtrage 'Formsemestre' mauvais"
|
||||||
|
|
||||||
# Date début
|
# Date début
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=True).count() == 6
|
scass.filter_by_date(etu2.assiduites, Assiduite).count() == 7
|
||||||
), "Filtrage 'Date début' mauvais"
|
), "Filtrage 'Date début' mauvais 1"
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:00+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=True).count() == 5
|
|
||||||
), "Filtrage 'Date début' mauvais"
|
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:01+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=True).count() == 5
|
|
||||||
), "Filtrage 'Date début' mauvais"
|
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:02+01:00")
|
|
||||||
assert (
|
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=True).count() == 4
|
|
||||||
), "Filtrage 'Date début' mauvais"
|
|
||||||
|
|
||||||
# Date fin
|
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=False).count() == 0
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7
|
||||||
), "Filtrage 'Date fin' mauvais"
|
), "Filtrage 'Date début' mauvais 2"
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:00+01:00")
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T10:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=False).count() == 1
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7
|
||||||
), "Filtrage 'Date fin' mauvais"
|
), "Filtrage 'Date début' mauvais 3"
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T16:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 4
|
||||||
|
), "Filtrage 'Date début' mauvais 4"
|
||||||
|
|
||||||
|
# Date Fin
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 0
|
||||||
|
), "Filtrage 'Date fin' mauvais 1"
|
||||||
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T10:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 1
|
||||||
|
), "Filtrage 'Date fin' mauvais 2"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-03T10:00:01+01:00")
|
date = scu.localize_datetime("2022-09-03T10:00:01+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=False).count() == 1
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 2
|
||||||
), "Filtrage 'Date fin' mauvais"
|
), "Filtrage 'Date fin' mauvais 3"
|
||||||
date = scu.localize_datetime("2023-01-04T13:00:01+01:00")
|
|
||||||
|
date = scu.localize_datetime("2022-09-03T16:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=False).count() == 6
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 3
|
||||||
), "Filtrage 'Date fin' mauvais"
|
), "Filtrage 'Date fin' mauvais 4"
|
||||||
date = scu.localize_datetime("2023-01-03T11:00:01+01:00")
|
|
||||||
|
date = scu.localize_datetime("2023-01-04T16:00+01:00")
|
||||||
assert (
|
assert (
|
||||||
scass.filter_assiduites_by_date(etu2.assiduites, date, sup=False).count() == 4
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 7
|
||||||
), "Filtrage 'Date fin' mauvais"
|
), "Filtrage 'Date fin' mauvais 5"
|
||||||
|
@ -8,3 +8,4 @@ from tools.import_scodoc7_user_db import import_scodoc7_user_db
|
|||||||
from tools.import_scodoc7_dept import import_scodoc7_dept
|
from tools.import_scodoc7_dept import import_scodoc7_dept
|
||||||
from tools.migrate_scodoc7_archives import migrate_scodoc7_dept_archives
|
from tools.migrate_scodoc7_archives import migrate_scodoc7_dept_archives
|
||||||
from tools.migrate_scodoc7_logos import migrate_scodoc7_dept_logos
|
from tools.migrate_scodoc7_logos import migrate_scodoc7_dept_logos
|
||||||
|
from tools.migrate_abs_to_assiduites import migrate_abs_to_assiduites
|
||||||
|
345
tools/migrate_abs_to_assiduites.py
Normal file
345
tools/migrate_abs_to_assiduites.py
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
# Script de migration des données de la base "absences" -> "assiduites"/"justificatifs"
|
||||||
|
from app import db
|
||||||
|
from app.profiler import Profiler
|
||||||
|
from app.models import (
|
||||||
|
Assiduite,
|
||||||
|
Justificatif,
|
||||||
|
Absence,
|
||||||
|
Identite,
|
||||||
|
ModuleImplInscription,
|
||||||
|
Departement,
|
||||||
|
)
|
||||||
|
from app.scodoc.sco_utils import (
|
||||||
|
EtatAssiduite,
|
||||||
|
EtatJustificatif,
|
||||||
|
localize_datetime,
|
||||||
|
ProgressBarColors,
|
||||||
|
printProgressBar,
|
||||||
|
)
|
||||||
|
from datetime import time, datetime, date
|
||||||
|
from json import dump, dumps
|
||||||
|
|
||||||
|
|
||||||
|
class _glob:
|
||||||
|
DUPLICATIONS_ASSIDUITES: dict[tuple[date, bool, int], Assiduite] = {}
|
||||||
|
DUPLICATIONS_JUSTIFICATIFS: dict[tuple[date, bool, int], Justificatif] = {}
|
||||||
|
DUPLICATED: list[Justificatif] = []
|
||||||
|
PROBLEMS: dict[int, list[str]] = {}
|
||||||
|
CURRENT_ETU: list = []
|
||||||
|
MODULES: list[tuple[int, int]] = []
|
||||||
|
COMPTE: list[int, int] = []
|
||||||
|
ERR_ETU: list[int] = []
|
||||||
|
|
||||||
|
|
||||||
|
class _Statistics:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.object: dict = {"total": 0}
|
||||||
|
self.year: int = None
|
||||||
|
|
||||||
|
def __set_year(self, year: int):
|
||||||
|
if year not in self.object:
|
||||||
|
self.object[year] = {
|
||||||
|
"etuds_inexistant": [],
|
||||||
|
"abs_invalide": {},
|
||||||
|
}
|
||||||
|
self.year = year
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __add_etud(self, etudid: int):
|
||||||
|
if etudid not in self.object[self.year]["etuds_inexistant"]:
|
||||||
|
self.object[self.year]["etuds_inexistant"].append(etudid)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __add_abs(self, abs: int, err: str):
|
||||||
|
if abs not in self.object[self.year]["abs_invalide"]:
|
||||||
|
self.object[self.year]["abs_invalide"][abs] = [err]
|
||||||
|
else:
|
||||||
|
self.object[self.year]["abs_invalide"][abs].append(err)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_problem(self, abs: Absence, err: str):
|
||||||
|
abs.jour: date
|
||||||
|
pivot: date = date(abs.jour.year, 9, 15)
|
||||||
|
year: int = abs.jour.year
|
||||||
|
if pivot < abs.jour:
|
||||||
|
year += 1
|
||||||
|
self.__set_year(year)
|
||||||
|
|
||||||
|
if err == "Etudiant inexistant":
|
||||||
|
self.__add_etud(abs.etudid)
|
||||||
|
else:
|
||||||
|
self.__add_abs(abs.id, err)
|
||||||
|
|
||||||
|
self.object["total"] += 1
|
||||||
|
|
||||||
|
def compute_stats(self) -> dict:
|
||||||
|
stats: dict = {"total": self.object["total"]}
|
||||||
|
for year in self.object:
|
||||||
|
if year == "total":
|
||||||
|
continue
|
||||||
|
stats[year] = {}
|
||||||
|
stats[year]["etuds_inexistant"] = len(self.object[year]["etuds_inexistant"])
|
||||||
|
stats[year]["abs_invalide"] = len(self.object[year]["abs_invalide"])
|
||||||
|
|
||||||
|
return stats
|
||||||
|
|
||||||
|
def export(self, file):
|
||||||
|
dump(self.object, file, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_abs_to_assiduites(
|
||||||
|
dept: str = "", morning: str = None, noon: str = None, evening: str = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
une absence à 3 états:
|
||||||
|
|
||||||
|
|.estabs|.estjust|
|
||||||
|
|1|0| -> absence non justifiée
|
||||||
|
|1|1| -> absence justifiée
|
||||||
|
|0|1| -> justifié
|
||||||
|
|
||||||
|
dualité des temps :
|
||||||
|
|
||||||
|
.matin: bool (0:00 -> time_pref | time_pref->23:59:59)
|
||||||
|
.jour : date (jour de l'absence/justificatif)
|
||||||
|
.moduleimpl_id: relation -> moduleimpl_id
|
||||||
|
description:str -> motif abs / raision justif
|
||||||
|
|
||||||
|
.entry_date: datetime -> timestamp d'entrée de l'abs
|
||||||
|
.etudid: relation -> Identite
|
||||||
|
"""
|
||||||
|
Profiler.clear()
|
||||||
|
stats: _Statistics = _Statistics()
|
||||||
|
|
||||||
|
time_elapsed: Profiler = Profiler("migration")
|
||||||
|
time_elapsed.start()
|
||||||
|
|
||||||
|
if morning is None:
|
||||||
|
pref_time_morning = time(8, 0)
|
||||||
|
else:
|
||||||
|
morning: list[str] = morning.split("h")
|
||||||
|
pref_time_morning = time(int(morning[0]), int(morning[1]))
|
||||||
|
|
||||||
|
if noon is None:
|
||||||
|
pref_time_noon = time(12, 0)
|
||||||
|
else:
|
||||||
|
noon: list[str] = noon.split("h")
|
||||||
|
pref_time_noon = time(int(noon[0]), int(noon[1]))
|
||||||
|
|
||||||
|
if evening is None:
|
||||||
|
pref_time_evening = time(18, 0)
|
||||||
|
else:
|
||||||
|
evening: list[str] = evening.split("h")
|
||||||
|
pref_time_evening = time(int(evening[0]), int(evening[1]))
|
||||||
|
|
||||||
|
absences_query = Absence.query
|
||||||
|
if dept is not None:
|
||||||
|
|
||||||
|
dept: Departement = Departement.query.filter_by(acronym=dept).first()
|
||||||
|
if dept is not None:
|
||||||
|
etuds_id: list[int] = [etud.id for etud in dept.etudiants]
|
||||||
|
absences_query = absences_query.filter(Absence.etudid.in_(etuds_id))
|
||||||
|
absences: Absence = absences_query.order_by(Absence.etudid)
|
||||||
|
|
||||||
|
_glob.DUPLICATED = []
|
||||||
|
_glob.DUPLICATIONS_ASSIDUITES = {}
|
||||||
|
_glob.DUPLICATIONS_JUSTIFICATIFS = {}
|
||||||
|
_glob.CURRENT_ETU = []
|
||||||
|
_glob.MODULES = []
|
||||||
|
_glob.COMPTE = [0, 0]
|
||||||
|
_glob.ERR_ETU = []
|
||||||
|
|
||||||
|
absences_len: int = absences.count()
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"{ProgressBarColors.BLUE}{absences_len} absences vont être migrées{ProgressBarColors.RESET}"
|
||||||
|
)
|
||||||
|
|
||||||
|
printProgressBar(0, absences_len, "Progression", "effectué", autosize=True)
|
||||||
|
|
||||||
|
for i, abs in enumerate(absences):
|
||||||
|
try:
|
||||||
|
if abs.estabs:
|
||||||
|
generated = _from_abs_to_assiduite(
|
||||||
|
abs, pref_time_morning, pref_time_noon, pref_time_evening
|
||||||
|
)
|
||||||
|
if not isinstance(generated, str):
|
||||||
|
db.session.add(generated)
|
||||||
|
_glob.COMPTE[0] += 1
|
||||||
|
except Exception as e:
|
||||||
|
stats.add_problem(abs, e.args[0])
|
||||||
|
|
||||||
|
try:
|
||||||
|
if abs.estjust:
|
||||||
|
generated = _from_abs_to_justificatif(
|
||||||
|
abs, pref_time_morning, pref_time_noon, pref_time_evening
|
||||||
|
)
|
||||||
|
if not isinstance(generated, str):
|
||||||
|
db.session.add(generated)
|
||||||
|
_glob.COMPTE[1] += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
stats.add_problem(abs, e.args[0])
|
||||||
|
|
||||||
|
if i % 10 == 0:
|
||||||
|
printProgressBar(
|
||||||
|
i,
|
||||||
|
absences_len,
|
||||||
|
"Progression",
|
||||||
|
"effectué",
|
||||||
|
autosize=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if i % 1000 == 0:
|
||||||
|
printProgressBar(
|
||||||
|
i,
|
||||||
|
absences_len,
|
||||||
|
"Progression",
|
||||||
|
"effectué",
|
||||||
|
autosize=True,
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
dup_assi = _glob.DUPLICATED
|
||||||
|
assi: Assiduite
|
||||||
|
for assi in dup_assi:
|
||||||
|
assi.moduleimpl_id = None
|
||||||
|
db.session.add(assi)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
printProgressBar(
|
||||||
|
absences_len,
|
||||||
|
absences_len,
|
||||||
|
"Progression",
|
||||||
|
"effectué",
|
||||||
|
autosize=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
time_elapsed.stop()
|
||||||
|
|
||||||
|
statistiques: dict = stats.compute_stats()
|
||||||
|
print(
|
||||||
|
f"{ProgressBarColors.GREEN}La migration a pris {time_elapsed.elapsed():.2f} secondes {ProgressBarColors.RESET}"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"{ProgressBarColors.RED}{statistiques['total']} absences qui n'ont pas pu être migrée."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Vous retrouverez un fichier json {ProgressBarColors.GREEN}/tmp/scodoc_migration_abs.json{ProgressBarColors.RED} contenant les problèmes de migrations"
|
||||||
|
)
|
||||||
|
with open("/tmp/scodoc_migration_abs.json", "w", encoding="utf-8") as file:
|
||||||
|
stats.export(file)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"{ProgressBarColors.CYAN}{_glob.COMPTE[0]} assiduités et {_glob.COMPTE[1]} justificatifs ont été générés.{ProgressBarColors.RESET}"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(dumps(statistiques, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def _from_abs_to_assiduite(
|
||||||
|
_abs: Absence, morning: time, noon: time, evening: time
|
||||||
|
) -> Assiduite:
|
||||||
|
etat = EtatAssiduite.ABSENT
|
||||||
|
date_deb: datetime = None
|
||||||
|
date_fin: datetime = None
|
||||||
|
|
||||||
|
if _abs.matin:
|
||||||
|
date_deb = datetime.combine(_abs.jour, morning)
|
||||||
|
date_fin = datetime.combine(_abs.jour, noon)
|
||||||
|
else:
|
||||||
|
date_deb = datetime.combine(_abs.jour, noon)
|
||||||
|
date_fin = datetime.combine(_abs.jour, evening)
|
||||||
|
|
||||||
|
date_deb = localize_datetime(date_deb)
|
||||||
|
date_fin = localize_datetime(date_fin)
|
||||||
|
|
||||||
|
duplicata: Assiduite = _glob.DUPLICATIONS_ASSIDUITES.get(
|
||||||
|
(_abs.jour, _abs.matin, _abs.etudid)
|
||||||
|
)
|
||||||
|
if duplicata is not None:
|
||||||
|
_glob.DUPLICATED.append(duplicata)
|
||||||
|
return "Duplicata"
|
||||||
|
|
||||||
|
desc: str = _abs.description
|
||||||
|
entry_date: datetime = _abs.entry_date
|
||||||
|
|
||||||
|
if _abs.etudid not in _glob.CURRENT_ETU:
|
||||||
|
etud: Identite = Identite.query.filter_by(id=_abs.etudid).first()
|
||||||
|
if etud is None:
|
||||||
|
raise Exception("Etudiant inexistant")
|
||||||
|
_glob.CURRENT_ETU.append(_abs.etudid)
|
||||||
|
|
||||||
|
moduleimpl_id: int = _abs.moduleimpl_id
|
||||||
|
|
||||||
|
if (
|
||||||
|
moduleimpl_id is not None
|
||||||
|
and (_abs.etudid, _abs.moduleimpl_id) not in _glob.MODULES
|
||||||
|
):
|
||||||
|
moduleimpl_inscription: ModuleImplInscription = (
|
||||||
|
ModuleImplInscription.query.filter_by(
|
||||||
|
moduleimpl_id=_abs.moduleimpl_id, etudid=_abs.etudid
|
||||||
|
).first()
|
||||||
|
)
|
||||||
|
if moduleimpl_inscription is None:
|
||||||
|
raise Exception("Moduleimpl_id incorrect ou étudiant non inscrit")
|
||||||
|
|
||||||
|
retour = Assiduite.fast_create_assiduite(
|
||||||
|
etudid=_abs.etudid,
|
||||||
|
date_debut=date_deb,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
moduleimpl_id=moduleimpl_id,
|
||||||
|
description=desc,
|
||||||
|
entry_date=entry_date,
|
||||||
|
)
|
||||||
|
_glob.DUPLICATIONS_ASSIDUITES[(_abs.jour, _abs.matin, _abs.etudid)] = retour
|
||||||
|
return retour
|
||||||
|
|
||||||
|
|
||||||
|
def _from_abs_to_justificatif(
|
||||||
|
_abs: Absence, morning: time, noon: time, evening: time
|
||||||
|
) -> Justificatif:
|
||||||
|
|
||||||
|
etat = EtatJustificatif.VALIDE
|
||||||
|
date_deb: datetime = None
|
||||||
|
date_fin: datetime = None
|
||||||
|
if _abs.matin:
|
||||||
|
date_deb = datetime.combine(_abs.jour, morning)
|
||||||
|
date_fin = datetime.combine(_abs.jour, noon)
|
||||||
|
else:
|
||||||
|
date_deb = datetime.combine(_abs.jour, noon)
|
||||||
|
date_fin = datetime.combine(_abs.jour, evening)
|
||||||
|
|
||||||
|
date_deb = localize_datetime(date_deb)
|
||||||
|
date_fin = localize_datetime(date_fin)
|
||||||
|
|
||||||
|
duplicata: Justificatif = _glob.DUPLICATIONS_JUSTIFICATIFS.get(
|
||||||
|
(_abs.jour, _abs.matin, _abs.etudid)
|
||||||
|
)
|
||||||
|
if duplicata is not None:
|
||||||
|
return "Duplicated"
|
||||||
|
|
||||||
|
desc: str = _abs.description
|
||||||
|
entry_date: datetime = _abs.entry_date
|
||||||
|
|
||||||
|
if _abs.etudid not in _glob.CURRENT_ETU:
|
||||||
|
etud: Identite = Identite.query.filter_by(id=_abs.etudid).first()
|
||||||
|
if etud is None:
|
||||||
|
raise Exception("Etudiant inexistant")
|
||||||
|
_glob.CURRENT_ETU.append(_abs.etudid)
|
||||||
|
|
||||||
|
retour = Justificatif.fast_create_justificatif(
|
||||||
|
etudid=_abs.etudid,
|
||||||
|
date_debut=date_deb,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
raison=desc,
|
||||||
|
entry_date=entry_date,
|
||||||
|
)
|
||||||
|
|
||||||
|
_glob.DUPLICATIONS_JUSTIFICATIFS[(_abs.jour, _abs.matin, _abs.etudid)] = retour
|
||||||
|
return retour
|
Loading…
Reference in New Issue
Block a user