2023-02-07 18:49:51 +01:00
# Script de migration des données de la base "absences" -> "assiduites"/"justificatifs"
from app import db
2023-02-09 21:04:53 +01:00
from app . profiler import Profiler
2023-02-07 18:49:51 +01:00
from app . models import (
Assiduite ,
Justificatif ,
Absence ,
Identite ,
2023-02-09 21:04:53 +01:00
ModuleImplInscription ,
2023-02-07 18:49:51 +01:00
Departement ,
)
2023-02-09 21:04:53 +01:00
from app . scodoc . sco_utils import (
EtatAssiduite ,
EtatJustificatif ,
localize_datetime ,
ProgressBarColors ,
printProgressBar ,
)
2023-02-07 18:49:51 +01:00
from datetime import time , datetime , date
2023-02-10 14:08:31 +01:00
from json import dump , dumps
2023-02-07 18:49:51 +01:00
2023-02-09 21:04:53 +01:00
class _glob :
2023-02-07 18:49:51 +01:00
DUPLICATIONS_ASSIDUITES : dict [ tuple [ date , bool , int ] , Assiduite ] = { }
2023-02-09 21:04:53 +01:00
DUPLICATIONS_JUSTIFICATIFS : dict [ tuple [ date , bool , int ] , Justificatif ] = { }
2023-02-07 18:49:51 +01:00
DUPLICATED : list [ Justificatif ] = [ ]
2023-02-09 21:04:53 +01:00
PROBLEMS : dict [ int , list [ str ] ] = { }
CURRENT_ETU : list = [ ]
MODULES : list [ tuple [ int , int ] ] = [ ]
COMPTE : list [ int , int ] = [ ]
2023-02-10 14:08:31 +01:00
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 )
2023-02-08 19:48:34 +01:00
2023-02-07 18:49:51 +01:00
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
"""
2023-02-09 21:04:53 +01:00
Profiler . clear ( )
2023-02-10 14:08:31 +01:00
stats : _Statistics = _Statistics ( )
2023-02-09 21:04:53 +01:00
time_elapsed : Profiler = Profiler ( " migration " )
time_elapsed . start ( )
2023-02-08 19:48:34 +01:00
if morning is None :
2023-02-07 18:49:51 +01:00
pref_time_morning = time ( 8 , 0 )
else :
morning : list [ str ] = morning . split ( " h " )
pref_time_morning = time ( int ( morning [ 0 ] ) , int ( morning [ 1 ] ) )
2023-02-08 19:48:34 +01:00
if noon is None :
2023-02-07 18:49:51 +01:00
pref_time_noon = time ( 12 , 0 )
else :
noon : list [ str ] = noon . split ( " h " )
pref_time_noon = time ( int ( noon [ 0 ] ) , int ( noon [ 1 ] ) )
2023-02-08 19:48:34 +01:00
if evening is None :
2023-02-07 18:49:51 +01:00
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
2023-02-08 19:48:34 +01:00
if dept is not None :
2023-02-07 18:49:51 +01:00
2023-02-09 21:04:53 +01:00
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 )
2023-02-07 18:49:51 +01:00
2023-02-09 21:04:53 +01:00
_glob . DUPLICATED = [ ]
_glob . DUPLICATIONS_ASSIDUITES = { }
_glob . DUPLICATIONS_JUSTIFICATIFS = { }
_glob . CURRENT_ETU = [ ]
_glob . MODULES = [ ]
_glob . COMPTE = [ 0 , 0 ]
2023-02-10 14:08:31 +01:00
_glob . ERR_ETU = [ ]
2023-02-09 21:04:53 +01:00
absences_len : int = absences . count ( )
print (
f " { ProgressBarColors . BLUE } { absences_len } absences vont être migrées { ProgressBarColors . RESET } "
)
2023-02-08 19:48:34 +01:00
printProgressBar ( 0 , absences_len , " Progression " , " effectué " , autosize = True )
for i , abs in enumerate ( absences ) :
2023-02-09 21:04:53 +01:00
try :
if abs . estabs :
generated = _from_abs_to_assiduite (
abs , pref_time_morning , pref_time_noon , pref_time_evening
)
2023-02-07 18:49:51 +01:00
if not isinstance ( generated , str ) :
db . session . add ( generated )
2023-02-09 21:04:53 +01:00
_glob . COMPTE [ 0 ] + = 1
except Exception as e :
2023-02-10 14:08:31 +01:00
stats . add_problem ( abs , e . args [ 0 ] )
2023-02-09 21:04:53 +01:00
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 :
2023-02-10 14:08:31 +01:00
stats . add_problem ( abs , e . args [ 0 ] )
2023-02-09 21:04:53 +01:00
if i % 10 == 0 :
printProgressBar (
i ,
absences_len ,
" Progression " ,
" effectué " ,
autosize = True ,
2023-02-07 18:49:51 +01:00
)
2023-02-08 19:48:34 +01:00
2023-02-09 21:04:53 +01:00
if i % 1000 == 0 :
printProgressBar (
i ,
absences_len ,
" Progression " ,
" effectué " ,
autosize = True ,
)
db . session . commit ( )
2023-02-07 18:49:51 +01:00
2023-02-09 21:04:53 +01:00
dup_assi = _glob . DUPLICATED
2023-02-07 18:49:51 +01:00
assi : Assiduite
for assi in dup_assi :
assi . moduleimpl_id = None
db . session . add ( assi )
db . session . commit ( )
2023-02-08 19:48:34 +01:00
printProgressBar (
absences_len ,
absences_len ,
" Progression " ,
" effectué " ,
autosize = True ,
2023-02-09 21:04:53 +01:00
)
time_elapsed . stop ( )
2023-02-10 14:08:31 +01:00
statistiques : dict = stats . compute_stats ( )
2023-02-09 21:04:53 +01:00
print (
f " { ProgressBarColors . GREEN } La migration a pris { time_elapsed . elapsed ( ) : .2f } secondes { ProgressBarColors . RESET } "
)
print (
2023-02-10 14:08:31 +01:00
f " { ProgressBarColors . RED } { statistiques [ ' total ' ] } absences qui n ' ont pas pu être migrée. "
2023-02-08 19:48:34 +01:00
)
2023-02-09 21:04:53 +01:00
print (
2023-02-10 14:08:31 +01:00
f " Vous retrouverez un fichier json { ProgressBarColors . GREEN } /tmp/scodoc_migration_abs.json { ProgressBarColors . RED } contenant les problèmes de migrations "
2023-02-09 21:04:53 +01:00
)
with open ( " /tmp/scodoc_migration_abs.json " , " w " , encoding = " utf-8 " ) as file :
2023-02-10 14:08:31 +01:00
stats . export ( file )
2023-02-09 21:04:53 +01:00
print (
f " { ProgressBarColors . CYAN } { _glob . COMPTE [ 0 ] } assiduités et { _glob . COMPTE [ 1 ] } justificatifs ont été générés. { ProgressBarColors . RESET } "
)
2023-02-10 14:08:31 +01:00
print ( dumps ( statistiques , indent = 2 ) )
2023-02-08 19:48:34 +01:00
2023-02-07 18:49:51 +01:00
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
2023-02-09 21:04:53 +01:00
2023-02-07 18:49:51 +01:00
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 )
2023-02-09 21:04:53 +01:00
duplicata : Assiduite = _glob . DUPLICATIONS_ASSIDUITES . get (
2023-02-07 18:49:51 +01:00
( _abs . jour , _abs . matin , _abs . etudid )
)
if duplicata is not None :
2023-02-09 21:04:53 +01:00
_glob . DUPLICATED . append ( duplicata )
2023-02-10 14:08:31 +01:00
return " Duplicata "
2023-02-07 18:49:51 +01:00
desc : str = _abs . description
entry_date : datetime = _abs . entry_date
2023-02-09 21:04:53 +01:00
if _abs . etudid not in _glob . CURRENT_ETU :
etud : Identite = Identite . query . filter_by ( id = _abs . etudid ) . first ( )
if etud is None :
2023-02-10 14:08:31 +01:00
raise Exception ( " Etudiant inexistant " )
2023-02-09 21:04:53 +01:00
_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 " )
2023-02-07 18:49:51 +01:00
2023-02-09 21:04:53 +01:00
retour = Assiduite . fast_create_assiduite (
etudid = _abs . etudid ,
2023-02-07 18:49:51 +01:00
date_debut = date_deb ,
date_fin = date_fin ,
etat = etat ,
2023-02-09 21:04:53 +01:00
moduleimpl_id = moduleimpl_id ,
2023-02-07 18:49:51 +01:00
description = desc ,
entry_date = entry_date ,
)
2023-02-09 21:04:53 +01:00
_glob . DUPLICATIONS_ASSIDUITES [ ( _abs . jour , _abs . matin , _abs . etudid ) ] = retour
2023-02-07 18:49:51 +01:00
return retour
def _from_abs_to_justificatif (
_abs : Absence , morning : time , noon : time , evening : time
) - > Justificatif :
2023-02-09 21:04:53 +01:00
2023-02-07 18:49:51 +01:00
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 )
2023-02-09 21:04:53 +01:00
duplicata : Justificatif = _glob . DUPLICATIONS_JUSTIFICATIFS . get (
( _abs . jour , _abs . matin , _abs . etudid )
)
if duplicata is not None :
return " Duplicated "
2023-02-07 18:49:51 +01:00
desc : str = _abs . description
entry_date : datetime = _abs . entry_date
2023-02-09 21:04:53 +01:00
if _abs . etudid not in _glob . CURRENT_ETU :
etud : Identite = Identite . query . filter_by ( id = _abs . etudid ) . first ( )
if etud is None :
2023-02-10 14:08:31 +01:00
raise Exception ( " Etudiant inexistant " )
2023-02-09 21:04:53 +01:00
_glob . CURRENT_ETU . append ( _abs . etudid )
2023-02-07 18:49:51 +01:00
2023-02-09 21:04:53 +01:00
retour = Justificatif . fast_create_justificatif (
etudid = _abs . etudid ,
2023-02-07 18:49:51 +01:00
date_debut = date_deb ,
date_fin = date_fin ,
etat = etat ,
raison = desc ,
entry_date = entry_date ,
)
2023-02-09 21:04:53 +01:00
_glob . DUPLICATIONS_JUSTIFICATIFS [ ( _abs . jour , _abs . matin , _abs . etudid ) ] = retour
2023-02-07 18:49:51 +01:00
return retour