init
This commit is contained in:
16
backend-django/backend/views/__init__.py
Normal file
16
backend-django/backend/views/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from .alimentation import *
|
||||
from .alimentation_commentaires import *
|
||||
from .alimentation_ref_droits import *
|
||||
from .alimentation_zones_geo import *
|
||||
from .chargement_competences import *
|
||||
from .current_user import *
|
||||
from .decision import *
|
||||
from .exportation_fichiers import *
|
||||
from .fiche_detaillee import *
|
||||
from .formation_emploi import *
|
||||
from .initial import *
|
||||
from .notation import *
|
||||
from .references import *
|
||||
from .scoring import *
|
||||
from .suppression_administres import *
|
||||
from .chargement_pam import *
|
||||
405
backend-django/backend/views/alimentation.py
Normal file
405
backend-django/backend/views/alimentation.py
Normal file
@@ -0,0 +1,405 @@
|
||||
from datetime import datetime
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
import datetime
|
||||
|
||||
from ..models import Administre, PAM
|
||||
from ..models import StatutPamChoices as StatutPam
|
||||
from ..serializers import AlimentationSerializer
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils.extraction.administre import (to_table_administres_bo)
|
||||
from ..utils.insertion.administre import (insert_administre_bo,
|
||||
update_administre_fmob)
|
||||
from ..utils_extraction import (DataFrameTypes, FileTypes, read_files_by_type,
|
||||
to_table_administre_notation,
|
||||
to_table_affectation, to_table_diplome,
|
||||
to_table_domaines, to_table_fe,
|
||||
to_table_filieres, to_table_fmob_femp,
|
||||
to_table_fmob_fmob,
|
||||
to_table_fonctions, to_table_fud,
|
||||
to_table_garnisons, to_table_groupesMarques,
|
||||
to_table_marques, to_table_pam, to_table_postes,
|
||||
to_table_ref_gest, to_table_ref_org,
|
||||
to_table_ref_sv_fil, to_table_reo_ocv,
|
||||
to_table_sous_vivier,
|
||||
to_table_zone_geographique)
|
||||
from ..utils_insertion import (insert_PAM, insert_administre_notation, insert_Affectation,
|
||||
insert_Diplome, insert_Filiere,
|
||||
insert_FMOB_femp, insert_FMOB_fmob,
|
||||
insert_Fonction,
|
||||
insert_FormationEmploi, insert_Fud,
|
||||
insert_Garnison, insert_Grade, insert_Marque,
|
||||
insert_MarquesGroupe, insert_Poste,
|
||||
insert_RefFeMere, insert_RefGest, insert_RefOrg,
|
||||
insert_RefSvFil, insert_SousVivier,
|
||||
insert_SousVivier_instances,
|
||||
insert_ZoneGeographique, update_domaine,
|
||||
update_m2m_links_gestionnaire, update_poste_ocv, insert_delta)
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
class AlimentationView(APIView):
|
||||
"""
|
||||
Cette page est l'alimentation principale d'Ogure.
|
||||
Elle permet à l'administrateur de charger un ensemble de fichiers pour alimenter ou mettre à jour la base de données.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
serializer_class = AlimentationSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
def get(self, request):
|
||||
"""La fonction get renvoie une reponse contenant Formulaire d'alimentation d'OGURE NG
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request
|
||||
|
||||
|
||||
:return: - **return** (*json*): json contenant "Formulaire d'alimentation d'OGURE NG".
|
||||
"""
|
||||
return Response("Formulaire d'alimentation d'OGURE NG")
|
||||
|
||||
|
||||
@execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def post(self, request):
|
||||
"""La fonction post charge les fichiers.
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant les fichiers
|
||||
|
||||
:return: - **Response** (*Response*): Reponse contient les erreurs de chargement des données .
|
||||
"""
|
||||
try:
|
||||
# TODO: préparer une variable qui renvoie les erreurs sous 3 axes : données référentielles, administrés, postes
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
self.logger.info('---------------------Upload begining---------------------------')
|
||||
start_time = time.time()
|
||||
|
||||
file_donnees_bo = serializer.validated_data.get('Donnees_BO_ADT')
|
||||
file_reo = serializer.validated_data.get('REO')
|
||||
file_reo_suivant = serializer.validated_data.get('REO_PAM_SUIVANT')
|
||||
file_reo_ocv = serializer.validated_data.get('REO_OCV')
|
||||
file_ref_gest = serializer.validated_data.get('referentiel_gestionnaire')
|
||||
file_ref_org = serializer.validated_data.get('referentiel_organique')
|
||||
file_ref_sv_fil = serializer.validated_data.get('refeferentiel_sous_vivier_filiere')
|
||||
file_ref_fe = serializer.validated_data.get('referentiel_fe')
|
||||
file_fmob = serializer.validated_data.get('FMOB')
|
||||
file_fmob_suivant = serializer.validated_data.get('FMOB_PAM_SUIVANT')
|
||||
file_filiere_domaine = serializer.validated_data.get('domaine_filiere')
|
||||
file_insee = serializer.validated_data.get('insee_maping')
|
||||
file_diplomes = serializer.validated_data.get('diplomes')
|
||||
file_fud = serializer.validated_data.get('FUD')
|
||||
file_ref_zones_geo = serializer.validated_data.get('ref_zones_geo')
|
||||
|
||||
self.logger.info('---------------------Upload ending-----------------------------')
|
||||
self.logger.debug("--------------upload time -- %d seconds ------------------------", time.time() - start_time)
|
||||
|
||||
df_by_type = read_files_by_type({
|
||||
FileTypes.BO: file_donnees_bo,
|
||||
FileTypes.DIPLOME: file_diplomes,
|
||||
FileTypes.DOM_FIL: file_filiere_domaine,
|
||||
FileTypes.FMOB_FEMP: file_fmob,
|
||||
FileTypes.FMOB_FEMP_PAM_SUIVANT: file_fmob_suivant,
|
||||
FileTypes.FUD: file_fud,
|
||||
FileTypes.INSEE: file_insee,
|
||||
FileTypes.REF_FE: file_ref_fe,
|
||||
FileTypes.REF_GEO: file_ref_zones_geo,
|
||||
FileTypes.REF_GEST: file_ref_gest,
|
||||
FileTypes.REF_ORG: file_ref_org,
|
||||
FileTypes.REF_SV_FIL: file_ref_sv_fil,
|
||||
FileTypes.REO: file_reo,
|
||||
FileTypes.REO_PAM_SUIVANT: file_reo_suivant,
|
||||
FileTypes.REO_OCV: file_reo_ocv,
|
||||
})
|
||||
|
||||
DF = DataFrameTypes
|
||||
diplomes_df = df_by_type.get(DF.DIPLOME)
|
||||
donnees_bo_df = df_by_type.get(DF.BO)
|
||||
femp_df = df_by_type.get(DF.FEMP)
|
||||
filiere_domaine_df = df_by_type.get(DF.DOM_FIL)
|
||||
fmob_df = df_by_type.get(DF.FMOB)
|
||||
fud_df = df_by_type.get(DF.FUD)
|
||||
insee_df = df_by_type.get(DF.INSEE)
|
||||
ref_fe_df = df_by_type.get(DF.REF_FE)
|
||||
ref_gest_df = df_by_type.get(DF.REF_GEST)
|
||||
ref_org_df = df_by_type.get(DF.REF_ORG)
|
||||
ref_sv_fil_df = df_by_type.get(DF.REF_SV_FIL)
|
||||
ref_zones_geo_df = df_by_type.get(DF.REF_GEO)
|
||||
reo_df = df_by_type.get(DF.REO)
|
||||
reo_ocv_df = df_by_type.get(DF.REO_OCV)
|
||||
|
||||
#dataframe pam + 1
|
||||
femp_suivant_df = df_by_type.get(DF.FEMP_PAM_SUIVANT)
|
||||
fmob_suivant_df = df_by_type.get(DF.FMOB_PAM_SUIVANT)
|
||||
reo_suivant_df = df_by_type.get(DF.REO_PAM_SUIVANT)
|
||||
|
||||
|
||||
self.logger.info('-------------------- Insert beginning ---------------------')
|
||||
start_time_insert = time.time()
|
||||
text_response = []
|
||||
|
||||
|
||||
|
||||
if ref_sv_fil_df is not None:
|
||||
sv_cree, sv_modifie, sv_erreur, sv_supprime = insert_SousVivier(to_table_sous_vivier(ref_sv_fil_df))
|
||||
text_response.append([f"Référentiel de sous-viviers/filières : {sv_cree} SousVivier créés, {sv_modifie} SousVivier mis à jour, {sv_erreur} SousVivier en erreur et {sv_supprime} SousVivier supprimés."])
|
||||
self.logger.info('Sous-viviers ---------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : sous-viviers (nécessite %s)', DF.REF_SV_FIL.value[1])
|
||||
|
||||
if ref_org_df is not None:
|
||||
ref_org_cree, ref_org_modifie, ref_org_erreur, ref_org_supprime = insert_RefOrg(to_table_ref_org(ref_org_df))
|
||||
text_response.append([f"Référentiel organique : {ref_org_cree} RefOrg créés, {ref_org_modifie} RefOrg mis à jour, {ref_org_erreur} RefOrg en erreur et {ref_org_supprime} RefOrg supprimés."])
|
||||
self.logger.info('Référentiel organique ------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : référentiel organique')
|
||||
|
||||
if ref_gest_df is not None:
|
||||
ref_gest_cree, ref_gest_modifie, ref_gest_erreur, ref_gest_supprime, user_cree, user_modifie, user_supprime, user_ignore = insert_RefGest(to_table_ref_gest(ref_gest_df))
|
||||
text_response.append([f"Référentiel de gestionnaires : {ref_gest_cree} RefGest créés, {ref_gest_modifie} RefGest mis à jour, {ref_gest_erreur} RefGest en erreur et {ref_gest_supprime} RefGest supprimés."])
|
||||
text_response.append([f"Référentiel de gestionnaires : {user_cree} User créés, {user_modifie} User mis à jour, {user_supprime} User supprimés et {user_ignore} User ignorés."])
|
||||
self.logger.info('Référentiel gestionnaire ---------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : référentiel de gestionnaires')
|
||||
|
||||
if ref_sv_fil_df is not None:
|
||||
ref_sv_cree, ref_sv_delete, ref_sv_erreur, ref_sv_ignore = insert_RefSvFil(to_table_ref_sv_fil(ref_sv_fil_df))
|
||||
text_response.append([f"Référentiel de sous-viviers/filières : {ref_sv_cree} RefSvFil créés, {ref_sv_delete} RefSvFil supprimés, {ref_sv_erreur} RefSvFil en erreur et {ref_sv_ignore} RefSvFil ignorés."])
|
||||
self.logger.info('Référentiel sous-vivier filière --------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : référentiel de sous-viviers/filières')
|
||||
|
||||
if ref_gest_df is not None or ref_org_df is not None or ref_sv_fil_df is not None:
|
||||
update_m2m_links_gestionnaire('SV')
|
||||
self.logger.info('Liens gestionnaires/sous-viviers -------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : liens gestionnaires/sous-viviers (nécessite %s ou %s ou %s)',
|
||||
DF.REF_GEST.value[1], DF.REF_ORG.value[1], DF.REF_SV_FIL.value[1])
|
||||
|
||||
if filiere_domaine_df is not None:
|
||||
update_domaine(to_table_domaines(filiere_domaine_df))
|
||||
self.logger.info('Domaine --------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : domaines (nécessite %s)', DF.DOM_FIL.value[1])
|
||||
|
||||
if insee_df is not None and donnees_bo_df is not None:
|
||||
gar_cree, gar_maj , error = insert_Garnison(to_table_garnisons(insee_df, donnees_bo_df))
|
||||
text_response.append([f"INSEE et Données BO : {gar_cree} garnisons crées, {gar_maj} garnisons mises à jour, {error} garnisons en erreur."])
|
||||
self.logger.info('Garnison -------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : garnisons (nécessite %s + %s)', DF.INSEE.value[1], DF.BO.value[1])
|
||||
|
||||
if reo_df is not None:
|
||||
reo_cree = insert_Fonction(to_table_fonctions(reo_df))
|
||||
text_response.append([f"REO : {reo_cree} fonctions de postes créés."])
|
||||
self.logger.info('Fonction -------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : fonctions (nécessite %s)', DF.REO.value[1])
|
||||
|
||||
if reo_suivant_df is not None:
|
||||
reo_suivant_cree = insert_Fonction(to_table_fonctions(reo_suivant_df))
|
||||
text_response.append([f"REO : {reo_suivant_cree} fonctions de postes A+1 créés."])
|
||||
self.logger.info('Fonction A+1 -------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : fonctions A + 1 (nécessite %s)', DF.REO_PAM_SUIVANT.value[1])
|
||||
|
||||
|
||||
# TODO if ....
|
||||
insert_Grade()
|
||||
self.logger.info('Grade ----------------------------------------------> Succès')
|
||||
|
||||
if filiere_domaine_df is not None:
|
||||
dom_fil_cree, dom_fil_modifie, dom_fil_erreur, dom_fil_supprime = insert_Filiere(to_table_filieres(filiere_domaine_df))
|
||||
text_response.append([f"Domaines - filières : {dom_fil_cree} couples domaine/filière créés, {dom_fil_modifie} couples domaine/filière mis à jour, {dom_fil_erreur} couples domaine/filière en erreur et {dom_fil_supprime} couples domaine/filière supprimés."])
|
||||
self.logger.info('Filières ------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : filières (nécessite %s)', DF.DOM_FIL.value[1])
|
||||
|
||||
# TODO if ....
|
||||
insert_MarquesGroupe(to_table_groupesMarques())
|
||||
self.logger.info('MarquesGroupe -------------------------------------> Succès')
|
||||
|
||||
# TODO if ....
|
||||
insert_Marque(to_table_marques())
|
||||
self.logger.info('Marque --------------------------------------------> Succès')
|
||||
|
||||
if ref_fe_df is not None:
|
||||
df = to_table_fe(ref_fe_df)
|
||||
self.logger.debug('Extraction des données du référentiel FE ----------> Succès')
|
||||
fe_cree, fe_maj, error_count = insert_FormationEmploi(df)
|
||||
text_response.append([f"Référentiel FE : {fe_cree} formation d'emplois créées, {fe_maj} formation d'emplois mises à jour, {error_count} formation d'emplois en erreur."])
|
||||
self.logger.info('FormationEmplois ----------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : formations-emplois (nécessite %s)', DF.REF_FE.value[1])
|
||||
|
||||
if ref_fe_df is not None:
|
||||
insert_RefFeMere(ref_fe_df)
|
||||
self.logger.info('Référentiel FE mère -------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : formations-emplois mères (nécessite %s)', DF.REF_FE.value[1])
|
||||
|
||||
if ref_gest_df is not None or ref_org_df is not None or ref_fe_df is not None:
|
||||
update_m2m_links_gestionnaire('FE')
|
||||
self.logger.info('Liens gestionnaires/formations-emplois ------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : liens gestionnaires/formations-emplois (nécessite %s ou %s ou %s)',
|
||||
DF.REF_GEST.value[1], DF.REF_ORG.value[1], DF.REF_FE.value[1])
|
||||
|
||||
if donnees_bo_df is not None:
|
||||
bo_adm_cree, bo_adm_modifie, bo_adm_deja_modifie, bo_adm_erreur, bo_adm_ignore, bo_dom_ignore, bo_fil_ignore = insert_administre_bo(to_table_administres_bo(donnees_bo_df))
|
||||
text_response.append([f"Données BO : {bo_adm_cree} administrés créés, {bo_adm_modifie} administrés mis à jour, {bo_adm_deja_modifie} adminsitrés déjà à jour, {bo_adm_erreur} administrés en erreur, {bo_adm_ignore} administrés ignorés, {bo_dom_ignore} domaines ignorés et {bo_fil_ignore} filières ignorées."])
|
||||
self.logger.info('Administre ----------------------------------------> Succès')
|
||||
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : administrés (nécessite %s)', DF.BO.value[1])
|
||||
|
||||
if diplomes_df is not None:
|
||||
diplome_cree, diplome_maj, diplome_sup = insert_Diplome(to_table_diplome(diplomes_df))
|
||||
text_response.append([f"Diplômes : {diplome_cree} diplômes créés, {diplome_maj} diplômes mis à jour, {diplome_sup} supprimés."])
|
||||
self.logger.info('Diplome -------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : diplômes (nécessite %s)', DF.DIPLOME.value[1])
|
||||
|
||||
if donnees_bo_df is not None:
|
||||
donnee_bo_cree, donnee_bo_sup = insert_Affectation(to_table_affectation(donnees_bo_df))
|
||||
text_response.append([f"Données BO : {donnee_bo_cree} affectation créés, {donnee_bo_sup} affectation mis à jour."])
|
||||
self.logger.info('Affectations --------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : affectations (nécessite %s)', DF.BO.value[1])
|
||||
|
||||
if fud_df is not None:
|
||||
fud_cree, fud_sup = insert_Fud(to_table_fud(fud_df))
|
||||
text_response.append([f"FUD : {fud_cree} FUD créés, {fud_sup} FUD supprimées."])
|
||||
self.logger.info('FUD -----------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : FUD (nécessite %s)', DF.FUD.value[1])
|
||||
|
||||
if donnees_bo_df is not None:
|
||||
bo_cree, bo_maj, error_count = insert_administre_notation(to_table_administre_notation(donnees_bo_df))
|
||||
text_response.append([f"Données BO : {bo_cree} notations créées, {bo_maj} notations mises à jour, {error_count} notations en erreurs."])
|
||||
self.logger.info('Administre Notation -------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : administrés/notations (nécessite %s)', DF.BO.value[1])
|
||||
|
||||
if reo_df is not None:
|
||||
annee_pam = PAM.objects.get(pam_statut='PAM en cours').pam_id
|
||||
reo_cree, reo_suivant_cree, reo_modifie, reo_erreur, reo_ignore = insert_Poste(to_table_postes(reo_df),annee_pam)
|
||||
text_response.append([f"REO : {reo_cree} postes créés, {reo_modifie} postes mis à jour, {reo_erreur} postes en erreur et {reo_ignore} postes ignorés."])
|
||||
self.logger.info('Poste ---------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : postes (nécessite %s)', DF.REO.value[1])
|
||||
|
||||
if reo_suivant_df is not None:
|
||||
annee_pam = PAM.objects.get(pam_statut='PAM A+1').pam_id
|
||||
reo_cree, reo_suivant_cree, reo_modifie, reo_erreur, reo_ignore = insert_Poste(to_table_postes(reo_suivant_df),annee_pam)
|
||||
#info reo
|
||||
text_response.append([f"REO A + 1 : {reo_suivant_cree} postes A+1 créés, {reo_erreur} postes A+1 en erreur et {reo_ignore} postes A+1 ignorés."])
|
||||
self.logger.info('Poste A+1 ---------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : postes A + 1 (nécessite %s)', DF.REO_PAM_SUIVANT.value[1])
|
||||
|
||||
if reo_suivant_df is not None:
|
||||
self.logger.info('Calcul du delta de la colonne Info REO, veuillez patienter ...')
|
||||
insert_delta(to_table_postes(reo_suivant_df))
|
||||
text_response.append([f"Mise à jour de la colonne info REO"])
|
||||
self.logger.info('Calcul du delta Info Reo ---------------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour de la colonne "info reo" ignorée')
|
||||
|
||||
# TODO if ....
|
||||
insert_SousVivier_instances()
|
||||
self.logger.info('Sous-viviers instances ---------------------------> Succès')
|
||||
|
||||
if reo_ocv_df is not None:
|
||||
reo_ocv_modifie, reo_ocv_erreur, reo_ocv_ignore = update_poste_ocv(to_table_reo_ocv(reo_ocv_df))
|
||||
text_response.append([f"Requêtes OCV : {reo_ocv_modifie} postes-ocv mis à jour, {reo_ocv_erreur} postes-ocv en erreur et {reo_ocv_ignore} postes-ocv ignorés."])
|
||||
self.logger.info('Poste-ocv -----------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : postes/administrés (nécessite %s)', DF.REO_OCV.value[1])
|
||||
|
||||
if ref_zones_geo_df is not None:
|
||||
ref_zones_geo_df_cree, ref_zones_geo_df_sup, error_count = insert_ZoneGeographique(to_table_zone_geographique(ref_zones_geo_df))
|
||||
text_response.append([f"Référentiels Zones Géographiques : {ref_zones_geo_df_cree} zones-geo créées, {ref_zones_geo_df_sup} zones-geo supprimés et {error_count} zones-geo en erreur."])
|
||||
self.logger.info('Référentiel zones géographiques -------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : zones géographiques (nécessite %s)', DF.REF_GEO.value[1])
|
||||
|
||||
if fmob_df is not None:
|
||||
fmob_cree, fmob_maj, ignore_count = insert_FMOB_fmob(to_table_fmob_fmob(fmob_df))
|
||||
text_response.append([f"Formulaire de mobilité : {fmob_cree} fmob créés, {fmob_maj} fmob mis à jour et {ignore_count} fmob ignorés."])
|
||||
self.logger.debug('Fichier FMOB --------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : FMOB (nécessite %s)', DF.FMOB.value[1])
|
||||
|
||||
if femp_df is not None:
|
||||
femp_cree, femp_maj, ignore_count = insert_FMOB_femp(to_table_fmob_femp(femp_df))
|
||||
text_response.append([f"Formulaire de mobilité : {femp_cree} femp créés, {femp_maj} femp mis à jour et {ignore_count} femp ignorés."])
|
||||
self.logger.debug('Fichier FEMP --------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : FEMP (nécessite %s)', DF.FEMP.value[1])
|
||||
|
||||
if fmob_suivant_df is not None:
|
||||
fmob_cree, fmob_maj, ignore_count = insert_FMOB_fmob(to_table_fmob_fmob(fmob_suivant_df))
|
||||
text_response.append([f"Formulaire de mobilité A+1 : {fmob_cree} fmob A+1 créés, {fmob_maj} fmob A+1 mis à jour et {ignore_count} fmob A+1 ignorés."])
|
||||
self.logger.debug('Fichier FMOB A+1--------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : FMOB A + 1(nécessite %s)', DF.FMOB_PAM_SUIVANT.value[1])
|
||||
|
||||
if femp_suivant_df is not None:
|
||||
femp_cree, femp_maj, ignore_count = insert_FMOB_femp(to_table_fmob_femp(femp_suivant_df))
|
||||
text_response.append([f"Formulaire de mobilité A+1 : {femp_cree} femp A+1 créés, {femp_maj} femp A+1 mis à jour et {ignore_count} femp A+1 ignorés."])
|
||||
self.logger.debug('Fichier FEMP A+1 --------------------------------------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : FEMP A + 1(nécessite %s)', DF.FEMP_PAM_SUIVANT.value[1])
|
||||
|
||||
|
||||
if fmob_df is not None:
|
||||
a_maintenir, a_traiter = update_administre_fmob(to_table_fmob_fmob(fmob_df))
|
||||
text_response.append([f"Formulaire de mobilité : {a_maintenir} administrés mis à jour en 'A maintenir' car FMOB annulé, {a_traiter} administrés mis à jour en 'A muter' car ils ont un FMOB."])
|
||||
self.logger.debug('Mise à jour du statut PAM si annulation FMOB et si FMOB existe------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : statut PAM si annulation FMOB (nécessite %s ou %s)', DF.BO.value[1], DF.FMOB.value[1])
|
||||
|
||||
if fmob_suivant_df is not None:
|
||||
a_maintenir, a_traiter = update_administre_fmob(to_table_fmob_fmob(fmob_suivant_df))
|
||||
text_response.append([f"Formulaire de mobilité A+1 : {a_maintenir} administrés A+1 mis à jour en 'A maintenir' car FMOB annulé, {a_traiter} administrés A+1 mis à jour en 'A muter' car ils ont un FMOB A+1."])
|
||||
self.logger.debug('Mise à jour du statut PAM A+1 si annulation FMOB et si FMOB existe ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : statut PAM A+1 si annulation FMOB (nécessite %s ou %s)', DF.BO.value[1], DF.FMOB_PAM_SUIVANT.value[1])
|
||||
|
||||
|
||||
|
||||
self.logger.debug('Administres Pams -------------------------> Success')
|
||||
|
||||
self.logger.debug('--------------- Insert time : %d seconds -----------------', time.time() - start_time_insert)
|
||||
self.logger.info('---------------------- Insert ending ----------------------')
|
||||
|
||||
text_response_df = pd.DataFrame(text_response)
|
||||
if text_response_df.empty:
|
||||
return Response(text_response_df)
|
||||
else:
|
||||
return Response(text_response_df[0])
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Impossible d'alimenter le(s) référentiel(s)"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
|
||||
|
||||
|
||||
70
backend-django/backend/views/alimentation_commentaires.py
Normal file
70
backend-django/backend/views/alimentation_commentaires.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import pandas as pd
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..serializers import AlimentationCommentairesSerializer
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils_extraction import (DataFrameTypes, FileTypes, read_files_by_type,
|
||||
to_table_commentaires)
|
||||
from ..utils_insertion import insert_Commentaires
|
||||
|
||||
|
||||
class AlimentationCommentairesView(APIView):
|
||||
""" Vue pour alimenter la base avec les commentaires """
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
serializer_class = AlimentationCommentairesSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
def get(self, request):
|
||||
return Response("Formulaire d'alimentation d'OGURE NG pour les commentaires")
|
||||
|
||||
|
||||
@atomic
|
||||
@execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def post(self, request):
|
||||
"""
|
||||
Charge le(s) fichier(s) et met à jour la base.
|
||||
|
||||
:param request: requête, contient les fichiers
|
||||
:type request: class:`rest_framework.request.Request`
|
||||
|
||||
:raises: class:`rest_framework.exceptions.APIException`
|
||||
|
||||
:return: réponse
|
||||
:rtype: class:`rest_framework.response.Response`
|
||||
"""
|
||||
try:
|
||||
validator = self.serializer_class(data=request.data)
|
||||
validator.is_valid(raise_exception=True)
|
||||
|
||||
df_comments = read_files_by_type({
|
||||
FileTypes.COMMENTS: validator.validated_data.get('commentaires')
|
||||
}).get(DataFrameTypes.COMMENTS)
|
||||
|
||||
if df_comments is not None:
|
||||
df = to_table_commentaires(df_comments)
|
||||
self.logger.info('Extraction des commentaires ------> Succès')
|
||||
|
||||
insert_Commentaires(df)
|
||||
self.logger.info('Insertion des commentaires ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : commentaires')
|
||||
return Response({'Insertion réussie'})
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Impossible d'alimenter le(s) référentiel(s)"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
153
backend-django/backend/views/alimentation_ref_droits.py
Normal file
153
backend-django/backend/views/alimentation_ref_droits.py
Normal file
@@ -0,0 +1,153 @@
|
||||
import time
|
||||
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..serializers import AlimentationRefsDroitSerializer
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils_extraction import (DataFrameTypes, FileTypes, read_files_by_type,
|
||||
to_table_fe, to_table_ref_gest,
|
||||
to_table_ref_org, to_table_ref_sv_fil,
|
||||
to_table_sous_vivier)
|
||||
from ..utils_insertion import (insert_FormationEmploi, insert_RefFeMere,
|
||||
insert_RefGest, insert_RefOrg, insert_RefSvFil,
|
||||
insert_SousVivier, insert_SousVivier_instances,
|
||||
update_m2m_links_gestionnaire)
|
||||
|
||||
|
||||
class AlimentationReferentielsDroitView(APIView):
|
||||
"""
|
||||
Page d'alimentation des référentiels destinés à la gestion des droits.
|
||||
- Chargement des sous-viviers via le référentiel sous-viviers/flières,
|
||||
- Chargement des référentiels gestionnaires, organique, sous-viviers/filières et FE,
|
||||
- Chargement des liens entre les formation-emplois et les gestionnaires suivant la gestion de droits mise en place,
|
||||
- Chargement des liens entre les sous-viviers et les gestionnaires suivant la gestion de droits mise en place,
|
||||
- Attribution des sous-viviers aux postes et aux administrés.
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
serializer_class = AlimentationRefsDroitSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
def get(self, request):
|
||||
return Response("Formulaire d'alimentation d'OGURE NG pour les référentiels destinés à la gestion des droits")
|
||||
|
||||
|
||||
@atomic
|
||||
@execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def post(self, request):
|
||||
"""
|
||||
Charge le(s) fichier(s) et met à jour la base.
|
||||
|
||||
:param request: requête, contient les fichiers
|
||||
:type request: class:`rest_framework.request.Request`
|
||||
|
||||
:raises: class:`rest_framework.exceptions.APIException`
|
||||
|
||||
:return: réponse
|
||||
:rtype: class:`rest_framework.response.Response`
|
||||
"""
|
||||
try:
|
||||
validator = self.serializer_class(data=request.data)
|
||||
validator.is_valid(raise_exception=True)
|
||||
|
||||
df_by_type = read_files_by_type({
|
||||
FileTypes.REF_FE: validator.validated_data.get('referentiel_fe'),
|
||||
FileTypes.REF_GEST: validator.validated_data.get('referentiel_gestionnaire'),
|
||||
FileTypes.REF_ORG: validator.validated_data.get('referentiel_organique'),
|
||||
FileTypes.REF_SV_FIL: validator.validated_data.get('refeferentiel_sous_vivier_filiere'),
|
||||
})
|
||||
DF = DataFrameTypes
|
||||
ref_fe_df = df_by_type.get(DF.REF_FE)
|
||||
ref_gest_df = df_by_type.get(DF.REF_GEST)
|
||||
ref_org_df = df_by_type.get(DF.REF_ORG)
|
||||
ref_sv_fil_df = df_by_type.get(DF.REF_SV_FIL)
|
||||
|
||||
self.logger.info('-------------------- Insert beginning ---------------------')
|
||||
start_time_insert = time.time()
|
||||
|
||||
if ref_sv_fil_df is not None:
|
||||
df = to_table_sous_vivier(ref_sv_fil_df)
|
||||
self.logger.info('Extraction des données de sous-viviers ------> Succès')
|
||||
insert_SousVivier(df)
|
||||
self.logger.info('Insertion des sous-viviers ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : sous-viviers (nécessite %s)', DF.REF_SV_FIL.value[1])
|
||||
|
||||
if ref_org_df is not None:
|
||||
df = to_table_ref_org(ref_org_df)
|
||||
self.logger.info('Extraction des données du référentiel organique DRHAT ------> Succès')
|
||||
insert_RefOrg(df)
|
||||
self.logger.info('Insertion du référentiel organique ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : référentiel organique')
|
||||
|
||||
if ref_gest_df is not None:
|
||||
df = to_table_ref_gest(ref_gest_df)
|
||||
self.logger.info('Extraction des données de gestionnaires DRHAT ------> Succès')
|
||||
insert_RefGest(df)
|
||||
self.logger.info('Insertion du référentiel gestionnaire / utilisateurs ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : référentiel de gestionnaires')
|
||||
|
||||
if ref_sv_fil_df is not None:
|
||||
df = to_table_ref_sv_fil(ref_sv_fil_df)
|
||||
self.logger.info('Extraction des données du référentiel de sous-viviers/filières ------> Succès')
|
||||
insert_RefSvFil(df)
|
||||
self.logger.info('Insertion du référentiel de sous-viviers/filières ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : référentiel de sous-viviers/filières')
|
||||
|
||||
if ref_gest_df is not None or ref_org_df is not None or ref_sv_fil_df is not None:
|
||||
update_m2m_links_gestionnaire('SV')
|
||||
self.logger.info('Insertion des liens M2M entre sous-viviers et gestionnaires ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : liens gestionnaires/sous-viviers (nécessite %s ou %s ou %s)',
|
||||
DF.REF_GEST.value[1], DF.REF_ORG.value[1], DF.REF_SV_FIL.value[1])
|
||||
|
||||
if ref_fe_df is not None:
|
||||
df = to_table_fe(ref_fe_df)
|
||||
self.logger.info('Extraction des données du référentiel FE ------> Succès')
|
||||
insert_FormationEmploi(df)
|
||||
self.logger.info('Insertion du référentiel FE ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : formations-emplois (nécessite %s)', DF.REF_FE.value[1])
|
||||
|
||||
if ref_fe_df is not None:
|
||||
insert_RefFeMere(ref_fe_df)
|
||||
self.logger.info('Référentiel FE mère ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : formations-emplois mères (nécessite %s)', DF.REF_FE.value[1])
|
||||
|
||||
if ref_gest_df is not None or ref_org_df is not None or ref_fe_df is not None:
|
||||
update_m2m_links_gestionnaire('FE')
|
||||
self.logger.info('Insertion des liens M2M entre formations-emplois et gestionnaires ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : liens gestionnaires/formations-emplois (nécessite %s ou %s ou %s)',
|
||||
DF.REF_GEST.value[1], DF.REF_ORG.value[1], DF.REF_FE.value[1])
|
||||
|
||||
# TODO if ....
|
||||
insert_SousVivier_instances()
|
||||
self.logger.info('Insertion des liens entre sous-vivivers et administrés/postes ------> Succès')
|
||||
|
||||
self.logger.debug('--------------- Insert time : %d seconds -----------------', time.time() - start_time_insert)
|
||||
self.logger.info('---------------------- Insert ending ----------------------')
|
||||
|
||||
return Response({'Insertion réussie'})
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Impossible d'alimenter les référentiels"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
70
backend-django/backend/views/alimentation_zones_geo.py
Normal file
70
backend-django/backend/views/alimentation_zones_geo.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import pandas as pd
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..serializers.alimentation import AlimentationZoneGeographiqueSerializer
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils_extraction import (DataFrameTypes, FileTypes, read_files_by_type,
|
||||
to_table_zone_geographique)
|
||||
from ..utils_insertion import insert_ZoneGeographique
|
||||
|
||||
|
||||
class AlimentationZoneGeographiqueView(APIView):
|
||||
""" Vue pour alimenter la base à partir de référentiels """
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
serializer_class = AlimentationZoneGeographiqueSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
def get(self, request):
|
||||
return Response("Formulaire d'alimentation d'OGURE NG pour les zones geographiques")
|
||||
|
||||
|
||||
@atomic
|
||||
@execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def post(self, request):
|
||||
"""
|
||||
Charge le(s) fichier(s) et met à jour la base.
|
||||
|
||||
:param request: requête, contient les fichiers
|
||||
:type request: class:`rest_framework.request.Request`
|
||||
|
||||
:raises: class:`rest_framework.exceptions.APIException`
|
||||
|
||||
:return: réponse
|
||||
:rtype: class:`rest_framework.response.Response`
|
||||
"""
|
||||
try:
|
||||
validator = self.serializer_class(data=request.data)
|
||||
validator.is_valid(raise_exception=True)
|
||||
|
||||
ref_zones_geo_df = read_files_by_type({
|
||||
FileTypes.REF_GEO: validator.validated_data.get('ref_zones_geo')
|
||||
}).get(DataFrameTypes.REF_GEO)
|
||||
|
||||
if ref_zones_geo_df is not None:
|
||||
df = to_table_zone_geographique(ref_zones_geo_df)
|
||||
self.logger.info('Extraction des données du référentiel ------> Succès')
|
||||
|
||||
insert_ZoneGeographique(df)
|
||||
self.logger.info('Mise à jour du référentiel ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : zones géographiques (nécessite %s)', DataFrameTypes.REF_GEO.value[1])
|
||||
return Response({'Insertion réussie'})
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Impossible d'alimenter le(s) référentiel(s)"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
340
backend-django/backend/views/chargement_competences.py
Normal file
340
backend-django/backend/views/chargement_competences.py
Normal file
@@ -0,0 +1,340 @@
|
||||
from typing import List, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from django.core.files.base import File
|
||||
from django.db.models import Q
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404
|
||||
from rest_framework import status
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..models import Administre, Poste
|
||||
from ..models.competence import Competence
|
||||
from ..models.domaine import Domaine
|
||||
from ..models.filiere import Filiere
|
||||
from ..serializers.alimentation import ChargementCompetencesSerializer
|
||||
from ..utils import cleanString
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils_extraction import open_excel
|
||||
|
||||
|
||||
class _SkillFiles():
|
||||
""" Regroupe les constantes de fichiers de compétences """
|
||||
|
||||
class Ref():
|
||||
""" Constantes pour les colonnes du fichier de référence """
|
||||
|
||||
PK = 'Macro compétence libellé court (15 caractères MAXI)' # E (tous onglets)
|
||||
CATEGORIE = 'Catégorie' # A (tous onglets)
|
||||
DOMAINE = 'Domaine' # B (tous onglets)
|
||||
FILIERE = 'Filière' # C (tous onglets)
|
||||
LIBELLE = 'Macro compétence libellé long' # D (tous onglets)
|
||||
|
||||
class Specific():
|
||||
""" Constantes pour les colonnes du fichier de compétences particulières """
|
||||
|
||||
ADMINISTRE_PK = 'N SAP' # A
|
||||
ADMINISTRE_COMPETENCE_1 = 'COMPETENCE 1' # Q
|
||||
ADMINISTRE_COMPETENCE_2 = 'COMPETENCE 2' # R
|
||||
ADMINISTRE_COMPETENCE_3 = 'COMPETENCE 2.1' # S la notation .1 est documentée dans 'read_excel' (pandas)
|
||||
|
||||
POSTE_DOMAINE = 'DOM EIP' # N
|
||||
POSTE_FILIERE = 'FIL EIP' # O
|
||||
POSTE_FE = 'CODE FE' # C
|
||||
POSTE_FONCTION = 'CODE FONCTION' # T
|
||||
POSTE_NF = 'NR EIP' # P
|
||||
POSTE_COMPETENCE_1 = 'COMPETENCE 1' # Q
|
||||
POSTE_COMPETENCE_2 = 'COMPETENCE 2' # R
|
||||
POSTE_COMPETENCE_3 = 'COMPETENCE 2.1' # S la notation .1 est documentée dans 'read_excel' (pandas)
|
||||
|
||||
|
||||
class ChargementCompetenceView(APIView):
|
||||
"""
|
||||
Cette classe est dédiée au vue de chargement des competences.
|
||||
- Charge et traite les fichiers de compétences.
|
||||
- Attribue les compétences présentes aux administrés et postes correspondants
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
parser_classes = [MultiPartParser]
|
||||
serializer_class = ChargementCompetencesSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
def get(self, request):
|
||||
return Response("Formulaire de chargement des référentiels de compétences")
|
||||
|
||||
|
||||
def _read_ref_skills(self, file: File) -> pd.DataFrame:
|
||||
"""
|
||||
Extrait les données du référentiel de compétences.
|
||||
|
||||
:param file: référentiel de compétences
|
||||
:type file: class:`django.core.files.base.File`
|
||||
|
||||
:return: DataFrame
|
||||
:rtype: class:`pandas.DataFrame`
|
||||
"""
|
||||
COLS = _SkillFiles.Ref
|
||||
col_mapping = {
|
||||
COLS.PK: Competence.Cols.PK,
|
||||
COLS.LIBELLE: Competence.Cols.LIBELLE,
|
||||
COLS.CATEGORIE: Competence.Cols.CATEGORIE,
|
||||
COLS.DOMAINE: Competence.Cols.REL_DOMAINE + '_id',
|
||||
COLS.FILIERE: Competence.Cols.REL_FILIERE + '_id'
|
||||
}
|
||||
df = (pd.concat([open_excel(file, sheetname=i, usecols=col_mapping.keys(), engine='openpyxl') for i in range(3)])
|
||||
.dropna(subset=[COLS.PK])
|
||||
.fillna(np.nan)
|
||||
.astype(str)
|
||||
.replace([np.nan, 'nan'], [None, None]))
|
||||
df[COLS.PK] = df[COLS.PK].str.replace('[^a-zA-Z0-9]', '_', regex=True)
|
||||
return (df.drop_duplicates(subset=[COLS.PK])
|
||||
.rename(columns=col_mapping))
|
||||
|
||||
|
||||
def _read_specific_skills(self, administre: bool, file: File) -> pd.DataFrame:
|
||||
"""
|
||||
Extrait les données du fichier de compétences particulières.
|
||||
|
||||
:param administre: True pour les administré, False pour les postes
|
||||
:type administre: bool
|
||||
|
||||
:param file: fichier de compétences particulières
|
||||
:type file: class:`django.core.files.base.File`
|
||||
|
||||
:return: DataFrame
|
||||
:rtype: class:`pandas.DataFrame`
|
||||
"""
|
||||
COLS = _SkillFiles.Specific
|
||||
if administre:
|
||||
col_mapping = {COLS.ADMINISTRE_PK: Administre.Cols.PK}
|
||||
col_types = {COLS.ADMINISTRE_PK: 'int32'}
|
||||
cols_skill = [COLS.ADMINISTRE_COMPETENCE_1, COLS.ADMINISTRE_COMPETENCE_2, COLS.ADMINISTRE_COMPETENCE_3]
|
||||
col_skill = COLS.ADMINISTRE_COMPETENCE_1
|
||||
sheetname = 0
|
||||
else:
|
||||
col_mapping = {
|
||||
COLS.POSTE_DOMAINE: Poste.Cols.REL_DOMAINE + '_id',
|
||||
COLS.POSTE_FILIERE: Poste.Cols.REL_FILIERE + '_id',
|
||||
COLS.POSTE_FE: Poste.Cols.REL_FORMATION_EMPLOI + '_id',
|
||||
COLS.POSTE_FONCTION: Poste.Cols.FONCTION,
|
||||
COLS.POSTE_NF: Poste.Cols.NIVEAU_FONCTIONNEL,
|
||||
}
|
||||
col_types = {c: 'str' for c in col_mapping.keys()}
|
||||
cols_skill = [COLS.POSTE_COMPETENCE_1, COLS.POSTE_COMPETENCE_2, COLS.POSTE_COMPETENCE_3]
|
||||
col_skill = COLS.POSTE_COMPETENCE_1
|
||||
sheetname = 1
|
||||
|
||||
dfs = []
|
||||
for temp_col_skill in cols_skill:
|
||||
dfs.append(open_excel(file, sheetname=sheetname, usecols=[*col_mapping.keys(), temp_col_skill], engine='openpyxl')
|
||||
.rename(columns={temp_col_skill: col_skill}))
|
||||
df = (pd.concat(dfs)
|
||||
.dropna(subset=[col_skill])
|
||||
.fillna(np.nan)
|
||||
.astype({**col_types, COLS.ADMINISTRE_COMPETENCE_1: 'str'})
|
||||
.replace([np.nan, 'nan'], [None, None]))
|
||||
df[col_skill] = df[col_skill].str.replace('[^a-zA-Z0-9]', '_', regex=True)
|
||||
return (df.drop_duplicates()
|
||||
.rename(columns=col_mapping))
|
||||
|
||||
|
||||
@atomic
|
||||
def _update_ref(self, df: pd.DataFrame, domaines_in_db: Union[Tuple[str], List[str]], filieres_in_db: Union[Tuple[str], List[str]]) -> None:
|
||||
"""
|
||||
Met à jour la table des compétences à partir du DataFrame de données de référence.
|
||||
|
||||
:param df: données de référence
|
||||
:type df: class:`pandas.DataFrame`
|
||||
"""
|
||||
ModelType = Competence
|
||||
Cols = ModelType.Cols
|
||||
col_pk = Cols.PK
|
||||
fields_to_update = (Cols.LIBELLE, Cols.CATEGORIE, Cols.REL_DOMAINE + '_id', Cols.REL_FILIERE + '_id')
|
||||
models_in_db = {m.pk: m for m in ModelType.objects.only(col_pk, *fields_to_update)}
|
||||
|
||||
batch_size = 100
|
||||
dict_create = {}
|
||||
dict_update = {}
|
||||
dict_up_to_date = {}
|
||||
error_count = 0
|
||||
to_ignore = {}
|
||||
for idx, rec in enumerate(df.to_dict('records')):
|
||||
pk = rec.get(col_pk)
|
||||
try:
|
||||
domaine = rec.get(Cols.REL_DOMAINE + '_id')
|
||||
if domaine is not None and domaine not in domaines_in_db:
|
||||
to_ignore.setdefault(pk, {}).setdefault(Domaine, domaine)
|
||||
continue
|
||||
filiere = rec.get(Cols.REL_FILIERE + '_id')
|
||||
if filiere is not None and filiere not in filieres_in_db:
|
||||
to_ignore.setdefault(pk, {}).setdefault(Filiere, filiere)
|
||||
continue
|
||||
|
||||
in_db = models_in_db.get(pk)
|
||||
model = ModelType(pk=pk, **{f: rec.get(f) for f in fields_to_update})
|
||||
if not in_db:
|
||||
model.full_clean(validate_unique=False)
|
||||
dict_create.setdefault(pk, model)
|
||||
elif any(getattr(in_db, f) != getattr(model, f) for f in fields_to_update):
|
||||
model.full_clean(validate_unique=False)
|
||||
dict_update.setdefault(pk, model)
|
||||
else:
|
||||
dict_up_to_date.setdefault(pk, model)
|
||||
except Exception:
|
||||
error_count = error_count + 1
|
||||
self.logger.exception('%s une erreur est survenue à la ligne : %s (pk=%s)', ModelType.__name__, idx, pk)
|
||||
|
||||
if error_count:
|
||||
self.logger.warning("%s(s) en erreur : %s", ModelType.__name__, error_count)
|
||||
|
||||
if to_ignore:
|
||||
self.logger.warning('%s(s) ignorée(s) : %s', ModelType.__name__, len(to_ignore))
|
||||
for _pk, _dict in to_ignore.items():
|
||||
self.logger.warning('- %s car :', _pk)
|
||||
for _type, v in _dict.items():
|
||||
self.logger.warning(' - %s absent(e) du référentiel : %s', _type.__name__, v)
|
||||
|
||||
if dict_create:
|
||||
ModelType.objects.bulk_create(dict_create.values(), batch_size=batch_size)
|
||||
self.logger.info('%s(s) créée(s) : %s', ModelType.__name__, len(dict_create))
|
||||
|
||||
if fields_to_update:
|
||||
if dict_update:
|
||||
ModelType.objects.bulk_update(dict_update.values(), batch_size=batch_size, fields=fields_to_update)
|
||||
self.logger.info('%s(s) mise(s) à jour : %s', ModelType.__name__, len(dict_update))
|
||||
|
||||
if dict_up_to_date:
|
||||
self.logger.info('%s(s) déjà à jour : %s', ModelType.__name__, len(dict_up_to_date))
|
||||
|
||||
deleted = ModelType.objects.filter(~Q(pk__in={*dict_create.keys(), *dict_update.keys(), *dict_up_to_date.keys()})).delete()[0]
|
||||
if deleted:
|
||||
self.logger.info('%s(s) supprimée(s) : %s', ModelType.__name__, deleted)
|
||||
|
||||
|
||||
@atomic
|
||||
def _update_specific(self, administre: bool, df: pd.DataFrame, skills_in_db: Union[Tuple[str], List[str]]) -> None:
|
||||
"""
|
||||
Met à jour les liens M2M entre le modèle et les compétences.
|
||||
|
||||
:param administre: True pour les administré, False pour les postes
|
||||
:type administre: bool
|
||||
|
||||
:param df: données de référence
|
||||
:type df: class:`pandas.DataFrame`
|
||||
|
||||
:param skills_in_db: clés de toutes les compétences en base, les autres compétences sont ignorées
|
||||
:type skills_in_db: Union[Tuple[str], List[str]]
|
||||
"""
|
||||
if administre:
|
||||
ModelType = Administre
|
||||
Cols = ModelType.Cols
|
||||
LinkModelType = getattr(ModelType, Cols.M2M_COMPETENCES).through
|
||||
fields_to_filter = (Cols.PK,)
|
||||
col_skill = _SkillFiles.Specific.ADMINISTRE_COMPETENCE_1
|
||||
else:
|
||||
ModelType = Poste
|
||||
Cols = ModelType.Cols
|
||||
LinkModelType = getattr(ModelType, Cols.M2M_COMPETENCES).through
|
||||
fields_to_filter = (Cols.REL_DOMAINE + '_id', Cols.REL_FILIERE + '_id', Cols.REL_FORMATION_EMPLOI + '_id', Cols.FONCTION, Cols.NIVEAU_FONCTIONNEL)
|
||||
col_skill = _SkillFiles.Specific.POSTE_COMPETENCE_1
|
||||
|
||||
link_dict = {}
|
||||
to_ignore = set()
|
||||
for rec in df.to_dict('records'):
|
||||
skill = rec.get(col_skill)
|
||||
if skill not in skills_in_db:
|
||||
to_ignore.add(skill)
|
||||
else:
|
||||
key = tuple(rec.get(f) for f in fields_to_filter)
|
||||
link_dict.setdefault(key, set()).add(skill)
|
||||
if to_ignore:
|
||||
self.logger.warning('%s(s) ignorée(s) car absente(s) du référentiel : %s (%s)', Competence.__name__, len(to_ignore), to_ignore)
|
||||
|
||||
batch_size = 100
|
||||
error_count = 0
|
||||
to_create = []
|
||||
for in_db in ModelType.objects.only('pk', *fields_to_filter):
|
||||
try:
|
||||
links = link_dict.get(tuple(getattr(in_db, f) for f in fields_to_filter)) or ()
|
||||
for link in links:
|
||||
to_create.append(LinkModelType(**{f'{ModelType.__name__.lower()}_id': in_db.pk, 'competence_id': link}))
|
||||
except Exception:
|
||||
error_count = error_count + 1
|
||||
self.logger.exception("une erreur est survenue lors de l'ajout de lien(s) %s[pk=%s]/%s", ModelType.__name__, in_db.pk, Competence.__name__)
|
||||
|
||||
if error_count:
|
||||
self.logger.warning("lien(s) %s/%s en erreur : %s", ModelType.__name__, Competence.__name__, error_count)
|
||||
|
||||
deleted = LinkModelType.objects.all().delete()[0]
|
||||
if deleted:
|
||||
self.logger.info('lien(s) %s/%s supprimé(s) : %s', ModelType.__name__, Competence.__name__, deleted)
|
||||
|
||||
if to_create:
|
||||
LinkModelType.objects.bulk_create(to_create, batch_size=batch_size)
|
||||
self.logger.info('lien(s) %s/%s créé(s) : %s', ModelType.__name__, Competence.__name__, len(to_create))
|
||||
|
||||
|
||||
@execution_time(warn_after=30000, logger_factory=data_perf_logger_factory)
|
||||
@query_count(warn_after=50, logger_factory=data_perf_logger_factory)
|
||||
def post(self, request: Request) -> Response:
|
||||
"""
|
||||
Charge les competences, met à jour la table de compétences et les liens M2M avec les administrés et les postes.
|
||||
|
||||
:param request: Request contenant le fichier de competence
|
||||
:type request: rest_framework.request.Request
|
||||
|
||||
:return: un message
|
||||
:rtype: class:`rest_framework.response.Response`
|
||||
"""
|
||||
try:
|
||||
# validation et récupération des fichiers
|
||||
ser = self.serializer_class(data=request.data)
|
||||
ser.is_valid(raise_exception=True)
|
||||
ref_skill_file = ser.validated_data.get('ref_skills')
|
||||
specific_skill_file = ser.validated_data.get('specific_skills')
|
||||
|
||||
try:
|
||||
df_ref = self._read_ref_skills(ref_skill_file)
|
||||
self.logger.info('Lecture du fichier de référentiel de compétences ------> Succès')
|
||||
self._update_ref(df_ref,
|
||||
domaines_in_db=list(Domaine.objects.values_list('pk', flat=True)),
|
||||
filieres_in_db=list(Filiere.objects.values_list('pk', flat=True)))
|
||||
self.logger.info('Mise à jour du référentiel de compétences ------> Succès')
|
||||
except Exception as e :
|
||||
self.logger.info('Lecture du fichier de référentiel de compétences ------> Ignoré')
|
||||
self.logger.info(e)
|
||||
|
||||
try:
|
||||
df_specific_administre = self._read_specific_skills(True, specific_skill_file)
|
||||
df_specific_poste = self._read_specific_skills(False, specific_skill_file)
|
||||
self.logger.info("Lecture des compétences particulières d'administrés ------> Succès")
|
||||
self.logger.info('Lecture des compétences particulières de postes ------> Succès')
|
||||
ref_data = list(Competence.objects.values_list('pk', flat=True))
|
||||
self._update_specific(True, df_specific_administre, ref_data)
|
||||
self.logger.info("Mise à jour des compétences particulières d'administrés ------> Succès")
|
||||
self._update_specific(False, df_specific_poste, ref_data)
|
||||
self.logger.info('Mise à jour des compétences particulières de postes ------> Success')
|
||||
except Exception as e :
|
||||
self.logger.info("Mise à jour des compétences particulières d'administrés et postes ------> Ignoré")
|
||||
self.logger.info(e)
|
||||
|
||||
return Response({'Insertion réussie'})
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Impossible d'alimenter le référentiel de compétences"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
189
backend-django/backend/views/chargement_pam.py
Normal file
189
backend-django/backend/views/chargement_pam.py
Normal file
@@ -0,0 +1,189 @@
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from django.db.models import Q
|
||||
import datetime
|
||||
from typing import Any, List, Tuple, Union
|
||||
|
||||
from ..utils_extraction import to_table_pam
|
||||
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils_insertion import insert_PAM
|
||||
from ..models import Administres_Pams, Postes_Pams,Calcul, Poste, PAM, StatutPamChoices as StatutPam, AvisPosteChoices
|
||||
from ..utils.insertion.commun import batch_iterator
|
||||
|
||||
class AlimentationPamView(APIView):
|
||||
""" Vue pour charger un nouveau PAM """
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
|
||||
|
||||
def get(self, request):
|
||||
try:
|
||||
self.logger.debug('Début de la mise à jour de la table PAM, veuillez patienter...')
|
||||
# today = datetime.date(2023,1,1)
|
||||
today = datetime.datetime.now()
|
||||
insert_PAM(to_table_pam(today))
|
||||
|
||||
self.logger.debug('Insertion/Mise à jour de la table PAM --------------------------------------------> Success')
|
||||
|
||||
annee_pam = PAM.objects.filter(pam_statut='PAM en cours')[0].pam_id
|
||||
annee_pam_suivant = PAM.objects.filter(pam_statut='PAM A+1')[0].pam_id
|
||||
|
||||
self.logger.debug('Mise à jour des données des administrés pour les PAM A et A+1, veuillez patienter ...')
|
||||
dict_create_administres = {}
|
||||
dict_update_administres = {}
|
||||
|
||||
qs = Administres_Pams.objects.all().filter(pam_id=annee_pam).values('administre_id')
|
||||
|
||||
batch_size = 50
|
||||
admin_suivant = dict((f"{o.administre_id}", o) for o in Administres_Pams.objects.all().filter(pam_id=annee_pam_suivant))
|
||||
|
||||
for a_id_sap in qs:
|
||||
administre_id=a_id_sap['administre_id']
|
||||
pk = str(administre_id) + str(annee_pam_suivant)
|
||||
|
||||
model_administre = Administres_Pams(**{
|
||||
'id' :pk,
|
||||
'pam_id' :annee_pam_suivant,
|
||||
'administre_id' :administre_id,
|
||||
'a_statut_pam_annee' :StatutPam.NON_ETUDIE,
|
||||
'notes_pam' :None,
|
||||
'a_ciat_pam' :False,
|
||||
'a_specifique_pam' :None,
|
||||
'a_liste_depts_souhaites_pam' :None,
|
||||
'a_liste_zones_geographiques_shm_pam' :None,
|
||||
'a_situationfuture_notes_fe' :None,
|
||||
})
|
||||
|
||||
if str(administre_id) not in admin_suivant :
|
||||
dict_create_administres.setdefault(pk, model_administre)
|
||||
|
||||
else:
|
||||
dict_update_administres.setdefault(pk, model_administre)
|
||||
|
||||
|
||||
if dict_create_administres:
|
||||
self.logger.debug("Nombre d'administrés à créer dans l'année %s : %s'",annee_pam_suivant,len(dict_create_administres))
|
||||
for idx, data_batch in enumerate(batch_iterator(list(dict_create_administres.values()), batch_size)):
|
||||
try:
|
||||
Administres_Pams.objects.bulk_create(data_batch)
|
||||
except Exception as e:
|
||||
self.logger.exception("Cet administré existe déjà dans la base : %s", e)
|
||||
|
||||
self.logger.debug('Mise à jour des liens Administres/Pams terminés.')
|
||||
|
||||
self.logger.debug('Mise à jour des données des postes pour les PAM A et A+1, veuillez patienter ...')
|
||||
dict_create_postes = {}
|
||||
dict_update_postes = {}
|
||||
|
||||
# def mise_a_jour_info_reo(info_reo, annee_pam):
|
||||
# if "SUREFF" in info_reo:
|
||||
# model.info_reo=info_reo
|
||||
# else:
|
||||
# model.info_reo=f'REO {annee_pam}'
|
||||
# return model.info_reo
|
||||
|
||||
qs = Postes_Pams.objects.all().filter(p_pam_id=annee_pam).values_list('poste_id','info_reo')
|
||||
|
||||
batch_size = 50
|
||||
poste_suivant = dict((f"{o.poste_id}", o) for o in Postes_Pams.objects.all().filter(p_pam_id=annee_pam_suivant))
|
||||
|
||||
for poste in qs:
|
||||
poste_id=poste[0]
|
||||
info_reo= poste[1]
|
||||
pk = str(poste_id) + str(annee_pam_suivant)
|
||||
model_poste = Postes_Pams(**{
|
||||
'id' :pk,
|
||||
'p_pam_id' :annee_pam_suivant,
|
||||
'poste_id' :poste_id,
|
||||
'p_avis_pam' :StatutPam.NON_ETUDIE,
|
||||
'p_avis_fe_pam' :StatutPam.NON_ETUDIE,
|
||||
'p_direct_commissionne_pam' :None,
|
||||
'p_notes_gestionnaire_pam' :None,
|
||||
'p_priorisation_pcp_pam' :None,
|
||||
'info_reo' :info_reo if "SUREFF" in info_reo else f'REO {annee_pam}',
|
||||
})
|
||||
|
||||
if str(poste_id) not in poste_suivant :
|
||||
dict_create_postes.setdefault(pk, model_poste)
|
||||
|
||||
else:
|
||||
dict_update_postes.setdefault(pk, model_poste)
|
||||
|
||||
if dict_create_postes:
|
||||
self.logger.debug("Nombre de postes à créer dans l'année %s : %s'",annee_pam_suivant,len(dict_create_postes))
|
||||
for idx, data_batch in enumerate(batch_iterator(list(dict_create_postes.values()), batch_size)):
|
||||
try:
|
||||
Postes_Pams.objects.bulk_create(data_batch)
|
||||
except Exception as e:
|
||||
self.logger.exception("Ce poste existe déjà dans la base : %s", e)
|
||||
|
||||
self.logger.debug('Mise à jour des liens Postes/Pams terminés.')
|
||||
|
||||
self.logger.debug(f'Début de la suppression des données du PAM {int(annee_pam)-1}, veuillez patienter ...')
|
||||
PAM.objects.filter(pam_id=int(annee_pam)-1).delete()
|
||||
self.logger.debug("Fin de l'initialisation/Mise à jour du PAM")
|
||||
|
||||
|
||||
return Response({'Initialisation/Mise à jour du Pam réussie'})
|
||||
|
||||
except (Http404, APIException):
|
||||
message = "Impossible de mettre à jour ce PAM"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
|
||||
class NettoyagePamView(APIView):
|
||||
""" Vue pour nettoyer les postes tagués en SUP REO """
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
def get(self, request):
|
||||
try:
|
||||
|
||||
self.logger.debug("Suppression des postes tagués en SUP REO de l'année A --------------------------------------------> Success")
|
||||
|
||||
annee_pam = PAM.objects.filter(pam_statut='PAM en cours')[0].pam_id
|
||||
|
||||
qs = Postes_Pams.objects.all().filter(p_pam_id=annee_pam).values('poste_id')
|
||||
|
||||
for p_id in qs:
|
||||
poste_id=p_id['poste_id']
|
||||
|
||||
if Postes_Pams.objects.filter(Q(info_reo='SUP REO') & Q(poste_id=poste_id)):
|
||||
self.logger.debug(f'Suppression du poste {poste_id}')
|
||||
Poste.objects.filter(p_id=poste_id).delete()
|
||||
|
||||
#Mise à jour des postes tagues CREE REO A en REO A
|
||||
if not Postes_Pams.objects.filter(Q(p_pam_id=annee_pam) & Q(poste_id=poste_id) & Q(info_reo__contains='SUREFF')):
|
||||
Postes_Pams.objects.filter(Q(p_pam_id=annee_pam) & Q(poste_id=poste_id)).update(info_reo=f'REO {annee_pam}')
|
||||
|
||||
self.logger.debug(f'Début de la suppression des administrés du PAM {int(annee_pam)-1}')
|
||||
Administres_Pams.objects.filter(pam_id=int(annee_pam)-1).delete()
|
||||
self.logger.debug(f'Début de la suppression des postes du PAM {int(annee_pam)-1}')
|
||||
Postes_Pams.objects.filter(p_pam_id=int(annee_pam)-1).delete()
|
||||
Calcul.objects.filter(pam_id=int(annee_pam)-1).delete()
|
||||
self.logger.debug(f'Fin de la suppression des données du PAM {int(annee_pam)-1}')
|
||||
|
||||
|
||||
return Response({'Mise à jour du Pam réussie'})
|
||||
|
||||
except (Http404, APIException):
|
||||
message = "Impossible de mettre à jour ce PAM"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
292
backend-django/backend/views/commun.py
Normal file
292
backend-django/backend/views/commun.py
Normal file
@@ -0,0 +1,292 @@
|
||||
# --------------------------------------------------------------------------------
|
||||
"""
|
||||
Définitions communes à plusieurs vues
|
||||
"""
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
from django.db.models import Q
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import SAFE_METHODS, BasePermission
|
||||
|
||||
from ..models import Administre, Decision, Poste, SpecifiqueChoices, Administres_Pams, Postes_Pams
|
||||
|
||||
from ..utils.decorators import (class_logger, decorate_functions,
|
||||
execution_time, query_count)
|
||||
from ..utils.permissions import (Profiles, KEY_READ, KEY_WRITE,
|
||||
get_profile_summary,
|
||||
get_adm_filter_by_lvl4_codes_fil,
|
||||
get_adm_filter_by_lvl4_codes_future_pcp,
|
||||
get_adm_filter_by_lvl4_codes_pcp,
|
||||
get_lvl4_org_codes_by_any_code,
|
||||
get_poste_filter_by_lvl4_codes_fil,
|
||||
get_poste_filter_by_lvl4_codes_pcp,
|
||||
get_queryset_org_by_user, is_truthy)
|
||||
from ..utils.view_predicates import viewset_functions
|
||||
|
||||
# décorateur pour tracer le temps d'exécution d'une vue
|
||||
execution_time_viewset = decorate_functions(
|
||||
lambda cls: execution_time(warn_after=5000, logger_name=cls),
|
||||
viewset_functions,
|
||||
factory=True
|
||||
)
|
||||
|
||||
# décorateur pour tracer le nombre de requêtes d'une vue
|
||||
query_count_viewset = decorate_functions(
|
||||
lambda cls: query_count(warn_after=20, logger_name=cls),
|
||||
viewset_functions,
|
||||
factory=True
|
||||
)
|
||||
|
||||
def api_exception(status_code: int, message: str = None) -> APIException:
|
||||
"""
|
||||
Crée une APIException avec un statut personnalisé.
|
||||
note : inutile d'appeler cette fonction pour un code 500 car APIException(message) suffit
|
||||
|
||||
:param status_code: code de statut HTTP
|
||||
:type status_code: int
|
||||
|
||||
:param message: message de l'exception
|
||||
:type message: str, optional
|
||||
|
||||
:return: exception
|
||||
:rtype: class:`APIException`
|
||||
"""
|
||||
return type('MyException', (APIException,), {'status_code': status_code, 'default_detail': message})
|
||||
|
||||
|
||||
@class_logger
|
||||
class GestionnairePermission(BasePermission):
|
||||
"""
|
||||
Classe de gestion des droits de lecture et d'écriture d'un gestionnaire
|
||||
"""
|
||||
message = "Le gestionnaire n'a pas les droits de lecture ou d'écriture"
|
||||
|
||||
def is_in_list_ok(self, list, list_ok): # A modif en utilisant set
|
||||
"""
|
||||
Cette fonction vérifie que tous les objets de la liste list soient dans la liste list_ok
|
||||
|
||||
:param list: liste 1
|
||||
:type list: list
|
||||
|
||||
:param list: liste 2
|
||||
:type list: list
|
||||
|
||||
:return: booléen indiquant si les objets de la liste 1 sont dans la liste 2
|
||||
:rtype: bool
|
||||
"""
|
||||
for obj in list:
|
||||
if obj not in list_ok:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def verif_droits_adm(self, pk, codes_lvl4, is_fil, is_pcp, is_bvt):
|
||||
"""
|
||||
Cette fonction vérifie si le gestionnaire a bien le droit de modifier (mettre à jour)
|
||||
les attributs du ou des administres d'identifiant pk.
|
||||
|
||||
:param pk: id sap du ou des administrés édités
|
||||
:type org_code: int ou list
|
||||
|
||||
:param codes_lvl4: codes de niveau 4 des référentiels organiques liés à l'utilisateur
|
||||
:type codes_lvl4: Tuple[str]
|
||||
|
||||
:param is_fil: profil filiere
|
||||
:type is_fil: bool
|
||||
|
||||
:param is_pcp: profil pcp
|
||||
:type is_pcp: bool
|
||||
|
||||
:param is_bvt: profil bvt
|
||||
:type is_bvt: bool
|
||||
|
||||
:return: booléen pour indiqué si l'utilisateur a les droits ou non sur l'édition
|
||||
du ou des administrés concernés
|
||||
:rtype: bool
|
||||
"""
|
||||
# On récupère le ou les administrés dont l'id match avec pk (liste ou non)
|
||||
qs = Administre.objects.filter(a_id_sap__in=pk) if isinstance(pk, list) else Administre.objects.filter(a_id_sap=pk)
|
||||
list_a = list(qs.values_list('pk', flat=True))
|
||||
|
||||
# On récupère le ou les administrés autorisés
|
||||
adm_filter = None
|
||||
if is_fil:
|
||||
adm_filter = get_adm_filter_by_lvl4_codes_fil(codes_lvl4)
|
||||
if is_pcp:
|
||||
pcp_filter = get_adm_filter_by_lvl4_codes_pcp(codes_lvl4) | get_adm_filter_by_lvl4_codes_future_pcp(codes_lvl4)
|
||||
adm_filter = adm_filter | pcp_filter if adm_filter else pcp_filter
|
||||
if is_bvt:
|
||||
bvt_filter = Q(**{f'{Administres_Pams.Cols.REL_ADMINISTRE}__{Administre.Cols.REL_SOUS_VIVIER}': 'BVT'})
|
||||
adm_filter = adm_filter | bvt_filter if adm_filter else bvt_filter
|
||||
|
||||
list_a_ok = Administres_Pams.objects.filter(adm_filter).values_list('administre_id', flat=True)
|
||||
perm = self.is_in_list_ok(list_a, list_a_ok)
|
||||
|
||||
msg = 'Administre rights verification ok\n' if perm else 'Administre rights verification not ok\n'
|
||||
self.logger.debug(msg)
|
||||
|
||||
return perm
|
||||
|
||||
|
||||
def verif_droits_poste(self, pk, codes_lvl4, is_fil, is_pcp, is_bvt, is_itd):
|
||||
"""
|
||||
Cette fonction vérifie si le gestionnaire a bien le droit de modifier (mettre à jour)
|
||||
les attributs du ou des postes d'identifiant pk.
|
||||
|
||||
:param pk: id sap du ou des administrés édités
|
||||
:type org_code: int ou list
|
||||
|
||||
:param codes_lvl4: codes de niveau 4 des référentiels organiques liés à l'utilisateur
|
||||
:type codes_lvl4: Tuple[str]
|
||||
|
||||
:param is_fil: profil filiere
|
||||
:type is_fil: bool
|
||||
|
||||
:param is_pcp: profil pcp
|
||||
:type is_pcp: bool
|
||||
|
||||
:param is_bvt: profil bvt
|
||||
:type is_bvt: bool
|
||||
|
||||
:param is_itd: profil itd
|
||||
:type is_itd: bool
|
||||
|
||||
:return: booléen pour indiqué si l'utilisateur a les droits ou non sur l'édition
|
||||
du ou des postes concernés
|
||||
:rtype: bool
|
||||
"""
|
||||
# On récupère le ou les postes dont l'id match avec pk (liste ou non)
|
||||
qs = Poste.objects.filter(p_id__in=pk) if isinstance(pk, list) else Poste.objects.filter(p_id=pk)
|
||||
list_p = list(qs.values_list('pk', flat=True))
|
||||
|
||||
# On créer le filtre avec le ou les postes autorisés
|
||||
poste_filter = None
|
||||
if is_fil:
|
||||
poste_filter = get_poste_filter_by_lvl4_codes_fil(codes_lvl4)
|
||||
if is_pcp:
|
||||
pcp_filter = get_poste_filter_by_lvl4_codes_pcp(codes_lvl4)
|
||||
poste_filter = poste_filter | pcp_filter if poste_filter else pcp_filter
|
||||
if is_itd:
|
||||
itd_filter = Q(**{f'{Postes_Pams.Cols.REL_POSTE}__p_specifique' : SpecifiqueChoices.ITD})
|
||||
poste_filter = poste_filter | itd_filter if poste_filter else itd_filter
|
||||
if is_bvt:
|
||||
bvt_filter = Q(**{f'{Postes_Pams.Cols.REL_POSTE}__{Poste.Cols.M2M_SOUS_VIVIERS}': 'BVT'})
|
||||
poste_filter = poste_filter | bvt_filter if poste_filter else bvt_filter
|
||||
|
||||
list_p_ok = Postes_Pams.objects.filter(poste_filter).values_list('poste_id', flat=True)
|
||||
perm = self.is_in_list_ok(list_p, list_p_ok)
|
||||
|
||||
msg = 'Poste rights verification ok\n' if perm else 'Poste rights verification not ok\n'
|
||||
self.logger.debug(msg)
|
||||
|
||||
return perm
|
||||
|
||||
|
||||
def commun_verif(self, request, view, obj=None):
|
||||
"""
|
||||
Partie commune de la vérification des droits de lecture aux fonctions has_permission et has_object_permission
|
||||
"""
|
||||
P = Profiles
|
||||
|
||||
user = request.user
|
||||
if not user or not user.is_authenticated:
|
||||
self.logger.debug('No user connected\n')
|
||||
return False
|
||||
|
||||
is_super = user.is_superuser
|
||||
|
||||
if is_super and str(view) in ['ReportingView']:
|
||||
self.logger.debug('Rights verification ok : the user is a superuser\n')
|
||||
return True
|
||||
|
||||
summary = get_profile_summary(user)
|
||||
profiles = summary.profiles
|
||||
r_profiles = profiles.get(KEY_READ, ())
|
||||
w_profiles = profiles.get(KEY_WRITE, ())
|
||||
|
||||
if not profiles:
|
||||
self.logger.debug('The user as no profile\n')
|
||||
return False
|
||||
|
||||
if request.method in SAFE_METHODS:
|
||||
resp = True if (r_profiles and r_profiles!=(P.SUPER,)) else False
|
||||
if not resp:
|
||||
msg = 'The user does not have reading rights\n'
|
||||
self.logger.debug(msg)
|
||||
return resp
|
||||
|
||||
# La gestion de droits des administrateurs est faite via IsAdminUser pour les api d'alimentation
|
||||
is_fil = P.FILIERE in w_profiles
|
||||
is_pcp = P.PCP in w_profiles
|
||||
is_bvt = P.BVT in w_profiles
|
||||
is_itd = P.ITD in w_profiles
|
||||
is_hme = P.HME in w_profiles
|
||||
|
||||
codes_lvl4 = get_lvl4_org_codes_by_any_code(summary.org_code)
|
||||
|
||||
if obj: # Modification d'une seule instance
|
||||
if not w_profiles or w_profiles==(P.SUPER,):
|
||||
self.logger.debug('The user does not have writing rights\n')
|
||||
return False
|
||||
|
||||
if isinstance(obj, Administre):
|
||||
pk = obj.pk
|
||||
return self.verif_droits_adm(pk, codes_lvl4, is_fil, is_pcp, is_bvt)
|
||||
|
||||
elif isinstance(obj, Poste):
|
||||
pk = obj.pk
|
||||
return self.verif_droits_poste(pk, codes_lvl4, is_fil, is_pcp, is_bvt, is_itd)
|
||||
|
||||
elif isinstance(obj, Decision):
|
||||
a_pk = obj.administre.pk
|
||||
p_pk = obj.poste.pk
|
||||
return self.verif_droits_poste(p_pk, codes_lvl4, is_fil, is_pcp, is_bvt, is_itd) or \
|
||||
self.verif_droits_adm(a_pk, codes_lvl4, is_fil, is_pcp, is_bvt)
|
||||
|
||||
else: # Vérification globale ou modification multiple
|
||||
|
||||
self.logger.debug('---- Writing profiles ----')
|
||||
self.logger.debug('Expert HME : %s', is_hme)
|
||||
self.logger.debug('Pameur BMOB : %s', is_pcp)
|
||||
self.logger.debug('Gestionnaire BVT : %s', is_bvt)
|
||||
self.logger.debug('Gestionnaire ITD : %s', is_itd)
|
||||
self.logger.debug('Gestionnaire BGCAT : %s', is_fil)
|
||||
self.logger.debug('--------------------------')
|
||||
|
||||
if isinstance(request.data, list):
|
||||
if not w_profiles or w_profiles==(P.SUPER,):
|
||||
self.logger.debug('The user does not have writing rights\n')
|
||||
return False
|
||||
|
||||
if 'a_id_sap' in request.data[0].keys():
|
||||
pk = [adm['a_id_sap'] for adm in request.data]
|
||||
return self.verif_droits_adm(pk, codes_lvl4, is_fil, is_pcp, is_bvt)
|
||||
|
||||
elif 'p_id' in request.data[0].keys():
|
||||
pk = [p['p_id'] for p in request.data]
|
||||
return self.verif_droits_poste(pk, codes_lvl4, is_fil, is_pcp, is_bvt, is_itd)
|
||||
|
||||
self.logger.debug('Global rights verification ok\n')
|
||||
return True # On autorise une permission globale pour ensuite permettre des vérifications au niveau des instances avec has_object_permission
|
||||
|
||||
self.logger.debug('User rights verification failed\n')
|
||||
return False
|
||||
|
||||
|
||||
def has_permission(self, request, view):
|
||||
if request.method != 'GET':
|
||||
self.logger.debug('------------------------- Permissions verification ---------------------')
|
||||
self.logger.debug('Request method : %s', request.method)
|
||||
self.logger.debug('Request data : %s', request.data)
|
||||
self.logger.debug('View : %s', view)
|
||||
|
||||
return self.commun_verif(request=request, view=view)
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
self.logger.debug('--------------------- Object permissions verification ------------------')
|
||||
self.logger.debug('Request method : %s', request.method)
|
||||
self.logger.debug('Request data : %s', request.data)
|
||||
self.logger.debug('View : %s', view)
|
||||
self.logger.debug('Object : %s', obj)
|
||||
|
||||
return self.commun_verif(request=request, view=view, obj=obj)
|
||||
46
backend-django/backend/views/current_user.py
Normal file
46
backend-django/backend/views/current_user.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..models import PcpFeGroupe, SousVivier, FormationEmploi
|
||||
from ..serializers.current_user import (CTX_KEY_PCP_FE_GROUPE,
|
||||
CTX_KEY_PROFILES, CTX_KEY_SOUS_VIVIERS,
|
||||
CTX_KEY_FORMATION_EMPLOIS, CTX_KEY_SOUS_VIVIERS_SUPER,
|
||||
UserInfoSerializer)
|
||||
from ..utils.decorators import class_logger
|
||||
from ..utils.permissions import KEY_READ, Profiles, get_profile_summary
|
||||
from .commun import execution_time_viewset, query_count_viewset
|
||||
|
||||
|
||||
@class_logger
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class CurrentUserView(APIView):
|
||||
"""
|
||||
Cette classe est dédiée au vue de l'utilisateur courant:
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
# TODO adapter au nouveau système de gestion de profils
|
||||
def get(self, request: Request) -> Response:
|
||||
"""
|
||||
Renvoie les informations de l'utilisateur connecté.
|
||||
|
||||
:param request: requête contenant l'utilisateur authentifié
|
||||
:type request: class:`rest_framework.request.Request`
|
||||
|
||||
:return: réponse contenant les informations de l'utilisateur dont la formation emploi group et les sous-viviers specifiques à cet utilisateur.
|
||||
:rtype: class:`rest_framework.response.Response`
|
||||
"""
|
||||
user = request.user
|
||||
summary = get_profile_summary(request.user)
|
||||
profiles = summary.profiles.get(KEY_READ, ())
|
||||
return Response(UserInfoSerializer(user, context={
|
||||
# CTX_KEY_SOUS_VIVIERS_SUPER: SousVivier.objects.all() if Profiles.SUPER in profiles else None,
|
||||
CTX_KEY_SOUS_VIVIERS: SousVivier.objects.filter(gestionnaires__id=request.user.id),
|
||||
CTX_KEY_FORMATION_EMPLOIS: FormationEmploi.objects.filter(gestionnaires__id=request.user.id).order_by('fe_mere_credo').distinct('fe_mere_credo'),
|
||||
CTX_KEY_PCP_FE_GROUPE: PcpFeGroupe.objects.filter(gestionnaire_id=user.pk).first(),
|
||||
CTX_KEY_PROFILES: summary.profiles
|
||||
}).data)
|
||||
152
backend-django/backend/views/decision.py
Normal file
152
backend-django/backend/views/decision.py
Normal file
@@ -0,0 +1,152 @@
|
||||
from django.db.transaction import atomic
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
import datetime
|
||||
from ..models import Administre, Decision, DecisionChoices, Notation, Poste
|
||||
from ..serializers.decision import CreateDecisionSerializer, DecisionSerializer
|
||||
from ..utils.decorators import class_logger
|
||||
from .commun import (GestionnairePermission, api_exception,
|
||||
execution_time_viewset, query_count_viewset)
|
||||
|
||||
|
||||
@class_logger
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class DecisionView(ModelViewSet):
|
||||
"""
|
||||
Cette classe est dédiée au vue des decisions.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, GestionnairePermission]
|
||||
serializer_class = DecisionSerializer
|
||||
queryset = Decision.objects.all()
|
||||
|
||||
def get_serializer_class(self):
|
||||
""" Renvoie un serializer particulier pour la création """
|
||||
if self.action == 'create':
|
||||
return CreateDecisionSerializer
|
||||
return self.serializer_class
|
||||
|
||||
def list(self, request: Request, pk=None) -> Response:
|
||||
"""Cette fonction envoie les informations du poste lié à l'administre dans une décision avec le score de l'administre et inversement.
|
||||
TODO: rajouter la possibilité d'envoyer les informations du poste lié à l'administré selon l'année du PAM dans lequel on se situe
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'administre ou le poste.
|
||||
|
||||
:return: - **res_decision** (*Response*): Json contenant le classement.
|
||||
"""
|
||||
decision = []
|
||||
q = 'poste'
|
||||
administre_id = None
|
||||
poste_id = None
|
||||
|
||||
if 'pam_annee' in request.query_params:
|
||||
annee_pam = request.query_params['pam_annee']
|
||||
else :
|
||||
annee_pam = None
|
||||
|
||||
if 'administre_id' in request.query_params:
|
||||
administre_id = request.query_params['administre_id']
|
||||
administre_pam_id = str(administre_id) + str(annee_pam)
|
||||
administres_keys = (
|
||||
'poste__p_id', 'poste__p_nf', 'poste__p_domaine', 'poste__p_filiere',
|
||||
'poste__p_eip', #'poste__p_avis', TODO : Change p_avis to postesPAM
|
||||
'poste__formation_emploi__fe_code', 'poste__competences',
|
||||
'poste__p_notes_gestionnaire', 'poste__p_liste_id_marques',
|
||||
'poste__formation_emploi__fe_libelle',
|
||||
'poste__formation_emploi__fe_garnison_lieu',
|
||||
'poste__p_dep', 'poste__p_code_fonction',
|
||||
'poste__p_fonction',
|
||||
'de_decision', 'de_date_decision', 'de_notes_gestionnaire')
|
||||
decision = Decision.objects.filter(administre_pam_id = administre_pam_id).select_related('poste')
|
||||
decision = list(decision.values(*administres_keys))
|
||||
|
||||
if 'poste_id' in request.query_params:
|
||||
q = "administre"
|
||||
poste_id = request.query_params['poste_id']
|
||||
poste_pam_id = poste_id + str(annee_pam)
|
||||
postes_keys = (
|
||||
'administre__a_id_sap', 'administre__a_nom', 'administre__a_prenom', 'administre__administre__a_statut_pam_annee',
|
||||
'administre__a_fonction', 'administre__a_code_fonction', 'administre__a_liste_id_competences',
|
||||
'administre__grade_id', 'administre__a_liste_id_marques', 'de_decision', 'de_date_decision',
|
||||
'de_notes_gestionnaire', 'administre__a_notes_gestionnaire', 'administre__a_liste_id_competences',
|
||||
'administre__decision__poste_id')
|
||||
|
||||
decision = Decision.objects.filter(poste_pam_id = poste_pam_id).select_related('administre')
|
||||
|
||||
decision = list(decision.values(*postes_keys))
|
||||
res_decision = []
|
||||
if len(decision) > 0:
|
||||
for k in range(len(decision)):
|
||||
decision_unit = decision[k]
|
||||
try:
|
||||
administre_id = decision_unit.administre_id
|
||||
except:
|
||||
administre_id = None
|
||||
|
||||
try:
|
||||
poste_id = decision_unit.poste_id
|
||||
except:
|
||||
poste_id = None
|
||||
|
||||
try:
|
||||
res_decision_unit = {q: {}, 'no_score_administre': Notation.objects.get(
|
||||
administre_id=administre_id,
|
||||
poste_id=poste_id, pam_id = annee_pam).no_score_administre}
|
||||
except:
|
||||
res_decision_unit = {q: {}, 'no_score_administre': None}
|
||||
for key in decision_unit:
|
||||
if (q + "__") in key:
|
||||
res_decision_unit[q][key.replace(q + '__', '')] = decision_unit[key]
|
||||
else:
|
||||
res_decision_unit[key] = decision_unit[key]
|
||||
# Ajout du relevé des décisions sur le poste (cas q = "poste")
|
||||
if q == "poste":
|
||||
res_decision_unit[q]['p_nb_prepositionne'] = Decision.objects.filter(
|
||||
de_decision=DecisionChoices.PREPOSITIONNE).count()
|
||||
res_decision_unit[q]['p_nb_positionne'] = Decision.objects.filter(
|
||||
de_decision=DecisionChoices.POSITIONNE).count()
|
||||
res_decision_unit[q]['p_nb_omi_active'] = Decision.objects.filter(
|
||||
de_decision=DecisionChoices.OMI_ACTIVE).count()
|
||||
res_decision_unit[q]['p_nb_omi_en_cours'] = Decision.objects.filter(
|
||||
de_decision=DecisionChoices.OMI_EN_COURS).count()
|
||||
res_decision.append(res_decision_unit)
|
||||
return Response(res_decision)
|
||||
|
||||
@atomic
|
||||
def perform_create(self, serializer) -> None:
|
||||
"""Cette fonction crée une decision à partir de données validées """
|
||||
|
||||
data = serializer.validated_data
|
||||
annee_pam = self.request.query_params['pam__in']
|
||||
administre_id = data.get('administre_id')
|
||||
administre_id_pam = str(administre_id) + annee_pam
|
||||
poste_id = data.get('poste_id')
|
||||
poste_id_pam = poste_id + annee_pam
|
||||
de_decision = data.get('de_decision')
|
||||
delete_former = data.get('delete_former')
|
||||
a = get_object_or_404(Administre, pk=administre_id)
|
||||
p = get_object_or_404(Poste, pk=poste_id)
|
||||
self.check_object_permissions(self.request, a)
|
||||
self.check_object_permissions(self.request, p)
|
||||
|
||||
qs_former = Decision.objects.filter(pk=administre_id)
|
||||
if delete_former:
|
||||
qs_former.delete()
|
||||
elif qs_former.exists():
|
||||
raise api_exception(status.HTTP_400_BAD_REQUEST, "une décision existe déjà pour cet administré")
|
||||
|
||||
Decision.objects.create(administre_id=administre_id, poste_id=poste_id, de_decision=de_decision, de_date_decision=timezone.now(),
|
||||
administre_pam_id=administre_id_pam, poste_pam_id=poste_id_pam )
|
||||
|
||||
def perform_update(self, serializer) -> None:
|
||||
""" Met à jour la décision à partir de données validées. La date est mise à jour en même temps que le statut """
|
||||
data = serializer.validated_data
|
||||
if hasattr(data, Decision.Cols.STATUT):
|
||||
setattr(data, Decision.Cols.DATE, timezone.now())
|
||||
super().perform_update(serializer)
|
||||
580
backend-django/backend/views/exportation_fichiers.py
Normal file
580
backend-django/backend/views/exportation_fichiers.py
Normal file
@@ -0,0 +1,580 @@
|
||||
import time
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from django.utils import timezone
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404, HttpResponse
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..utils_extraction import APP_NAN
|
||||
from ..utils.alimentation import BOCols, ReoCols
|
||||
from ..serializers import ExportationSerializer
|
||||
from ..models import Administre, Affectation, Administre_Notation, Poste, FichiersExporte
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
|
||||
|
||||
class ExportationFichiersView(APIView):
|
||||
""" Vue pour exporter les données de la base dans des fichiers au format de ceux utilisés pour l'insertion"""
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
serializer_class = ExportationSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
def get(self, request):
|
||||
resp = []
|
||||
resp.append(["Formulaire d\'OGURE NG permettant d'exporter les données de la base au format des fichiers d'insertion."])
|
||||
resp.append(["Selectionnez le fichier que vous souhaitez exporter puis appuyez sur le bouton 'POST' pour lancer l'exportation."])
|
||||
resp = pd.DataFrame(resp)
|
||||
return Response(resp[0])
|
||||
|
||||
def export_adm_bo_df(self):
|
||||
self.logger.info("Lecture et traitement de la table des administrés... ")
|
||||
start_time_adm = time.time()
|
||||
|
||||
Cols = Administre.Cols
|
||||
administres = Administre.objects.all().values_list(
|
||||
Cols.PK,
|
||||
Cols.REL_GRADE,
|
||||
'a_nom',
|
||||
'a_prenom',
|
||||
'a_id_def',
|
||||
'a_eip',
|
||||
'a_eis',
|
||||
'a_domaine_gestion',
|
||||
Cols.REL_DOMAINE,
|
||||
Cols.REL_FILIERE,
|
||||
'a_nf',
|
||||
'a_arme',
|
||||
'a_rg_origine_recrutement',
|
||||
'a_date_entree_service',
|
||||
'a_nombre_enfants',
|
||||
'a_origine_recrutement',
|
||||
'a_date_naissance',
|
||||
'a_fonction',
|
||||
'a_diplome_hl',
|
||||
'a_dernier_diplome',
|
||||
'a_date_rdc',
|
||||
'a_credo_fe',
|
||||
f'{Cols.REL_FORMATION_EMPLOI}__fe_garnison_lieu',
|
||||
'a_date_arrivee_fe',
|
||||
'a_date_dernier_acr',
|
||||
'a_pos_statuaire',
|
||||
'a_date_pos_statuaire',
|
||||
'a_interruption_service',
|
||||
'a_grade_date_debut',
|
||||
'a_profession_conjoint',
|
||||
'a_sap_conjoint',
|
||||
'a_id_def_conjoint',
|
||||
'a_sexe_conjoint',
|
||||
'a_date_fonction1',
|
||||
'a_fonction1',
|
||||
'a_date_fonction2',
|
||||
'a_fonction2',
|
||||
'a_date_fonction3',
|
||||
'a_fonction3',
|
||||
'a_date_fonction4',
|
||||
'a_fonction4',
|
||||
'a_date_fonction5',
|
||||
'a_fonction5',
|
||||
'a_date_fonction6',
|
||||
'a_fonction6',
|
||||
'a_date_fonction7',
|
||||
'a_fonction7',
|
||||
'a_date_fonction8',
|
||||
'a_fonction8',
|
||||
'a_date_fonction9',
|
||||
'a_fonction9',
|
||||
'a_date_mariage',
|
||||
'a_situation_fam',
|
||||
'a_sexe',
|
||||
'a_date_fud',
|
||||
'a_fud',
|
||||
Cols.DATE_STATUT_CONCERTO,
|
||||
Cols.STATUT_CONCERTO,
|
||||
Cols.DATE_STATUT_CONCERTO_FUTUR,
|
||||
Cols.STATUT_CONCERTO_FUTUR,
|
||||
'a_domaine_poste',
|
||||
'a_filiere_poste',
|
||||
'a_nf_poste',
|
||||
'a_lien_service',
|
||||
'a_marqueur_pn',
|
||||
'a_pls_gb_max',
|
||||
'a_enfants',
|
||||
)
|
||||
|
||||
administres_df = pd.DataFrame.from_records(administres)
|
||||
|
||||
adm_bo_cols = BOCols.columns(
|
||||
BOCols.ID_SAP, # A
|
||||
BOCols.GRADE, # B
|
||||
BOCols.NOM, # C
|
||||
BOCols.PRENOM, # D
|
||||
BOCols.ID_DEF, # E
|
||||
BOCols.EIP, # F
|
||||
BOCols.EIS, # G
|
||||
BOCols.DOMAINE_GESTION, # H
|
||||
BOCols.DOMAINE, # I
|
||||
BOCols.FILIERE, # J
|
||||
BOCols.NF, # K
|
||||
BOCols.ARME, # L
|
||||
BOCols.REGROUPEMENT_ORIGINE_RECRUTEMENT, # M
|
||||
BOCols.DATE_ENTREE_SERVICE, # N
|
||||
BOCols.NOMBRE_ENFANTS, # O
|
||||
BOCols.ORIGINE_RECRUTEMENT, # Q
|
||||
BOCols.DATE_NAISSANCE, # R
|
||||
BOCols.FONCTION, # T
|
||||
BOCols.DIPLOME_PLUS_HAUT_NIVEAU, # U
|
||||
BOCols.DERNIER_DIPLOME, # V
|
||||
BOCols.DATE_RDC, # W
|
||||
BOCols.CREDO_FE, # X
|
||||
BOCols.GARNISON, # Z (utilisé dans la table des garnisons)
|
||||
BOCols.DATE_ARRIVEE_FE, # AA
|
||||
BOCols.DATE_DERNIER_ACR, # AC
|
||||
BOCols.POSITION_STATUTAIRE, # AE
|
||||
BOCols.DATE_POSITION_STATUAIRE, # AF
|
||||
BOCols.INTERRUPTION_SERVICE, # AG
|
||||
BOCols.DATE_DEBUT_GRADE, # AH
|
||||
BOCols.PROFESSION_CONJOINT, # AL
|
||||
BOCols.ID_SAP_CONJOINT, # AM
|
||||
BOCols.ID_DEF_CONJOINT, # AN
|
||||
BOCols.SEXE_CONJOINT, # AT
|
||||
BOCols.DATE_FONCTION_1, # BM
|
||||
BOCols.FONCTION_1, # BN
|
||||
BOCols.DATE_FONCTION_2, # BO
|
||||
BOCols.FONCTION_2, # BP
|
||||
BOCols.DATE_FONCTION_3, # BQ
|
||||
BOCols.FONCTION_3, # BR
|
||||
BOCols.DATE_FONCTION_4, # BS
|
||||
BOCols.FONCTION_4, # BT
|
||||
BOCols.DATE_FONCTION_5, # BU
|
||||
BOCols.FONCTION_5, # BV
|
||||
BOCols.DATE_FONCTION_6, # BW
|
||||
BOCols.FONCTION_6, # BX
|
||||
BOCols.DATE_FONCTION_7, # BY
|
||||
BOCols.FONCTION_7, # BZ
|
||||
BOCols.DATE_FONCTION_8, # CA
|
||||
BOCols.FONCTION_8, # CB
|
||||
BOCols.DATE_FONCTION_9, # CC
|
||||
BOCols.FONCTION_9, # CD
|
||||
BOCols.DATE_MARIAGE, # CH
|
||||
BOCols.SITUATION_FAMILIALE, # CI
|
||||
BOCols.SEXE, # CJ
|
||||
BOCols.DATE_FUD, # DV
|
||||
BOCols.FUD, # DW
|
||||
BOCols.DATE_STATUT_CONCERTO, # DX
|
||||
BOCols.STATUT_CONCERTO, # DY
|
||||
BOCols.DATE_STATUT_CONCERTO_FUTUR, # DZ
|
||||
BOCols.STATUT_CONCERTO_FUTUR, # EA
|
||||
BOCols.DOMAINE_POSTE, # ED (non utilisé)
|
||||
BOCols.FILIERE_POSTE, # EE (non utilisé)
|
||||
BOCols.NF_POSTE, # EF (non utilisé)
|
||||
BOCols.DATE_LIEN_SERVICE, # EG
|
||||
BOCols.MARQUEUR_PN, # EH
|
||||
BOCols.PLS_GB_MAX, # EI
|
||||
BOCols.ENFANTS, # EK
|
||||
)
|
||||
|
||||
administres_df.columns = adm_bo_cols
|
||||
|
||||
# Changement des types des colonnes dates
|
||||
administres_df[BOCols.DATE_ARRIVEE_FE] = pd.to_datetime(administres_df[BOCols.DATE_ARRIVEE_FE], errors='coerce').dt.strftime('%d/%m/%Y') # AA
|
||||
administres_df[BOCols.DATE_DEBUT_GRADE] = pd.to_datetime(administres_df[BOCols.DATE_DEBUT_GRADE], errors='coerce').dt.strftime('%d/%m/%Y') # AH
|
||||
administres_df[BOCols.DATE_DERNIER_ACR] = pd.to_datetime(administres_df[BOCols.DATE_DERNIER_ACR], errors='coerce').dt.strftime('%d/%m/%Y') # AC
|
||||
administres_df[BOCols.DATE_ENTREE_SERVICE] = pd.to_datetime(administres_df[BOCols.DATE_ENTREE_SERVICE], errors='coerce').dt.strftime('%d/%m/%Y') # N
|
||||
administres_df[BOCols.DATE_FUD] = pd.to_datetime(administres_df[BOCols.DATE_FUD], errors='coerce').dt.strftime('%d/%m/%Y') # DV
|
||||
administres_df[BOCols.DATE_LIEN_SERVICE] = pd.to_datetime(administres_df[BOCols.DATE_LIEN_SERVICE], errors='coerce').dt.strftime('%d/%m/%Y') # EG
|
||||
administres_df[BOCols.DATE_NAISSANCE] = pd.to_datetime(administres_df[BOCols.DATE_NAISSANCE], errors='coerce').dt.strftime('%d/%m/%Y') # R
|
||||
administres_df[BOCols.DATE_POSITION_STATUAIRE] = pd.to_datetime(administres_df[BOCols.DATE_POSITION_STATUAIRE], errors='coerce').dt.strftime('%d/%m/%Y') # AF
|
||||
administres_df[BOCols.DATE_RDC] = pd.to_datetime(administres_df[BOCols.DATE_RDC], errors='coerce').dt.strftime('%d/%m/%Y') # W
|
||||
administres_df[BOCols.DATE_STATUT_CONCERTO] = pd.to_datetime(administres_df[BOCols.DATE_STATUT_CONCERTO], errors='coerce').dt.strftime('%d/%m/%Y') # DX
|
||||
administres_df[BOCols.DATE_STATUT_CONCERTO_FUTUR] = pd.to_datetime(administres_df[BOCols.DATE_STATUT_CONCERTO_FUTUR], errors='coerce').dt.strftime('%d/%m/%Y') # DZ
|
||||
administres_df[BOCols.DATE_MARIAGE] = pd.to_datetime(administres_df[BOCols.DATE_MARIAGE], errors='coerce').dt.strftime('%d/%m/%Y') # CH
|
||||
for i in range(1, 10):
|
||||
administres_df[f'Fonction -{i} DD'] = pd.to_datetime(administres_df[f'Fonction -{i} DD']).dt.strftime('%d/%m/%Y')
|
||||
|
||||
# Changement des types des colonnes bool
|
||||
administres_df[[BOCols.MARQUEUR_PN]] = administres_df[[BOCols.MARQUEUR_PN]].replace([True, False], ['X', None])
|
||||
|
||||
# CHangement des types des colonnes int
|
||||
administres_df[BOCols.ID_SAP_CONJOINT] = administres_df[BOCols.ID_SAP_CONJOINT].fillna(0).astype(int).replace({0: None})
|
||||
administres_df[BOCols.PLS_GB_MAX] = administres_df[BOCols.PLS_GB_MAX].fillna(0).astype(int).replace({0: None})
|
||||
|
||||
administres_df = (administres_df.fillna(APP_NAN)
|
||||
.replace({APP_NAN: None})
|
||||
.replace({np.nan: None})
|
||||
.replace({'nan': None}))
|
||||
|
||||
self.logger.info("Table des administrés lue et traitée en %d secondes : %s lignes et %s colonnes extraites\n", time.time()-start_time_adm, administres_df.shape[0], administres_df.shape[1])
|
||||
return administres_df
|
||||
|
||||
|
||||
def export_aff_bo_df(self):
|
||||
self.logger.info("Lecture et traitement de la table des affectations... ")
|
||||
start_time_aff = time.time()
|
||||
|
||||
aff = pd.DataFrame.from_records(Affectation.objects.all().values()).sort_values('affect_date', ascending=False)
|
||||
|
||||
aff_grouped = aff.groupby(['administre_id']).agg(
|
||||
affect_libelle=('affect_libelle', ',,,'.join),
|
||||
affect_date=('affect_date', ',,,'.join),
|
||||
)
|
||||
|
||||
aff_date_cols = [f'Affectation -{i} DD' for i in range(1,10)]
|
||||
aff_libelle_cols = [f'Affectation -{i} L' for i in range(1,10)]
|
||||
|
||||
aff_date_df = aff_grouped['affect_date'].str.split(',,,', expand=True)
|
||||
aff_libelle_df = aff_grouped['affect_libelle'].str.split(',,,', expand=True)
|
||||
|
||||
nb_cols_date = aff_date_df.shape[1]
|
||||
nb_cols_libelle = aff_date_df.shape[1]
|
||||
|
||||
aff_date_cols_tronc = aff_date_cols[:nb_cols_date]
|
||||
aff_libelle_cols_tronc = aff_libelle_cols[:nb_cols_libelle]
|
||||
|
||||
aff_grouped[aff_date_cols_tronc] = aff_date_df
|
||||
aff_grouped[aff_libelle_cols_tronc] = aff_libelle_df
|
||||
|
||||
aff_grouped = (aff_grouped.drop(['affect_libelle', 'affect_date'], axis=1)
|
||||
.reset_index())
|
||||
|
||||
# Changement des types des colonnes dates
|
||||
for i in range(1, 10):
|
||||
aff_grouped[f'Affectation -{i} DD'] = pd.to_datetime(aff_grouped[f'Affectation -{i} DD']).dt.strftime('%d/%m/%Y')
|
||||
|
||||
aff_grouped = (aff_grouped.fillna(APP_NAN)
|
||||
.replace({APP_NAN: None})
|
||||
.replace({np.nan: None})
|
||||
.replace({'nan': None}))
|
||||
|
||||
self.logger.info("Table des affectations lue et traitée en %d secondes : %s lignes et %s colonnes extraites\n", time.time()-start_time_aff, aff_grouped.shape[0], aff_grouped.shape[1])
|
||||
return aff_grouped
|
||||
|
||||
|
||||
def export_no_bo_df(self):
|
||||
self.logger.info("Lecture et traitement de la table des notations... ")
|
||||
start_time_no = time.time()
|
||||
|
||||
no = pd.DataFrame.from_records(Administre_Notation.objects.all().values()).sort_values('no_age_annees', ascending=False)
|
||||
|
||||
no_grouped = no.groupby(["administre_id"]).agg(
|
||||
no_annne_de_notation=("no_annne_de_notation", ",,,".join),
|
||||
no_nr_ou_iris=("no_nr_ou_iris", ",,,".join),
|
||||
no_rac_ou_iris_cumule=("no_rac_ou_iris_cumule", ",,,".join),
|
||||
no_rf_qsr=("no_rf_qsr", ",,,".join),
|
||||
no_aptitude_emploie_sup=("no_aptitude_emploie_sup", ",,,".join),
|
||||
no_potentiel_responsabilite_sup=("no_potentiel_responsabilite_sup", ",,,".join),
|
||||
no_age_annees=("no_age_annees", ",,,".join),
|
||||
)
|
||||
|
||||
no_age_annees_cols = ['Age en années (au 31/12)']
|
||||
no_annne_de_notation_cols = ['Année notation A'] + [f'Année notation A-{i}' for i in range(1,6)]
|
||||
no_nr_ou_iris_cols = ['IRIS / RAC retenu A'] + [f'IRIS / RAC retenu A-{i}' for i in range(1,6)]
|
||||
no_rac_ou_iris_cumule_cols = ['NR/NGC cumulé A'] + [f'NR/NGC cumulé A-{i}' for i in range(1,6)]
|
||||
no_rf_qsr_cols = ['QSR A'] + [f'QSR A-{i}' for i in range(1,6)]
|
||||
no_aptitude_emploie_sup_cols = ['Apt resp / Emp sup A'] + [f'Apt resp / Emp sup A-{i}' for i in range(1,6)]
|
||||
no_potentiel_responsabilite_sup_cols = ['Potentiel responsabilités catégorie sup A'] + [f'Potentiel responsabilités catégorie sup A-{i}' for i in range(1,6)]
|
||||
|
||||
no_grouped[no_age_annees_cols] = no_grouped['no_age_annees'].str.split(',,,', expand=True)[0]
|
||||
no_grouped[no_annne_de_notation_cols] = no_grouped['no_annne_de_notation'].str.split(',,,', expand=True)
|
||||
no_grouped[no_nr_ou_iris_cols] = no_grouped['no_nr_ou_iris'].str.split(',,,', expand=True)
|
||||
no_grouped[no_rac_ou_iris_cumule_cols] = no_grouped['no_rac_ou_iris_cumule'].str.split(',,,', expand=True)
|
||||
no_grouped[no_rf_qsr_cols] = no_grouped['no_rf_qsr'].str.split(',,,', expand=True)
|
||||
no_grouped[no_aptitude_emploie_sup_cols] = no_grouped['no_aptitude_emploie_sup'].str.split(',,,', expand=True)
|
||||
no_grouped[no_potentiel_responsabilite_sup_cols] = no_grouped['no_potentiel_responsabilite_sup'].str.split(',,,', expand=True)
|
||||
|
||||
no_grouped = (no_grouped.drop(['no_annne_de_notation',
|
||||
'no_nr_ou_iris',
|
||||
'no_rac_ou_iris_cumule',
|
||||
'no_rf_qsr',
|
||||
'no_aptitude_emploie_sup',
|
||||
'no_potentiel_responsabilite_sup',
|
||||
'no_age_annees'], axis=1)
|
||||
.reset_index())
|
||||
|
||||
# Changement des types des colonnes int
|
||||
no_grouped[f'Année notation A'] = no_grouped[f'Année notation A'].astype(float).fillna(0).astype(int).replace({0: None})
|
||||
no_grouped[f'IRIS / RAC retenu A'] = no_grouped[f'IRIS / RAC retenu A'].astype(float).fillna(0).astype(int).replace({0: None})
|
||||
no_grouped[f'NR/NGC cumulé A'] = no_grouped[f'NR/NGC cumulé A'].astype(float).fillna(0).astype(int).replace({0: None})
|
||||
for i in range(1, 6):
|
||||
no_grouped[f'Année notation A-{i}'] = no_grouped[f'Année notation A-{i}'].astype(float).fillna(0).astype(int).replace({0: None})
|
||||
no_grouped[f'IRIS / RAC retenu A-{i}'] = no_grouped[f'IRIS / RAC retenu A-{i}'].astype(float).fillna(0).astype(int).replace({0: None})
|
||||
no_grouped[f'NR/NGC cumulé A-{i}'] = no_grouped[f'NR/NGC cumulé A-{i}'].astype(float).fillna(0).astype(int).replace({0: None})
|
||||
|
||||
no_grouped = (no_grouped.fillna(APP_NAN)
|
||||
.replace({APP_NAN: None})
|
||||
.replace({np.nan: None})
|
||||
.replace({'nan': None}))
|
||||
|
||||
self.logger.info("Table des notations lue et traitée en %d secondes : %s lignes et %s colonnes extraites\n", time.time()-start_time_no, no_grouped.shape[0], no_grouped.shape[1])
|
||||
return no_grouped
|
||||
|
||||
|
||||
def export_pos_reo_df(self):
|
||||
self.logger.info("Lecture et traitement de la table des postes... ")
|
||||
start_time_pos = time.time()
|
||||
|
||||
Cols = Poste.Cols
|
||||
postes = Poste.objects.all().values_list(
|
||||
'p_annee',
|
||||
Cols.REL_FORMATION_EMPLOI,
|
||||
'p_dep',
|
||||
Cols.CATEGORIE,
|
||||
'p_eip',
|
||||
Cols.REL_DOMAINE,
|
||||
Cols.REL_FILIERE,
|
||||
Cols.NIVEAU_FONCTIONNEL,
|
||||
Cols.PK,
|
||||
f'{Cols.REL_FONCTION}__fon_id',
|
||||
f'{Cols.REL_FONCTION}__fon_libelle',
|
||||
'p_nfs',
|
||||
)
|
||||
|
||||
postes_df = pd.DataFrame.from_records(postes)
|
||||
|
||||
pos_reo_cols = ReoCols.columns(
|
||||
ReoCols.ANNEE_PROJET, # B
|
||||
ReoCols.FORMATION_EMPLOI, # F
|
||||
ReoCols.CODE_POSTAL, # J
|
||||
ReoCols.CATEGORIE, # P
|
||||
ReoCols.EIP, # T
|
||||
ReoCols.DOMAINE, # U
|
||||
ReoCols.FILIERE, # V
|
||||
ReoCols.CODE_NF, # W
|
||||
ReoCols.ID_POSTE, # X
|
||||
ReoCols.FONCTION_ID, # Y
|
||||
ReoCols.FONCTION_LIBELLE, # Z
|
||||
ReoCols.DOMAINE_GESTION, # AA
|
||||
)
|
||||
|
||||
postes_df.columns = pos_reo_cols
|
||||
|
||||
postes_df = (postes_df.fillna(APP_NAN)
|
||||
.replace({APP_NAN: None})
|
||||
.replace({np.nan: None})
|
||||
.replace({'nan': None}))
|
||||
|
||||
self.logger.info("Table des postes lue et traitée en %d secondes : %s lignes et %s colonnes extraites\n", time.time()-start_time_pos, postes_df.shape[0], postes_df.shape[1])
|
||||
return postes_df
|
||||
|
||||
|
||||
@atomic
|
||||
# @execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def export_bo(self, request):
|
||||
self.logger.info("--------- Début de l'exportation du fichier 'Données BO' --------- ")
|
||||
start_time_export = time.time()
|
||||
|
||||
administres_df = self.export_adm_bo_df()
|
||||
affectations_df = self.export_aff_bo_df()
|
||||
notations_df = self.export_no_bo_df()
|
||||
|
||||
bo_df = administres_df.merge(affectations_df, how='left', left_on=BOCols.ID_SAP, right_on='administre_id')
|
||||
bo_df = bo_df.drop(['administre_id'], axis=1)
|
||||
bo_df = bo_df.merge(notations_df, how='left', left_on=BOCols.ID_SAP, right_on='administre_id')
|
||||
bo_df = bo_df.drop(['administre_id'], axis=1)
|
||||
|
||||
bo_cols = BOCols.columns(
|
||||
BOCols.ID_SAP, # A
|
||||
BOCols.GRADE, # B
|
||||
BOCols.NOM, # C
|
||||
BOCols.PRENOM, # D
|
||||
BOCols.ID_DEF, # E
|
||||
BOCols.EIP, # F
|
||||
BOCols.EIS, # G
|
||||
BOCols.DOMAINE_GESTION, # H
|
||||
BOCols.DOMAINE, # I
|
||||
BOCols.FILIERE, # J
|
||||
BOCols.NF, # K
|
||||
BOCols.ARME, # L
|
||||
BOCols.REGROUPEMENT_ORIGINE_RECRUTEMENT, # M
|
||||
BOCols.DATE_ENTREE_SERVICE, # N
|
||||
BOCols.NOMBRE_ENFANTS, # O
|
||||
BOCols.ORIGINE_RECRUTEMENT, # Q
|
||||
BOCols.DATE_NAISSANCE, # R
|
||||
BOCols.FONCTION, # T
|
||||
BOCols.DIPLOME_PLUS_HAUT_NIVEAU, # U
|
||||
BOCols.DERNIER_DIPLOME, # V
|
||||
BOCols.DATE_RDC, # W
|
||||
BOCols.CREDO_FE, # X
|
||||
BOCols.GARNISON, # Z (utilisé dans la table des garnisons)
|
||||
BOCols.DATE_ARRIVEE_FE, # AA
|
||||
BOCols.DATE_DERNIER_ACR, # AC
|
||||
BOCols.POSITION_STATUTAIRE, # AE
|
||||
BOCols.DATE_POSITION_STATUAIRE, # AF
|
||||
BOCols.INTERRUPTION_SERVICE, # AG
|
||||
BOCols.DATE_DEBUT_GRADE, # AH
|
||||
BOCols.PROFESSION_CONJOINT, # AL
|
||||
BOCols.ID_SAP_CONJOINT, # AM
|
||||
BOCols.ID_DEF_CONJOINT, # AN
|
||||
BOCols.SEXE_CONJOINT, # AT
|
||||
BOCols.DATE_AFFECTATION_1, # AU
|
||||
BOCols.AFFECTATION_1, # AV
|
||||
BOCols.DATE_AFFECTATION_2, # AW
|
||||
BOCols.AFFECTATION_2, # AX
|
||||
BOCols.DATE_AFFECTATION_3, # AY
|
||||
BOCols.AFFECTATION_3, # AZ
|
||||
BOCols.DATE_AFFECTATION_4, # BA
|
||||
BOCols.AFFECTATION_4, # BB
|
||||
BOCols.DATE_AFFECTATION_5, # BC
|
||||
BOCols.AFFECTATION_5, # BD
|
||||
BOCols.DATE_AFFECTATION_6, # BE
|
||||
BOCols.AFFECTATION_6, # BF
|
||||
BOCols.DATE_AFFECTATION_7, # BG
|
||||
BOCols.AFFECTATION_7, # BH
|
||||
BOCols.DATE_AFFECTATION_8, # BI
|
||||
BOCols.AFFECTATION_8, # BJ
|
||||
BOCols.DATE_AFFECTATION_9, # BK
|
||||
BOCols.AFFECTATION_9, # BL
|
||||
BOCols.DATE_FONCTION_1, # BM
|
||||
BOCols.FONCTION_1, # BN
|
||||
BOCols.DATE_FONCTION_2, # BO
|
||||
BOCols.FONCTION_2, # BP
|
||||
BOCols.DATE_FONCTION_3, # BQ
|
||||
BOCols.FONCTION_3, # BR
|
||||
BOCols.DATE_FONCTION_4, # BS
|
||||
BOCols.FONCTION_4, # BT
|
||||
BOCols.DATE_FONCTION_5, # BU
|
||||
BOCols.FONCTION_5, # BV
|
||||
BOCols.DATE_FONCTION_6, # BW
|
||||
BOCols.FONCTION_6, # BX
|
||||
BOCols.DATE_FONCTION_7, # BY
|
||||
BOCols.FONCTION_7, # BZ
|
||||
BOCols.DATE_FONCTION_8, # CA
|
||||
BOCols.FONCTION_8, # CB
|
||||
BOCols.DATE_FONCTION_9, # CC
|
||||
BOCols.FONCTION_9, # CD
|
||||
BOCols.AGE_ANNEES, # CG
|
||||
BOCols.DATE_MARIAGE, # CH
|
||||
BOCols.SITUATION_FAMILIALE, # CI
|
||||
BOCols.SEXE, # CJ
|
||||
BOCols.ANNEE_NOTATION, # CL
|
||||
BOCols.RAC_OU_IRIS_CUMULE, # CM
|
||||
BOCols.NR_OU_IRIS, # CN
|
||||
BOCols.RF_QSR, # CO
|
||||
BOCols.APTITUDE_EMPLOI_SUP, # CP
|
||||
BOCols.POTENTIEL_RESPONSABILITE_SUP, # CQ
|
||||
BOCols.ANNEE_NOTATION_1, # CR
|
||||
BOCols.RAC_OU_IRIS_CUMULE_1, # CS
|
||||
BOCols.NR_OU_IRIS_1, # CT
|
||||
BOCols.RF_QSR_1, # CU
|
||||
BOCols.APTITUDE_EMPLOI_SUP_1, # CV
|
||||
BOCols.POTENTIEL_RESPONSABILITE_SUP_1, # CW
|
||||
BOCols.ANNEE_NOTATION_2, # CX
|
||||
BOCols.RAC_OU_IRIS_CUMULE_2, # CY
|
||||
BOCols.NR_OU_IRIS_2, # CZ
|
||||
BOCols.RF_QSR_2, # DA
|
||||
BOCols.APTITUDE_EMPLOI_SUP_2, # DB
|
||||
BOCols.POTENTIEL_RESPONSABILITE_SUP_2, # DC
|
||||
BOCols.ANNEE_NOTATION_3, # DD
|
||||
BOCols.RAC_OU_IRIS_CUMULE_3, # DE
|
||||
BOCols.NR_OU_IRIS_3, # DF
|
||||
BOCols.RF_QSR_3, # DG
|
||||
BOCols.APTITUDE_EMPLOI_SUP_3, # DH
|
||||
BOCols.POTENTIEL_RESPONSABILITE_SUP_3, # DI
|
||||
BOCols.ANNEE_NOTATION_4, # DJ
|
||||
BOCols.RAC_OU_IRIS_CUMULE_4, # DK
|
||||
BOCols.NR_OU_IRIS_4, # DL
|
||||
BOCols.RF_QSR_4, # DM
|
||||
BOCols.APTITUDE_EMPLOI_SUP_4, # DN
|
||||
BOCols.POTENTIEL_RESPONSABILITE_SUP_4, # DO
|
||||
BOCols.ANNEE_NOTATION_5, # DP
|
||||
BOCols.RAC_OU_IRIS_CUMULE_5, # DQ
|
||||
BOCols.NR_OU_IRIS_5, # DR
|
||||
BOCols.RF_QSR_5, # DS
|
||||
BOCols.APTITUDE_EMPLOI_SUP_5, # DT
|
||||
BOCols.POTENTIEL_RESPONSABILITE_SUP_5, # DU
|
||||
BOCols.DATE_FUD, # DV
|
||||
BOCols.FUD, # DW
|
||||
BOCols.DATE_STATUT_CONCERTO, # DX
|
||||
BOCols.STATUT_CONCERTO, # DY
|
||||
BOCols.DATE_STATUT_CONCERTO_FUTUR, # DZ
|
||||
BOCols.STATUT_CONCERTO_FUTUR, # EA
|
||||
BOCols.DOMAINE_POSTE, # ED (non utilisé)
|
||||
BOCols.FILIERE_POSTE, # EE (non utilisé)
|
||||
BOCols.NF_POSTE, # EF (non utilisé)
|
||||
BOCols.DATE_LIEN_SERVICE, # EG
|
||||
BOCols.MARQUEUR_PN, # EH
|
||||
BOCols.PLS_GB_MAX, # EI
|
||||
BOCols.ENFANTS, # EK
|
||||
)
|
||||
|
||||
bo_df = bo_df.reindex(columns=bo_cols)
|
||||
|
||||
if bo_df.empty:
|
||||
return Response("Aucune donnée à extraire.")
|
||||
else:
|
||||
try:
|
||||
self.logger.info("Exportation du fichier 'Données BO'...")
|
||||
start_time_export_bo = time.time()
|
||||
|
||||
now = timezone.now().date()
|
||||
bo_df.to_excel(f'..\\fichiers_exportes\\{now}_donnees_BO_export.xlsx', index=False, header=True)
|
||||
bo_obj = FichiersExporte(nom_fichier=f'{now}_donnees_BO_export.xlsx')
|
||||
bo_obj.save()
|
||||
|
||||
self.logger.info("Exportation du fichier 'Données BO' réalisée en %d secondes : %s lignes et %s colonnes exportées", time.time()-start_time_export_bo, bo_df.shape[0], bo_df.shape[1])
|
||||
self.logger.info("---- Exportation totale réalisée en : %d minutes et %d secondes ----\n", (time.time()-start_time_export)//60, (time.time()-start_time_export)%60)
|
||||
return Response("Exportation terminée. Vous trouverez le fichier 'Données BO' exporté dans le dossier 'fichiers_exportes'.")
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Imposible de réaliser l'exportation du fichier 'Données BO'. Vérifiez que les fichiers Excel d'exportation sont bien fermés."
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
|
||||
|
||||
@atomic
|
||||
# @execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def export_reo(self, request):
|
||||
self.logger.info("--------- Début de l'exportation du fichier 'REO' --------- ")
|
||||
start_time_export = time.time()
|
||||
|
||||
postes_df = self.export_pos_reo_df()
|
||||
reo_df = postes_df
|
||||
|
||||
if reo_df.empty:
|
||||
return Response("Aucune donnée à extraire.")
|
||||
else:
|
||||
try:
|
||||
self.logger.info("Exportation du fichier 'REO'...")
|
||||
start_time_export_reo = time.time()
|
||||
|
||||
now = timezone.now().date()
|
||||
reo_df.to_excel(f'..\\fichiers_exportes\\{now}_REO_export.xlsx', index=False, header=True)
|
||||
reo_obj = FichiersExporte(nom_fichier=f'{now}_REO_export.xlsx')
|
||||
reo_obj.save()
|
||||
|
||||
self.logger.info("Exportation du fichier 'REO' réalisée en %d secondes : %s lignes et %s colonnes exportées", time.time()-start_time_export_reo, reo_df.shape[0], reo_df.shape[1])
|
||||
self.logger.info("------- Exportation totale réalisée en : %d secondes ------\n", time.time()-start_time_export)
|
||||
return Response("Exportation terminée. Vous trouverez le fichier 'REO' exporté dans le dossier 'fichiers_exportes'.")
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Imposible de réaliser l'exportation du fichier 'REO'. Vérifiez que les fichiers Excel d'exportation sont bien fermés."
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
nom_fichier = serializer.validated_data.get('fichier_exporte')
|
||||
|
||||
if nom_fichier == '1':
|
||||
resp = self.export_bo(request)
|
||||
return resp
|
||||
if nom_fichier == '2':
|
||||
resp = self.export_reo(request)
|
||||
return resp
|
||||
return Response("Imposible de réaliser l'exportation.")
|
||||
|
||||
|
||||
110
backend-django/backend/views/fiche_detaillee.py
Normal file
110
backend-django/backend/views/fiche_detaillee.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from django.forms import model_to_dict
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..models import FMOB, Administre, FormationEmploi
|
||||
from ..utils.decorators import class_logger
|
||||
from .commun import (GestionnairePermission, execution_time_viewset,
|
||||
query_count_viewset)
|
||||
|
||||
|
||||
@class_logger
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class FicheDetailleeView(APIView):
|
||||
"""
|
||||
Cette classe est dédiée au vue de la fiche détaillée des administrés
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, GestionnairePermission]
|
||||
|
||||
def get(self, request: Request) -> Response:
|
||||
"""La fonction get recupére les infos administrés
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'identifiant de l'administré
|
||||
|
||||
:return: - **Response** (*objet*): informations concernant l'administré, informations sur le formulaire de mobilité.
|
||||
"""
|
||||
try:
|
||||
res = {"result": "error"}
|
||||
if 'administre_id' in request.query_params:
|
||||
administre_id = request.query_params['administre_id']
|
||||
annee_pam = request.query_params['pam__in']
|
||||
administre_id_pam = request.query_params['administre_id'] + annee_pam
|
||||
administre = Administre.objects.get(a_id_sap=administre_id)
|
||||
res = model_to_dict(administre, fields=[field.name for field in administre._meta.fields if field != 'a_liste_id_competences'])
|
||||
res['a_liste_id_competences'] = ','.join(list(administre.a_liste_id_competences.values_list('comp_libelle', flat=True)))
|
||||
fmobs = FMOB.objects.filter(administre_id=administre_id_pam)
|
||||
res["fmob"] = None
|
||||
res['pam'] = None
|
||||
|
||||
if fmobs.exists():
|
||||
fmob_dict = model_to_dict(fmobs[0])
|
||||
res["fmob"] = fmob_dict
|
||||
|
||||
res["fe"] = None
|
||||
fe = list(FormationEmploi.objects.filter(fe_code=administre.formation_emploi_id).values(
|
||||
*('fe_code', 'fe_code_postal', 'fe_garnison_lieu', 'fe_libelle')))
|
||||
# if fe.exists():
|
||||
# fe = fe[0]
|
||||
# garnison_dict = model_to_dict()
|
||||
# res["garnison"] = garnison_dict
|
||||
if len(fe):
|
||||
res["fe"] = fe[0]
|
||||
res["conjoint"] = None
|
||||
conjoint = Administre.objects.filter(a_id_sap=administre.a_sap_conjoint)
|
||||
if conjoint.exists():
|
||||
self.logger.debug('has conjoint')
|
||||
conjoint_dict = model_to_dict(conjoint[0])
|
||||
conjoint_dict['pam'] = None
|
||||
conjoint_dict["fe"] = None
|
||||
fe_conjoint = list(FormationEmploi.objects.filter(fe_code=conjoint_dict['formation_emploi']).values(
|
||||
*('fe_code', 'fe_code_postal', 'fe_garnison_lieu', 'fe_libelle')))
|
||||
if len(fe):
|
||||
conjoint_dict["fe"] = fe_conjoint[0]
|
||||
|
||||
# Verifier si le conjoint est formobé
|
||||
conjoint_dict["fmob_O_N"] = "Oui" if FMOB.objects.filter(administre_id=str(conjoint_dict['a_id_sap']) + annee_pam).exists() else "Non"
|
||||
res["conjoint"] = conjoint_dict
|
||||
|
||||
adm_affect = list(administre.adm_affec.values())
|
||||
adm_affect_sorted = sorted(adm_affect, key=lambda d: d['affect_date'], reverse=True)
|
||||
for i in range(len(adm_affect_sorted)):
|
||||
res['a_affectation{}'.format(i + 1)] = adm_affect_sorted[i]['affect_libelle']
|
||||
res['a_date_affectation{}'.format(i + 1)] = adm_affect_sorted[i]['affect_date']
|
||||
|
||||
adm_dip = list(administre.adm_dip.values())
|
||||
adm_dip_sorted = sorted(adm_dip, key=lambda d: d['diplome_date'], reverse=True)
|
||||
for i in range(len(adm_dip)):
|
||||
res['a_diplome_{}'.format(i + 1)] = adm_dip_sorted[i]['diplome_libelle']
|
||||
res['a_diplome_{}_date'.format(i + 1)] = adm_dip_sorted[i]['diplome_date']
|
||||
res['a_diplome_{}_note'.format(i + 1)] = adm_dip_sorted[i]['diplome_note']
|
||||
|
||||
adm_fud = list(administre.adm_fud.values())
|
||||
adm_fud_sorted = sorted(adm_fud, key=lambda d: d['fud_date_debut'], reverse=True)
|
||||
for i in range(len(adm_fud)):
|
||||
res['a_fud_{}_dd'.format(i + 1)] = adm_fud_sorted[i]['fud_date_debut']
|
||||
res['a_fud_{}_df'.format(i + 1)] = adm_fud_sorted[i]['fud_date_fin']
|
||||
res['a_fud_{}_l'.format(i + 1)] = adm_fud_sorted[i]['fud_libelle']
|
||||
|
||||
adm_not = list(administre.adm_not.values())
|
||||
adm_not_sorted = sorted(adm_not, key=lambda d: d['no_age_annees'], reverse=False)
|
||||
for i in range(len(adm_not_sorted)):
|
||||
res['no_annne_de_notation_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_annne_de_notation']
|
||||
res['no_nr_ou_iris_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_nr_ou_iris']
|
||||
res['no_rac_ou_iris_cumule_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_rac_ou_iris_cumule']
|
||||
res['no_rf_qsr_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_rf_qsr']
|
||||
res['no_aptitude_emploie_sup_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_aptitude_emploie_sup']
|
||||
res['no_potentiel_responsabilite_sup_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_potentiel_responsabilite_sup']
|
||||
res['no_age_annees_A_{}'.format(i + 1)] = adm_not_sorted[i]['no_age_annees']
|
||||
return Response(res)
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except:
|
||||
message = "impossible d'afficher la vue détaillée"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
23
backend-django/backend/views/formation_emploi.py
Normal file
23
backend-django/backend/views/formation_emploi.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from ..models import FormationEmploi
|
||||
from ..serializers.formation_emploi import FormationEmploiSerializer
|
||||
from .commun import (GestionnairePermission, execution_time_viewset,
|
||||
query_count_viewset)
|
||||
|
||||
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class FormationEmploiView(ModelViewSet):
|
||||
"""
|
||||
Cette classe est dédiée au vue des FormationEmplois.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, GestionnairePermission]
|
||||
serializer_class = FormationEmploiSerializer
|
||||
|
||||
# important : mettre à jour quand le serializer change
|
||||
def get_queryset(self):
|
||||
Cols = FormationEmploi.Cols
|
||||
return FormationEmploi.objects.select_related(Cols.REL_MERE)
|
||||
|
||||
1463
backend-django/backend/views/initial.py
Normal file
1463
backend-django/backend/views/initial.py
Normal file
File diff suppressed because it is too large
Load Diff
146
backend-django/backend/views/notation.py
Normal file
146
backend-django/backend/views/notation.py
Normal file
@@ -0,0 +1,146 @@
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db.models import Q
|
||||
from numpy import True_
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from ..models import Decision, DecisionChoices, Notation, Poste
|
||||
from ..paginations import HeavyDataPagination
|
||||
from ..serializers.notation import NotationSerializer
|
||||
from .commun import (GestionnairePermission, execution_time_viewset,
|
||||
query_count_viewset)
|
||||
|
||||
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class NotationView(ModelViewSet):
|
||||
"""
|
||||
Cette classe est dédiée au vue des notations.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, GestionnairePermission]
|
||||
serializer_class = NotationSerializer
|
||||
queryset = Notation.objects.all()
|
||||
pagination_class = HeavyDataPagination
|
||||
|
||||
def list(self, request: Request, pk=None) -> Response:
|
||||
"""La fonction list envoie le classement des postes pour un administré particulier et inversement.
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'administre ou le poste.
|
||||
|
||||
:return: - **JsonResponse** (*JsonResponse*): Json contenant le classement.
|
||||
"""
|
||||
notations_list = []
|
||||
counter = 10
|
||||
if 'administre_id' in request.query_params:
|
||||
q = 'poste_pam'
|
||||
q1 = 'poste'
|
||||
administre_id = request.query_params['administre_id']
|
||||
administres_keys = (
|
||||
'poste_pam__id',
|
||||
'poste_pam__poste__p_id',
|
||||
'poste_pam__poste__p_nf',
|
||||
'poste_pam__poste__p_domaine',
|
||||
'poste_pam__poste__p_filiere',
|
||||
'poste_pam__poste__p_eip',
|
||||
'poste_pam__poste__formation_emploi__fe_code',
|
||||
'poste_pam__poste__p_notes_gestionnaire',
|
||||
'poste_pam__poste__p_liste_id_marques',
|
||||
'poste_pam__poste__p_code_fonction',
|
||||
'poste_pam__p_avis_pam',
|
||||
'poste_pam__poste__p_dep',
|
||||
'poste_pam__poste__formation_emploi__fe_libelle',
|
||||
'poste_pam__poste__p_fonction',
|
||||
'poste_pam__poste__formation_emploi__fe_garnison_lieu',
|
||||
'poste_pam__poste__formation_emploi__fe_code_postal',
|
||||
'no_score_administre',
|
||||
'no_flag_cple_ideal')
|
||||
|
||||
notation_qs = (Notation.objects.filter(Q(administre_pam_id=administre_id) & Q(poste_pam__decisions__isnull=True))
|
||||
.order_by('-no_score_administre')
|
||||
.select_related('poste_pams'))
|
||||
|
||||
notation_qs_matching_parfait = (Notation.objects.filter(administre_pam_id=administre_id,
|
||||
no_flag_cple_ideal=True,
|
||||
poste_pam__decisions__isnull=True)
|
||||
.select_related('poste_pams'))
|
||||
|
||||
notation_qs.union(notation_qs_matching_parfait)
|
||||
notation_qs = notation_qs | notation_qs_matching_parfait
|
||||
notations_list = list(notation_qs.values(*administres_keys))
|
||||
|
||||
for notation in notations_list:
|
||||
poste_id = notation['poste_pam__id']
|
||||
topCounter = 0
|
||||
allNotationsInvolved = Notation.objects.filter(poste_pam_id=poste_id)
|
||||
for note in allNotationsInvolved:
|
||||
topList = list(
|
||||
Notation.objects.filter(no_id=note.no_id).order_by('-no_score_administre').values('poste_pam_id'))
|
||||
topPostes = [poste['poste_pam_id'] for poste in topList]
|
||||
if poste_id in topPostes:
|
||||
topCounter += 1
|
||||
|
||||
notation['poste__nb_top'] = topCounter
|
||||
|
||||
if 'poste_id' in request.query_params:
|
||||
q = "administre_pam"
|
||||
q1 = 'administre'
|
||||
poste_id = request.query_params['poste_id']
|
||||
postes_keys = (
|
||||
'administre_pam__id',
|
||||
'administre_pam__administre__a_id_sap',
|
||||
'administre_pam__administre__a_nom',
|
||||
'administre_pam__administre__a_prenom',
|
||||
'administre_pam__a_statut_pam_annee',
|
||||
'administre_pam__administre__grade_id',
|
||||
'administre_pam__administre__a_liste_id_marques',
|
||||
'administre_pam__decision__de_decision',
|
||||
'administre_pam__decision__de_date_decision',
|
||||
'no_score_administre',
|
||||
'no_flag_cple_ideal',
|
||||
'administre_pam__notes_pam',
|
||||
'administre_pam__administre__a_notes_gestionnaire',
|
||||
'administre_pam__administre__a_fonction',
|
||||
'administre_pam__administre__a_code_fonction',
|
||||
'administre_pam__administre__a_liste_id_marques',
|
||||
'administre_pam__decision__poste_id')
|
||||
|
||||
notation_qs = (Notation.objects.filter(Q(poste_pam_id=poste_id) & Q(administre_pam__decision__isnull=True))
|
||||
.order_by('-no_score_administre')
|
||||
.select_related('administre_pam'))
|
||||
notation_qs_matching_parfait = (Notation.objects.filter(poste_pam_id=poste_id,
|
||||
no_flag_cple_ideal=True,
|
||||
administre_pam__decision__isnull=True)
|
||||
.select_related('administre_pam'))
|
||||
notation_qs.union(notation_qs_matching_parfait)
|
||||
notations_list = list(
|
||||
notation_qs.values(*postes_keys))
|
||||
|
||||
for notation in notations_list:
|
||||
administre_id = notation['administre_pam__id']
|
||||
topCounter = 0
|
||||
allNotationsInvolved = Notation.objects.filter(administre_pam_id=administre_id)
|
||||
for note in allNotationsInvolved:
|
||||
topList = list(Notation.objects.filter(no_id=note.no_id)
|
||||
.order_by('-no_score_administre')
|
||||
.values('administre_pam_id'))
|
||||
topAdministres = [administre['administre_pam_id'] for administre in topList]
|
||||
if administre_id in topAdministres:
|
||||
topCounter += 1
|
||||
|
||||
notation['administre__nb_top'] = topCounter
|
||||
|
||||
result = []
|
||||
for notation in notations_list:
|
||||
res_notation = {q1: {}}
|
||||
for key in notation:
|
||||
if (q + "__") in key:
|
||||
res_notation[q1][key.replace(q + '__', '').replace(q1 + '__', '')] = notation[key]
|
||||
else:
|
||||
res_notation[key] = notation[key]
|
||||
result.append(res_notation)
|
||||
|
||||
|
||||
return Response(result)
|
||||
71
backend-django/backend/views/references.py
Normal file
71
backend-django/backend/views/references.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from django.db.models import Q
|
||||
|
||||
from ..models import AvisPosteChoices as AvisPoste
|
||||
from ..models import (Competence, Domaine, Filiere, FormationEmploi, Grade,
|
||||
SousVivier, PAM,
|
||||
Marque, MarquesGroupe, RefSvFil, SpecifiqueChoices)
|
||||
from ..models import StatutFuturChoices as StatutFutur
|
||||
from ..models import StatutPamChoices as StatutPam
|
||||
from ..models import ZoneGeographique
|
||||
from ..models import Fonction
|
||||
from ..serializers import (ChoicesSerializer, RefAvisPosteChoicesSerializer,
|
||||
RefStatutPamChoicesSerializer)
|
||||
from ..serializers.commun import AssignmentState
|
||||
from ..utils.decisions import get_all_decisions
|
||||
from .commun import execution_time_viewset, query_count_viewset
|
||||
|
||||
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class ReferencesView(APIView):
|
||||
"""
|
||||
Cette classe est dédiée au vue de la reference.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
La fonction get envoie les données de référence
|
||||
|
||||
:return: réponse contenant les données de référence
|
||||
"""
|
||||
|
||||
groupesMarques = [obj.as_dict() for obj in MarquesGroupe.objects.all().order_by('gm_code')]
|
||||
marques = [obj.as_dict() for obj in Marque.objects.all().order_by('mar_code')]
|
||||
competences = [obj.as_dict() for obj in Competence.objects.all().order_by('comp_id')]
|
||||
domaines = [obj.as_dict() for obj in Domaine.objects.all().order_by('d_code')]
|
||||
filieres = [obj.as_dict() for obj in Filiere.objects.all().order_by('domaine__d_code', 'f_code')]
|
||||
zones = [obj.as_dict() for obj in ZoneGeographique.objects.all()]
|
||||
# TODO: Vérifier la présence de l'ordre sur les postes'
|
||||
grades = [obj.as_dict() for obj in Grade.objects.all()]
|
||||
FEs = [obj.as_dict() for obj in FormationEmploi.objects.all().order_by('fe_mere_credo')]
|
||||
garnison_lieux = [obj.as_dict() for obj in FormationEmploi.objects.all().order_by('fe_mere_credo', 'fe_garnison_lieu')]
|
||||
RefSvFils_init = {obj.as_dict()['ref_sv_fil_code'] for obj in RefSvFil.objects.all().order_by('ref_sv_fil_code')}
|
||||
RefSvFils = [{'ref_sv_fil_code': obj} for obj in RefSvFils_init]
|
||||
Specifique = [{'code': i.value} for i in SpecifiqueChoices]
|
||||
SousViviers = [obj.as_dict() for obj in SousVivier.objects.filter(~Q(sv_id='BVT')).order_by('sv_libelle')]
|
||||
Pams = [obj.as_dict() for obj in PAM.objects.filter(~Q(pam_id='SORG')& Q(pam_statut__in=['PAM en cours', 'PAM A+1'])).order_by('pam_id')]
|
||||
Etr = [obj.as_dict() for obj in Fonction.objects.all()]
|
||||
return Response({
|
||||
'groupesMarques': groupesMarques,
|
||||
'marques': marques,
|
||||
'domaines': domaines,
|
||||
'filieres': filieres,
|
||||
'grades': grades,
|
||||
'competences': competences,
|
||||
'FEs': FEs,
|
||||
'statutFutur': ChoicesSerializer(StatutFutur, many=True).data,
|
||||
'Zones': zones,
|
||||
'decisions': ChoicesSerializer(get_all_decisions(), many=True).data,
|
||||
'avisAdministre': RefStatutPamChoicesSerializer(StatutPam, many=True).data,
|
||||
'avisPoste': RefAvisPosteChoicesSerializer(AvisPoste, many=True).data,
|
||||
'RefSvFils': RefSvFils,
|
||||
'Specifique': Specifique,
|
||||
'etatsFeBmob': tuple(e.value for e in AssignmentState),
|
||||
'sousViviers': SousViviers,
|
||||
'pams': Pams,
|
||||
'etr' : Etr,
|
||||
})
|
||||
426
backend-django/backend/views/scoring.py
Normal file
426
backend-django/backend/views/scoring.py
Normal file
@@ -0,0 +1,426 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import pandas as pd
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404, JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..models import Administre, StatutPamChoices
|
||||
from ..models import AvisPosteChoices as AvisPoste
|
||||
from ..models import Poste, SousVivier
|
||||
from ..models.calcul import Calcul
|
||||
from ..models.calcul import StatutCalculChoices as StatutCalcul
|
||||
from ..serializers import ScoringSelectifValidator, ScoringValidator
|
||||
from ..utils.decorators import class_logger
|
||||
from ..utils_calcul import lancer_calculs, lancer_calculSelectif
|
||||
from .commun import (GestionnairePermission, api_exception,
|
||||
execution_time_viewset, query_count_viewset)
|
||||
import _json
|
||||
|
||||
|
||||
# @execution_time_viewset
|
||||
# @query_count_viewset
|
||||
@class_logger
|
||||
class ScoringView(APIView):
|
||||
"""
|
||||
Cette classe est dédiée au vue de lancement pour le scoring et au calcul des scores des administrés à muter avec les postes à pourvoir.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, GestionnairePermission]
|
||||
|
||||
def __recuperer_chemin_requete(self, request: Request) -> str:
|
||||
""" récupère et reconstruit le chemin de la requête (mais pas forcément tel que le client la connaît)
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant le port et l'adrresse ip du serveur
|
||||
|
||||
:return: - **return** (*string*): une chaine de caractère contenant le statut modifié du calcul.
|
||||
|
||||
"""
|
||||
|
||||
port = request.get_port()
|
||||
return f"{request.scheme}://localhost{':' + port if port else ''}{request.path}"
|
||||
|
||||
|
||||
def __appeler_api_depiler_calculs(self, request: Request) -> None:
|
||||
""" appelle l'API pour lancer le prochain calcul en attente, ce qui permet d'attribuer l'opération à un worker indéfini
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant le chemin de la requète
|
||||
"""
|
||||
url = self.__recuperer_chemin_requete(request)
|
||||
try:
|
||||
requests.patch(url, headers=request.headers, cookies=request.COOKIES, timeout=0.001)
|
||||
except requests.exceptions.ReadTimeout:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.logger.debug("échec de l'appel à %s: %s", url, e)
|
||||
|
||||
@atomic
|
||||
def __creer_calcul(self, pam_id: str, sv_id: str) -> Calcul:
|
||||
""" crée un calcul en base sauf s'il existe déjà un calcul en cours ou en attente pour le même sous-vivier
|
||||
Raises
|
||||
------
|
||||
ApiException:
|
||||
un calcul est déjà en cours ou en attente pour le même sous-vivier
|
||||
IntegrityError:
|
||||
la création a échoué
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:type sv_id: str
|
||||
:param sv_id: clé primaire du sous vivier
|
||||
:return: - **Object** (*Object*): retourne l'objet calcul crée
|
||||
"""
|
||||
|
||||
Statut = StatutCalcul
|
||||
COL_ID = 'id'
|
||||
COL_SV = 'sous_vivier_id'
|
||||
COL_PAM = 'pam_id'
|
||||
COL_STATUT = 'ca_statut'
|
||||
COL_DEBUT = 'ca_date_debut'
|
||||
|
||||
colonnes = (COL_ID, COL_SV, COL_PAM, COL_DEBUT, COL_STATUT)
|
||||
df = pd.DataFrame.from_records(Calcul.objects.values_list(*colonnes), columns=colonnes)
|
||||
|
||||
en_cours = df[df[COL_STATUT] == Statut.EN_COURS][COL_SV].values
|
||||
if sv_id in en_cours:
|
||||
raise api_exception(status.HTTP_400_BAD_REQUEST, 'un calcul est déjà en cours pour ce sous-vivier')
|
||||
|
||||
en_attente = df[df[COL_STATUT] == Statut.EN_ATTENTE][COL_SV].values
|
||||
if sv_id in en_attente:
|
||||
raise api_exception(status.HTTP_400_BAD_REQUEST, 'un calcul est déjà en attente pour ce sous-vivier')
|
||||
|
||||
if en_attente.size:
|
||||
calcul_autorise = False
|
||||
else:
|
||||
nb_max = getattr(settings, 'MAX_CALCULS', None)
|
||||
calcul_autorise = not isinstance(nb_max, int) or len(en_cours) < nb_max
|
||||
|
||||
date_debut = timezone.now()
|
||||
Calcul.objects.filter(Q(id=str(sv_id)+str(pam_id)) & Q(pam_id=pam_id) & Q(sous_vivier_id=sv_id) & ~Q(ca_statut__in=(Statut.EN_COURS, Statut.EN_ATTENTE))).delete()
|
||||
if calcul_autorise:
|
||||
return Calcul.objects.create(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id, ca_date_debut=date_debut, ca_statut_pourcentage=0, ca_statut=Statut.EN_COURS)
|
||||
return Calcul.objects.create(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id, ca_date_debut=date_debut, ca_statut_pourcentage=0, ca_statut=Statut.EN_ATTENTE)
|
||||
|
||||
@atomic
|
||||
def __creer_calculSelectif(self, sv_id: str, pam_id: str, l_a_id: list, l_p_id: list) -> Calcul:
|
||||
""" crée un calcul en base sauf s'il existe déjà un calcul en cours ou en attente pour le même sous-vivier
|
||||
Raises
|
||||
------
|
||||
ApiException:
|
||||
un calcul est déjà en cours ou en attente pour le même sous-vivier
|
||||
IntegrityError:
|
||||
la création a échoué
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:type sv_id: str
|
||||
:param sv_id: clé primaire du sous vivier
|
||||
:return: - **Object** (*Object*): retourne l'objet calcul crée
|
||||
"""
|
||||
|
||||
Statut = StatutCalcul
|
||||
COL_ID = 'id'
|
||||
COL_SV = 'sous_vivier_id'
|
||||
COL_STATUT = 'ca_statut'
|
||||
COL_DEBUT = 'ca_date_debut'
|
||||
COL_PAM = 'pam_id'
|
||||
|
||||
colonnes = (COL_ID, COL_SV, COL_DEBUT, COL_STATUT, COL_PAM)
|
||||
df = pd.DataFrame.from_records(Calcul.objects.values_list(*colonnes), columns=colonnes)
|
||||
|
||||
|
||||
en_cours = df[df[COL_STATUT] == Statut.EN_COURS][COL_SV].values
|
||||
|
||||
if l_a_id and l_p_id:
|
||||
if sv_id in en_cours:
|
||||
raise api_exception(status.HTTP_400_BAD_REQUEST, 'un calcul est déjà en cours pour ce sous-ensemble')
|
||||
|
||||
en_attente = df[df[COL_STATUT] == Statut.EN_ATTENTE][COL_SV].values
|
||||
if sv_id in en_attente:
|
||||
raise api_exception(status.HTTP_400_BAD_REQUEST, 'un calcul est déjà en attente pour ce sous-ensemble')
|
||||
|
||||
if en_attente.size:
|
||||
calcul_autorise = False
|
||||
else:
|
||||
nb_max = getattr(settings, 'MAX_CALCULS', None)
|
||||
calcul_autorise = not isinstance(nb_max, int) or len(en_cours) < nb_max
|
||||
|
||||
date_debut = timezone.now()
|
||||
Calcul.objects.filter(Q(id=str(sv_id)+str(pam_id)+'selectif') & Q(pam_id=pam_id) & Q(sous_vivier_id=sv_id) & ~Q(ca_statut__in=(Statut.EN_COURS, Statut.EN_ATTENTE))).delete()
|
||||
if calcul_autorise:
|
||||
return Calcul.objects.create(id=str(sv_id)+str(pam_id)+'selectif',pam_id=pam_id,sous_vivier_id=sv_id, ca_date_debut=date_debut, ca_statut_pourcentage=0, ca_statut=Statut.EN_COURS)
|
||||
return Calcul.objects.create(id=str(sv_id)+str(pam_id)+'selectif',pam_id=pam_id,sous_vivier_id=sv_id, ca_date_debut=date_debut, ca_statut_pourcentage=0, ca_statut=Statut.EN_ATTENTE)
|
||||
|
||||
@atomic
|
||||
def __modifier_statut_premier_calcul_en_attente(self) -> Optional[Tuple[str, datetime]]:
|
||||
""" lance le calcul en attente le plus ancien (si possible). Il ne s'agit que d'une modification en base.
|
||||
(ID de sous-vivier, date de début) du calcul lancé ou None si aucun calcul ne peut être lancé
|
||||
:type request: rest_framework.request.Request
|
||||
:type sv_id: str
|
||||
:param sv_id: clé primaire du sous vivier
|
||||
:type ca_date_debut : date
|
||||
:param ca_date_debut: date de début calcul en attente
|
||||
:return: - **Object** (*Object*): retourne l'objet calcul crée
|
||||
Raises
|
||||
------
|
||||
ApiException:
|
||||
un calcul est déjà en cours ou en attente pour le même sous-vivier
|
||||
IntegrityError:
|
||||
la création a échoué
|
||||
"""
|
||||
|
||||
Statut = StatutCalcul
|
||||
COL_ID = 'id'
|
||||
COL_SV = 'sous_vivier_id'
|
||||
COL_STATUT = 'ca_statut'
|
||||
COL_DEBUT = 'ca_date_debut'
|
||||
COL_PAM = 'pam_id'
|
||||
|
||||
|
||||
colonnes = (COL_ID, COL_SV, COL_PAM, COL_DEBUT, COL_STATUT)
|
||||
df = pd.DataFrame.from_records(Calcul.objects.order_by(COL_DEBUT).values_list(*colonnes), columns=colonnes)
|
||||
|
||||
en_attente = df[df[COL_STATUT] == Statut.EN_ATTENTE]
|
||||
if en_attente.empty:
|
||||
return None
|
||||
|
||||
en_cours = df[df[COL_STATUT] == Statut.EN_COURS][COL_SV].values
|
||||
nb_max = getattr(settings, 'MAX_CALCULS', None)
|
||||
calcul_autorise = not isinstance(nb_max, int) or en_cours.size < nb_max
|
||||
if not calcul_autorise:
|
||||
return None
|
||||
|
||||
a_lancer = en_attente.iloc[0]
|
||||
sv_id = a_lancer[COL_ID]
|
||||
date_attente = a_lancer[COL_DEBUT]
|
||||
date_debut = timezone.now()
|
||||
|
||||
# la date de début est mise à jour aussi pour éviter de compter le temps d'attente
|
||||
mis_a_jour = (
|
||||
Calcul.objects
|
||||
.filter(sous_vivier_id=sv_id, ca_date_debut=date_attente, ca_statut=Statut.EN_ATTENTE)
|
||||
.update(ca_date_debut=date_debut, ca_statut=Statut.EN_COURS)
|
||||
)
|
||||
return (sv_id, date_debut) if mis_a_jour else None
|
||||
|
||||
|
||||
def __executer_calcul(self, pam_id: str,sv_id: str, date_debut: datetime) -> Calcul:
|
||||
""" exécute un calcul (ID de sous-vivier, date de début) du calcul lancé ou None si aucun calcul ne peut être lancé
|
||||
:type request: rest_framework.request.Request
|
||||
:type sv_id: str
|
||||
:param sv_id: clé primaire du sous vivier
|
||||
:type ca_date_debut : date
|
||||
:param ca_date_debut: date de début calcul en attente
|
||||
:return: - **Object** (*Object*): retourne l'objet calcul ainsi que son statut
|
||||
"""
|
||||
|
||||
Statut = StatutCalcul
|
||||
qs_calcul = Calcul.objects.filter(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id, ca_date_debut=date_debut)
|
||||
qs_en_cours = qs_calcul.filter(ca_statut=Statut.EN_COURS)
|
||||
try:
|
||||
lancer_calculs(pam_id,sv_id)
|
||||
except SystemExit:
|
||||
self.logger.info("OGURE M'A TUER")
|
||||
qs_en_cours.update(ca_date_fin=timezone.now(), ca_statut=Statut.TERMINE_DE_FORCE)
|
||||
raise
|
||||
except Exception as e:
|
||||
qs_calcul = Calcul.objects.filter(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id, ca_date_debut=date_debut)
|
||||
if str(e) == "Arret du calcul":
|
||||
qs_calcul.update(ca_date_fin=timezone.now(), ca_statut=Statut.TERMINE_DE_FORCE)
|
||||
raise
|
||||
else:
|
||||
qs_calcul.update(ca_date_fin=timezone.now(), ca_statut=Statut.ERREUR)
|
||||
raise
|
||||
qs_en_cours.update(ca_date_fin=timezone.now(), ca_statut=Statut.TERMINE)
|
||||
return qs_calcul.first()
|
||||
|
||||
def __executer_calculSelectif(self, sv_id: str, pam_id: str, l_a_id: list, l_p_id: list, date_debut: datetime) -> Calcul:
|
||||
""" exécute un calcul (ID de sous-vivier, date de début) du calcul lancé ou None si aucun calcul ne peut être lancé
|
||||
:type request: rest_framework.request.Request
|
||||
:type sv_id: str
|
||||
:param sv_id: clé primaire du sous vivier
|
||||
:type ca_date_debut : date
|
||||
:param ca_date_debut: date de début calcul en attente
|
||||
:return: - **Object** (*Object*): retourne l'objet calcul ainsi que son statut
|
||||
"""
|
||||
|
||||
Statut = StatutCalcul
|
||||
qs_calcul = Calcul.objects.filter(id=str(sv_id)+str(pam_id)+'selectif',sous_vivier_id=sv_id, pam_id=pam_id, ca_date_debut=date_debut)
|
||||
qs_en_cours = qs_calcul.filter(ca_statut=Statut.EN_COURS)
|
||||
try:
|
||||
lancer_calculSelectif(sv_id, pam_id, l_a_id, l_p_id)
|
||||
|
||||
except SystemExit:
|
||||
qs_en_cours.update(ca_date_fin=timezone.now(), ca_statut=Statut.TERMINE_DE_FORCE)
|
||||
raise
|
||||
except Exception as e:
|
||||
qs_calcul = Calcul.objects.filter(id=str(sv_id)+str(pam_id)+'selectif',sous_vivier_id=sv_id, pam_id=pam_id,ca_date_debut=date_debut)
|
||||
if str(e) == "Arret du calcul":
|
||||
qs_calcul.update(ca_date_fin=timezone.now(), ca_statut=Statut.TERMINE_DE_FORCE)
|
||||
raise
|
||||
else:
|
||||
qs_calcul.update(ca_date_fin=timezone.now(), ca_statut=Statut.ERREUR)
|
||||
raise
|
||||
qs_en_cours.update(ca_date_fin=timezone.now(), ca_statut=Statut.TERMINE)
|
||||
return qs_calcul.first()
|
||||
|
||||
|
||||
def get(self, request: Request) -> Response:
|
||||
"""La fonction get vérifie s'il y a un calcul en cours et renvoie des informations sur le calcul.
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'identifiant du sous vivier
|
||||
:return: - réponse contenant les informations sur le calcul comme statut, date_debut, date_fin, administres et postes.
|
||||
"""
|
||||
debut = None
|
||||
fin = None
|
||||
administres = 0
|
||||
postes = 0
|
||||
try:
|
||||
sv_id = request.query_params['sous_vivier_id']
|
||||
pam_id = request.query_params['pam_id']
|
||||
calcul = Calcul.objects.get(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id)
|
||||
debut = calcul.ca_date_debut
|
||||
fin = calcul.ca_date_fin
|
||||
administres_statuts = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER]
|
||||
administres = Administre.objects.filter(pam__pam_id=pam_id,sous_vivier_id=sv_id,a_statut_pam__in=administres_statuts).count()
|
||||
postes = Poste.objects.values_list('p_avis').filter(Q(p_pam__pam_id=pam_id) & Q(sous_viviers=sv_id) & Q(p_avis__in=[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4])).count()
|
||||
statut = calcul.ca_statut
|
||||
statut_pourcentage = calcul.ca_statut_pourcentage
|
||||
|
||||
except:
|
||||
statut = StatutCalcul.AUCUN
|
||||
statut_pourcentage = 0
|
||||
|
||||
return Response(
|
||||
{"statut": statut, "statut_pourcentage": statut_pourcentage, "date_debut": debut, "date_fin": fin, "administres": administres, "postes": postes})
|
||||
|
||||
def patch(self, request: Request, id=None) -> Response:
|
||||
"""La fonction patch est une méthode privée qui execute des méthodes privées qui exécute le calcul et le dépile.
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'identifiant du sous vivier et la date de dbut du calcul
|
||||
"""
|
||||
Statut = StatutCalcul
|
||||
try:
|
||||
id_et_debut = self.__modifier_statut_premier_calcul_en_attente()
|
||||
if id_et_debut is None:
|
||||
return Response({'message': "aucun calcul n'a été lancé"})
|
||||
|
||||
try:
|
||||
calcul = self.__executer_calcul(id_et_debut[0], id_et_debut[1])
|
||||
return Response({'statut': calcul.ca_statut if calcul else Statut.AUCUN})
|
||||
finally:
|
||||
self.__appeler_api_depiler_calculs(request)
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except SystemExit:
|
||||
raise APIException('lancement du prochain calcul arrêté')
|
||||
except:
|
||||
message = 'impossible de lancer le prochain calcul en attente'
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
|
||||
|
||||
def post(self, request: Request) -> Response:
|
||||
""" lance le calcul sur un sous vivier
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'identifiant du sous vivier
|
||||
:type sv_id: str
|
||||
:param sv_id: clé primaire du sous vivier
|
||||
:return: - **return** (*Response*): retourne le statut des postes
|
||||
"""
|
||||
try:
|
||||
# Gestion des 2 cas, calculs sélectifs ou calculs sur l'ensemble du sous-vivier
|
||||
if 'administre_id' in request.data.keys() and 'poste_id' in request.data.keys():
|
||||
Statut = StatutCalcul
|
||||
validator = ScoringSelectifValidator(data=request.data)
|
||||
validator.is_valid(raise_exception=True)
|
||||
sv_id = validator.validated_data.get('sous_vivier_id')
|
||||
pam_id = validator.validated_data.get('pam_id')
|
||||
get_object_or_404(SousVivier.objects, sv_id=sv_id)
|
||||
l_a_id = validator.validated_data.get('administre_id')
|
||||
l_p_id = validator.validated_data.get('poste_id')
|
||||
# calcul
|
||||
calcul = self.__creer_calculSelectif(sv_id, pam_id, l_a_id, l_p_id)
|
||||
if calcul.ca_statut == Statut.EN_COURS:
|
||||
try:
|
||||
calcul = self.__executer_calculSelectif(sv_id, pam_id, l_a_id, l_p_id, calcul.ca_date_debut)
|
||||
except Exception as e:
|
||||
Statut= Statut.ERREUR
|
||||
calcul.ca_statut = Statut
|
||||
calcul.save()
|
||||
error = str(e).split(',')
|
||||
return JsonResponse({'info': error[0]})
|
||||
finally:
|
||||
self.__appeler_api_depiler_calculs(request)
|
||||
|
||||
else:
|
||||
Statut = StatutCalcul
|
||||
validator = ScoringValidator(data=request.data)
|
||||
# validation
|
||||
validator.is_valid(raise_exception=True)
|
||||
sv_id = validator.validated_data.get('sous_vivier_id')
|
||||
pam_id = validator.validated_data.get('pam_id')
|
||||
get_object_or_404(SousVivier.objects, sv_id=sv_id)
|
||||
# calcul
|
||||
calcul = self.__creer_calcul(pam_id,sv_id)
|
||||
if calcul.ca_statut == Statut.EN_COURS:
|
||||
try:
|
||||
calcul = self.__executer_calcul(pam_id,sv_id, calcul.ca_date_debut)
|
||||
except Exception as e:
|
||||
|
||||
Statut= Statut.ERREUR
|
||||
calcul.ca_statut = Statut
|
||||
calcul.save()
|
||||
error = str(e).split(',')
|
||||
return JsonResponse({'info': error[0]})
|
||||
|
||||
finally:
|
||||
self.__appeler_api_depiler_calculs(request)
|
||||
return Response({'statut': calcul.ca_statut if calcul else Statut.AUCUN})
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except SystemExit:
|
||||
raise APIException('calcul arrêté')
|
||||
except:
|
||||
message = 'impossible de lancer le calcul'
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
|
||||
|
||||
@class_logger
|
||||
@execution_time_viewset
|
||||
@query_count_viewset
|
||||
class ArretCalcul(APIView):
|
||||
"""
|
||||
Cette classe est dédiée a l'arrêt du calcul.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated, GestionnairePermission]
|
||||
|
||||
def post(self, request):
|
||||
"""La fonction post change le statut d'un calcul à EN_ATTENTE_ARRET
|
||||
|
||||
:type request: rest_framework.request.Request
|
||||
:param request: Request contenant l'identifiant du sous vivier
|
||||
|
||||
:return: - **return** (*json*): json contenant le statut modifié du calcul.
|
||||
"""
|
||||
Statut = StatutCalcul
|
||||
sv_id = request.data['sous_vivier_id']
|
||||
pam_id = request.data['pam_id']
|
||||
calcul = Calcul.objects.get(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id)
|
||||
if Calcul.objects.filter(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id).exists() and calcul.ca_statut == Statut.EN_COURS:
|
||||
calcul.ca_statut = Statut.EN_ATTENTE_ARRET
|
||||
calcul.save()
|
||||
elif Calcul.objects.filter(id=str(sv_id)+str(pam_id),pam_id=pam_id,sous_vivier_id=sv_id).exists() and calcul.ca_statut == Statut.EN_ATTENTE:
|
||||
calcul.ca_statut = Statut.TERMINE
|
||||
calcul.save()
|
||||
return Response({"statut": calcul.ca_statut})
|
||||
70
backend-django/backend/views/suppression_administres.py
Normal file
70
backend-django/backend/views/suppression_administres.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import pandas as pd
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import IsAdminUser, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..serializers import SuppressionAdministresSerializer
|
||||
from ..utils.alimentation_decorators import (data_perf_logger_factory,
|
||||
get_data_logger)
|
||||
from ..utils.decorators import execution_time, query_count
|
||||
from ..utils_extraction import (DataFrameTypes, FileTypes, read_files_by_type,
|
||||
to_table_suppression_administres)
|
||||
from ..utils_insertion import suppression_administres
|
||||
|
||||
|
||||
class SuppressionAdministresView(APIView):
|
||||
""" Vue pour supprimer les administrés de la base """
|
||||
|
||||
permission_classes = [IsAuthenticated, IsAdminUser]
|
||||
serializer_class = SuppressionAdministresSerializer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = get_data_logger(self)
|
||||
|
||||
|
||||
def get(self, request):
|
||||
return Response("Formulaire d\'OGURE NG permettant de supprimer des administés de la base de données")
|
||||
|
||||
|
||||
@atomic
|
||||
@execution_time(logger_factory=data_perf_logger_factory)
|
||||
@query_count(logger_factory=data_perf_logger_factory)
|
||||
def post(self, request):
|
||||
"""
|
||||
Charge le(s) fichier(s) et met à jour la base.
|
||||
|
||||
:param request: requête, contient les fichiers
|
||||
:type request: class:`rest_framework.request.Request`
|
||||
|
||||
:raises: class:`rest_framework.exceptions.APIException`
|
||||
|
||||
:return: réponse
|
||||
:rtype: class:`rest_framework.response.Response`
|
||||
"""
|
||||
try:
|
||||
validator = self.serializer_class(data=request.data)
|
||||
validator.is_valid(raise_exception=True)
|
||||
adm_suppr = 0
|
||||
|
||||
df_adm_suppr = read_files_by_type({
|
||||
FileTypes.ADM_SUPPR: validator.validated_data.get('administres')
|
||||
}).get(DataFrameTypes.ADM_SUPPR)
|
||||
|
||||
if df_adm_suppr is not None:
|
||||
df = to_table_suppression_administres(df_adm_suppr)
|
||||
self.logger.info('Extraction des administés à supprimer ------> Succès')
|
||||
adm_suppr = suppression_administres(df)
|
||||
self.logger.info('Suppression des administrés ------> Succès')
|
||||
else:
|
||||
self.logger.info('Mise à jour ignorée : suppression des administrés')
|
||||
return Response({f'Suppression de {adm_suppr} administré(s) réussie'})
|
||||
except (Http404, APIException):
|
||||
raise
|
||||
except BaseException:
|
||||
message = "Echec de la suppression"
|
||||
self.logger.exception(message)
|
||||
raise APIException(message)
|
||||
Reference in New Issue
Block a user