Files
test_OgureNG/backend-django/backend/utils/decisions.py
2022-11-08 21:19:51 +01:00

267 lines
9.8 KiB
Python

from enum import Enum
from typing import Dict, List, Optional, Tuple, Union
from ..models import Administre, CustomUser, Decision, DecisionChoices, Administres_Pams
from ..models import StatutPamChoices as StatutPam
from .logging import get_logger
from .permissions import KEY_WRITE, Profiles, get_profiles_by_adm
logger = get_logger(__name__)
# clé d'une valeur de l'arbre de décision : choix disponibles
KEY_CHOICES = 'choices'
# clé d'une valeur de l'arbre de décision : profils habilités à modifier le statut
KEY_PROFILES = 'editable_by'
# clé d'un élément de "get_available_decisions" : pour une création
KEY_CREATE = 'creation'
# clé d'un élément de "get_available_decisions" : pour une mise à jour
KEY_UPDATE = 'update'
class ExtraDecisions(str, Enum):
""" décisions en plus """
EMPTY = ''
def __repr__(self):
return self.__str__()
def __init_decisions():
D = DecisionChoices
EX = ExtraDecisions
P = Profiles
return {
# === commun ===
None: {
KEY_PROFILES: (P.FILIERE, P.BVT),
KEY_CHOICES: (D.PROPOSITION_FE, D.HME_PROPOSITION_VIVIER)
},
D.REMIS_A_DISPOSITION: {
KEY_PROFILES: (P.FILIERE,),
KEY_CHOICES: (EX.EMPTY,),
},
# === en métropole ===
D.PROPOSITION_FE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL),
KEY_CHOICES: (D.DIALOGUE_EN_COURS, D.FOREMP_EN_COURS)
},
D.DIALOGUE_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL,),
KEY_CHOICES: (D.DIALOGUE_TERMINE, D.DIALOGUE_INFRUCTUEUX)
},
D.DIALOGUE_TERMINE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL,),
KEY_CHOICES: (D.FOREMP_EN_COURS,)
},
D.DIALOGUE_INFRUCTUEUX: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL,),
KEY_CHOICES: (D.FOREMP_EN_COURS, D.REMIS_A_DISPOSITION)
},
D.FOREMP_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL,),
KEY_CHOICES: (D.FOREMP_TERMINE,)
},
D.FOREMP_TERMINE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL,),
KEY_CHOICES: (D.PREPOSITIONNE, D.REMIS_A_DISPOSITION)
},
D.PREPOSITIONNE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.POSITIONNE, D.REMIS_A_DISPOSITION)
},
D.POSITIONNE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.OMIP_EN_COURS, D.OMI_EN_COURS, D.REMIS_A_DISPOSITION)
},
D.OMIP_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.OMIP_TERMINE, D.REMIS_A_DISPOSITION),
},
D.OMIP_TERMINE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.ATTENTE_AVIONAGE, D.OMI_EN_COURS, D.REMIS_A_DISPOSITION),
},
D.ATTENTE_AVIONAGE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.OMI_EN_COURS,),
},
D.OMI_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.OMI_ACTIVE, D.REMIS_A_DISPOSITION),
},
D.OMI_ACTIVE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.OMI_ANNULE,),
},
D.OMI_ANNULE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.REMIS_A_DISPOSITION,),
},
# === hors métropole (HME) ===
D.HME_PROPOSITION_VIVIER: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_DIALOGUE_INITIE,)
},
D.HME_ETUDE_DESISTEMENT: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_DESISTEMENT,)
},
D.HME_DESISTEMENT: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.REMIS_A_DISPOSITION,)
},
D.HME_DIALOGUE_INITIE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_DIALOGUE_EN_COURS,)
},
D.HME_DIALOGUE_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_DIALOGUE_TERMINE, D.HME_DIALOGUE_INFRUCTUEUX)
},
D.HME_DIALOGUE_TERMINE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_PREPOSITIONNE,)
},
D.HME_DIALOGUE_INFRUCTUEUX: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_ETUDE_DESISTEMENT, D.HME_PREPOSITIONNE, D.REMIS_A_DISPOSITION)
},
D.HME_FOREMP_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_FOREMP_TERMINE, D.HME_OMI_EN_COURS)
},
D.HME_FOREMP_TERMINE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_OMIP_EN_COURS, D.HME_OMI_EN_COURS)
},
D.HME_PREPOSITIONNE: {
KEY_PROFILES: (P.HME,),
KEY_CHOICES: (D.HME_VALIDATION_EXPERT, D.HME_REFUS_EXPERT)
},
D.HME_VALIDATION_EXPERT: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_POSITIONNE,)
},
D.HME_POSITIONNE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_OMIP_EN_COURS, D.HME_OMI_EN_COURS, D.HME_FOREMP_EN_COURS)
},
D.HME_REFUS_EXPERT: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_PREPOSITIONNE, D.REMIS_A_DISPOSITION),
},
D.HME_OMIP_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_OMIP_TERMINE,),
},
D.HME_OMIP_TERMINE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_ATTENTE_AVIONAGE, D.HME_OMI_EN_COURS),
},
D.HME_ATTENTE_AVIONAGE: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_OMI_EN_COURS,),
},
D.HME_OMI_EN_COURS: {
KEY_PROFILES: (P.PCP, P.PCP_ACTUEL, P.PCP_FUTUR),
KEY_CHOICES: (D.HME_OMI_ACTIVE,),
},
D.HME_OMI_ACTIVE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.HME_OMI_ANNULE,),
},
D.HME_OMI_ANNULE: {
KEY_PROFILES: (P.PCP, P.PCP_FUTUR,),
KEY_CHOICES: (D.REMIS_A_DISPOSITION,),
},
}
# enchaînement des décisions
DECISIONS = __init_decisions()
def get_all_decisions() -> Tuple[DecisionChoices]:
"""
Renvoie tous les statuts de décisions possibles. Une décision vide correspondrait à une absence de décision et n'est donc pas présente.
:return: toutes les décisions
:rtype: Tuple[DecisionChoices]
"""
return tuple(DecisionChoices)
def get_available_decisions(
administres: Union[List[Administres_Pams], Tuple[Administres_Pams]],
user: CustomUser = None,
profiles_by_adm: Dict[int, Tuple[Profiles]] = None
) -> Dict[int, Dict[str, Tuple[Union[DecisionChoices, ExtraDecisions]]]]:
"""
Renvoie les décisions disponibles pour l'utilisateur s'il modifie le statut de décision des administrés donnés.
:param administres: Administres_Pams
:type administres: Union[List[Administres_Pams], Tuple[Administres_Pams]]
:param user: utilisateur
:type user: class:`CustomUser`, optional
:param profiles_by_adm: profils pour chaque administré (voir get_profiles_by_adm)
:type profiles_by_adm: Dict[int, Tuple[Profiles]], optional
:return: dictionnaire de dictionnaires {<ID SAP>: {<KEY_CREATE>: <décisions>, <KEY_UPDATE>: <décisions>} }
:rtype: Dict[int, Dict[str, Tuple[Union[DecisionChoices, ExtraDecisions]]]]
"""
pam_without_decision = tuple(x for x in StatutPam if not x.dec_enabled)
pam_with_decision = tuple(x for x in StatutPam if x.dec_enabled)
result = {}
# restrictions : dictionnaire de dictionnaires {<ID SAP>: <même forme qu'une valeur de DECISIONS>}
restrictions_create_by_adm = {}
restrictions_update_by_adm = {}
adm_to_process = []
for adm in administres:
adm_id = adm.pk
pam = adm.a_statut_pam_annee
if pam in pam_without_decision:
# le statut PAM ne permet aucun choix
result[adm_id] = {KEY_CREATE: (), KEY_UPDATE: ()}
elif pam in pam_with_decision:
# le statut PAM active l'arbre de décisions
adm_to_process.append(adm)
statut_decision = adm.decision.de_decision or None if hasattr(adm, Administres_Pams.Cols.REL_DECISION) else None
restrictions_update_by_adm[adm_id] = DECISIONS.get(statut_decision) or {}
else:
logger.info('statut PAM non géré pour les décisions possibles : %s', pam)
result[adm_id] = {KEY_CREATE: (), KEY_UPDATE: ()}
if adm_to_process:
default_restrictions_create = DECISIONS.get(None) or {}
def get_decisions(profiles, restrictions) -> Tuple[Union[DecisionChoices, ExtraDecisions]]:
allowed_profiles = restrictions.get(KEY_PROFILES) or ()
choices = restrictions.get(KEY_CHOICES)
decisions = ()
if choices and any(p in allowed_profiles for p in profiles):
decisions = tuple(choices)
return decisions
_profiles_by_adm = profiles_by_adm or get_profiles_by_adm(user, *adm_to_process)
for adm in adm_to_process:
adm_id = adm.pk
profiles = (_profiles_by_adm.get(adm_id) or {}).get(KEY_WRITE) or ()
result.setdefault(adm_id, {
KEY_CREATE: get_decisions(profiles, restrictions_create_by_adm.get(adm_id) or default_restrictions_create),
KEY_UPDATE: get_decisions(profiles, restrictions_update_by_adm.get(adm_id) or {})
})
return result