# Script de migration des données de la base "absences" -> "assiduites"/"justificatifs" import shutil from app import db from app.models import ( Assiduite, Justificatif, Absence, Identite, ModuleImpl, Departement, ) from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif, localize_datetime from datetime import time, datetime, date class glob: DUPLICATIONS_ASSIDUITES: dict[tuple[date, bool, int], Assiduite] = {} DUPLICATED: list[Justificatif] = [] class bcolors: BLUE = "\033[94m" CYAN = "\033[96m" GREEN = "\033[92m" MAGENTA = "\033[95m" RED = "\033[91m" RESET = "\033[0m" 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 = bcolors.RED if 50 > float(percent) > 25: color = bcolors.MAGENTA if 75 > float(percent) > 50: color = bcolors.BLUE if 90 > float(percent) > 75: color = bcolors.CYAN if 100 >= float(percent) > 90: color = bcolors.GREEN styling = f"{prefix} |{fill}| {percent}% {suffix}" if autosize: cols, _ = shutil.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)}{bcolors.RESET}", end="\r") # Affiche une nouvelle ligne vide if iteration == total: print(f"\n{finish_msg}") 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 """ 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: depts_id = [dep.id for dep in Departement.query.filter_by(acronym=dept).all()] absences_query = absences_query.filter(Absence.etudid.in_(depts_id)) absences: list[Absence] = absences_query.order_by(Absence.jour).all() glob.DUPLICATED = [] glob.DUPLICATIONS_ASSIDUITES = {} absences_len: int = len(absences) printProgressBar(0, absences_len, "Progression", "effectué", autosize=True) for i, abs in enumerate(absences): 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) 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) printProgressBar( i, absences_len, "Progression", "effectué", autosize=True, ) 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, finish_msg=f"{bcolors.GREEN}Les absences ont bien été migrées.{bcolors.RESET}", ) 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 "Duplicated" desc: str = _abs.description entry_date: datetime = _abs.entry_date etud: Identite = Identite.query.filter_by(id=_abs.etudid).first() moduleimpl: ModuleImpl = ModuleImpl.query.filter_by(id=_abs.moduleimpl_id).first() retour = Assiduite.create_assiduite( etud=etud, date_debut=date_deb, date_fin=date_fin, etat=etat, moduleimpl=moduleimpl, 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) desc: str = _abs.description entry_date: datetime = _abs.entry_date etud: Identite = Identite.query.filter_by(id=_abs.etudid).first() retour = Justificatif.create_justificatif( etud=etud, date_debut=date_deb, date_fin=date_fin, etat=etat, raison=desc, entry_date=entry_date, ) return retour