""" Ce module contient les Utilitaires d'extraction des données à partir des fichiers """ import datetime import logging from multiprocessing.connection import answer_challenge import time from enum import Enum, auto from typing import Callable, Dict import numpy as np import pandas as pd from django.core.files.base import File from django.db.models import Q from .models import FMOB, Administre from .models import AvisPosteChoices as AvisPoste from .models import (Diplome, Domaine, Filiere, Fonction, FormationEmploi, Garnison, Grade, Marque, MarquesGroupe, Notation, Poste, PreferencesListe, Administres_Pams, Postes_Pams) from .models import StatutPamChoices as StatutPam from .utils.alimentation import BOCols, FmobCols, InseeCols, ReoCols from .utils.alimentation_decorators import (data_perf_logger_factory, get_data_logger) from .utils.decorators import execution_time logger = get_data_logger(__name__) pd.options.mode.chained_assignment = None # default='warn' # valeur arbitraire pour symboliser np.nan. En effet un ensemble d'opérations comme # fillna(np.nan).astype(str).replace([np.nan, 'nan'], [None, None])) # n'est pas pratique car 'nan' pourrait être une valeur du fichier (qui ne représente pas None) APP_NAN = '__ogure_nan__' # expression régulière pour représenter un caractère alphanumérique REGEX_NON_ALPHANUM = '[^a-zA-Z0-9]' class FileTypes(str, Enum): """ enum pour les types de fichiers chargés par l'utilisateur """ # militaires à supprimer ADM_SUPPR = auto() # données BO BO = auto() # commentaires COMMENTS = auto() # diplômes DIPLOME = auto() # filières et domaines DOM_FIL = auto() # FMOB FMOB_FEMP = auto() # FMOB PAM A+1 FMOB_FEMP_PAM_SUIVANT = auto() # FUD FUD = auto() # INSEE INSEE = auto() # référentiel FE REF_FE = auto() # référentiel de zones géographiques SHM REF_GEO = auto() # référentiel de gestionnaires REF_GEST = auto() # référentiel organique REF_ORG = auto() # référentiel de sous-viviers/filières REF_SV_FIL = auto() # REO REO = auto() # REO PAM A +1 REO_PAM_SUIVANT = auto() # REO (OCV) REO_OCV = auto() def __repr__(self): return self.__str__() class DataFrameTypes(Enum): """ enum pour les data frames issus de lectures de fichiers """ # nom (type de fichier, libellé) ADM_SUPPR = (FileTypes.ADM_SUPPR, 'militaires à supprimer') BO = (FileTypes.BO, 'BO') COMMENTS = (FileTypes.COMMENTS, 'commentaires') DIPLOME = (FileTypes.DIPLOME, 'diplômes') DOM_FIL = (FileTypes.DOM_FIL, 'filières / domaines') FEMP = (FileTypes.FMOB_FEMP, 'FEMP') FMOB = (FileTypes.FMOB_FEMP, 'FMOB') FUD = (FileTypes.FUD, 'FUD') INSEE = (FileTypes.INSEE, 'mapping INSEE') REF_FE = (FileTypes.REF_FE, 'référentiel FE') REF_GEO = (FileTypes.REF_GEO, 'référentiel de zones géographiques SHM') REF_GEST = (FileTypes.REF_GEST, 'référentiel de gestionnaires') REF_ORG = (FileTypes.REF_ORG, 'référentiel organique') REF_SV_FIL = (FileTypes.REF_SV_FIL, 'référentiel de sous-viviers/filières') REO = (FileTypes.REO, 'REO') REO_OCV = (FileTypes.REO_OCV, 'postes OCV') #Dataframe PAM A + 1 FEMP_PAM_SUIVANT = (FileTypes.FMOB_FEMP_PAM_SUIVANT, 'FEMP_PAM_SUIVANT') FMOB_PAM_SUIVANT = (FileTypes.FMOB_FEMP_PAM_SUIVANT, 'FMOB_PAM_SUIVANT') REO_PAM_SUIVANT = (FileTypes.REO_PAM_SUIVANT, 'REO_PAM_SUIVANT') def __repr__(self): return self.__str__() class Files(): """ Regroupe les constantes de fichiers TODO combiner avec FileTypes ? """ class AdmSuppr(): """ Constantes pour les colonnes du fichier de militaires à supprimer """ ID_SAP = 'Matricule SAP' # A class DonneesBo(): """ Constantes pour les colonnes du fichier de données BO """ CREDO_FE = "CREDO FE act" # X DATE_ARRIVEE_FE = "Date arrivée FE" DATE_DEBUT_GRADE = "Grade act DD" DATE_ENTREE_SERVICE = "Entrée en Service" DATE_FONCTION_1 = "Fonction -1 DD" DATE_FONCTION_2 = "Fonction -2 DD" DATE_FONCTION_3 = "Fonction -3 DD" DATE_FONCTION_4 = "Fonction -4 DD" DATE_FONCTION_5 = "Fonction -5 DD" DATE_FONCTION_6 = "Fonction -6 DD" DATE_FONCTION_7 = "Fonction -7 DD" DATE_FONCTION_8 = "Fonction -8 DD" DATE_FONCTION_9 = "Fonction -9 DD" DATE_LIEN_SERVICE = "Lien au service DF" DATE_NAISSANCE = "Naissance" DATE_POSITION_STATUAIRE = "Date Position statutaire" DATE_STATUT_CONCERTO = "Situation administrative act DD" # DW DATE_STATUT_CONCERTO_FUTUR = "Date Position statu future" # DY ID_SAP = 'Matricule SAP' # A STATUT_CONCERTO = "Situation admi actuelle" # DX STATUT_CONCERTO_FUTUR = "Position statutaire future" # DZ class FMOB(): """ Constantes pour les colonnes du fichier de FMOB """ ID_SAP = 'Matricule SAP' class REO(): """ Constantes pour les colonnes du fichier de FMOB """ CODE_POSTAL = 'Code Postal /OB G' def open_excel(io, sheetname, engine=None, header=0, usecols=None, skiprows=0, dtype=None, converters=None): """ Ouvre un onglet spécifique d'un fichier excel :type io: chaine de caractères :param io: chemin du fichier excel :type sheetname: chaine de caractères :param sheetname: nom de l'onglet à ouvrir :type header: entier :param header: indice de la ligne comportant le nom des colonnes :return: - **df** (*DataFrame*): tableau de l'onglet à ouvrir. """ df = pd.read_excel(io, sheet_name=sheetname, engine=engine, header=header, usecols=usecols, skiprows=skiprows, dtype=dtype, converters=converters) return df def read_files_by_type(files_by_type: Dict[FileTypes, File]) -> Dict[DataFrameTypes, pd.DataFrame]: """ Lit les fichiers donnés et renvoie des data frames. :param files_by_type: fichiers indexés par type :type files_by_type: Dict[FileTypes, File] :return: data frame indexés par type :rtype: Dict[DataFrameTypes, pd.DataFrame] """ df_by_type = {} # FIXME: (front) Forcer l'engine en fonction de l'extension du fichier logger.info('------------------ Start File Processing -----------------------') @execution_time(logger_factory=data_perf_logger_factory) def read_file(label: str, file: File, read_func: Callable[[None], pd.DataFrame]) -> pd.DataFrame: """ Utilise la fonction pour lire le fichier """ logger.debug('Lecture du fichier %s', label) try: df = read_func(file) logger.info('Lecture du fichier %s ------> Succès', label.ljust(40)) except BaseException: logger.error('Lecture du fichier %s ------> Echec', label.ljust(40)) raise return df def read_file_add_df(df_type: DataFrameTypes, read_func: Callable[[None], pd.DataFrame]) -> None: """ Vérifie qu'il existe un fichier pour le type de DataFrame donné. Si oui, utilise la fonction pour le lire et ajoute le DataFrame au résultat. :param df_type: type de DataFrame :type df_type: DataFrameTypes :param read_func: fonction de lecture du fichier :type read_func: Callable[[None], pd.DataFrame] """ file_type = df_type.value[0] file = files_by_type.get(file_type) if file: df_by_type[df_type] = read_file(df_type.value[1], file, read_func) read_file_add_df(DataFrameTypes.ADM_SUPPR, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.BO, lambda f: open_excel(f, sheetname=0, engine='openpyxl', usecols=BOCols.columns(), converters=BOCols.converters())) read_file_add_df(DataFrameTypes.COMMENTS, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.DIPLOME, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.DOM_FIL, lambda f: open_excel(f, sheetname='Feuil2', engine='openpyxl')) read_file_add_df(DataFrameTypes.FEMP, lambda f: open_excel(f, sheetname='FEMP', engine='openpyxl', converters=FmobCols.converters())) read_file_add_df(DataFrameTypes.FMOB, lambda f: open_excel(f, sheetname='FMOB', engine='openpyxl', converters=FmobCols.converters())) read_file_add_df(DataFrameTypes.FEMP_PAM_SUIVANT, lambda f: open_excel(f, sheetname='FEMP', engine='openpyxl', converters=FmobCols.converters())) read_file_add_df(DataFrameTypes.FMOB_PAM_SUIVANT, lambda f: open_excel(f, sheetname='FMOB', engine='openpyxl', converters=FmobCols.converters())) read_file_add_df(DataFrameTypes.FUD, lambda f: open_excel(f, sheetname=0, engine='openpyxl', skiprows=1)) read_file_add_df(DataFrameTypes.INSEE, lambda f: open_excel(f, sheetname=0, engine='openpyxl', usecols=InseeCols.columns(), converters=InseeCols.converters())) read_file_add_df(DataFrameTypes.REF_FE, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.REF_GEO, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.REF_GEST, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.REF_ORG, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.REF_SV_FIL, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) read_file_add_df(DataFrameTypes.REO, lambda f: open_excel(f, sheetname=0, engine='openpyxl', converters=ReoCols.converters())) read_file_add_df(DataFrameTypes.REO_PAM_SUIVANT, lambda f: open_excel(f, sheetname=0, engine='openpyxl', converters=ReoCols.converters())) read_file_add_df(DataFrameTypes.REO_OCV, lambda f: open_excel(f, sheetname=0, engine='openpyxl')) logger.info('------------------- End File Processing ------------------------') return df_by_type #Fonction pour la table PAM def to_table_pam(date): """ Construit la table PAM qui servira à récuperer l'année du PAM et piloter l'affichage des données en fonction du bon pam pour chaque tables Cloture le PAM en le sauvegardant dans un fichier excel """ annee_pam = str(date.year) annee_pam_suivant = str(date.year + 1) pam=pd.DataFrame(columns = ['pam_id','pam_date', 'pam_libelle','pam_statut']) pam_id = annee_pam pam_id_suivant = annee_pam_suivant pam_libelle = f"PAM de l'année {annee_pam}" pam_libelle_suivant = f"PAM de l'année {annee_pam_suivant}" pam_date = date pam_date_suivant = pam_date pam_statut = "PAM en cours" pam_statut_suivant = "PAM A+1" pam['pam_id'] = [pam_id,pam_id_suivant] pam['pam_date'] = [pam_date,pam_date_suivant] pam['pam_libelle'] = [pam_libelle,pam_libelle_suivant] pam['pam_statut'] = [pam_statut,pam_statut_suivant] return pam # Fonction pour la table Domaine def to_table_domaines(df_domaines): """ Transformation de la table domaines. Sélection et renommage des champs. :param df_domaines: tableau domaines :type df_domaines: class:`pandas.DataFrame` :return: tableau transformé contenant les information de domaines :rtype: class:`pandas.DataFrame` """ col_pk = Domaine.Cols.PK col_mapping = {'DOMAINE': col_pk} df = df_domaines[col_mapping.keys()] df = (df.rename(columns=col_mapping) .dropna(subset=[col_pk]) .drop_duplicates(subset=[col_pk])) df[col_pk] = df[col_pk].str.replace(REGEX_NON_ALPHANUM, '_', regex=True) return df # Fonction pour la table Filières def to_table_filieres(df_filiere): """ Transformation de la table filieres. Sélection et renommage des champs. :type df_filiere: dataframe :param df_filiere: tableau filieres :return: - **filieres** (*DataFrame*): Tableau transformé contenant les information de filieres. """ col_fil = ['DOMAINE', 'FILIERE'] filieres = df_filiere[col_fil] filieres.rename(columns={'DOMAINE': 'domaine_id', 'FILIERE': 'f_code'}, inplace=True) filieres = (filieres.fillna(APP_NAN) .replace({APP_NAN: None})) return filieres # Fonction pour la table Garnison def to_table_garnisons(df_insee, df_donneebo): """Création de la table Garnison à partir des tables insee et donnee bo. Sélection et renommage des champs. :type df_insee: dataframe :param df_insee: table insee :type df_donneebo: dataframe :param df_donneebo: table donnees BO :return: - **garnisons** (*DataFrame*): Tableau créé contenant les information de garnisons. . """ garnisons = df_donneebo.drop_duplicates(subset="Garnison act", keep='first') garnisons = garnisons.merge(df_insee, how="left", left_on="Matricule SAP", right_on="Matricule SAP") garnisons = garnisons[['CODE INSEE', 'Garnison act', 'CODE POSTAL']] garnisons.drop_duplicates(subset="CODE INSEE", keep='first', inplace=True) garnisons.columns = ["gar_id", "gar_lieu", "gar_code_postal"] garnisons.reset_index(drop=True, inplace=True) return garnisons # Fonction pour la table affectation def to_table_affectation(df_donneebo): """Création de la table Affectation à partir de la table donnee bo. Sélection et renommage des champs. :type df_donneebo: dataframe :param df_donneebo: table donnees BO :type to_table_administre_df: dataframe :param to_table_administre_df: le retour de la fonction to_table_administre :return: - **all_affectation** (*DataFrame*): Tableau créé contenant les information des affectations. . """ all_affec = [] administres_fields = ('a_id_sap',) administres = pd.DataFrame.from_records(Administre.objects.all().values(*administres_fields)) all_donnees = df_donneebo.merge(administres, how='inner', left_on='Matricule SAP', right_on='a_id_sap') for i in range(1, 10): frames = ['Matricule SAP', 'Affectation -' + str(i) + ' L', 'Affectation -' + str(i) + ' DD'] df_temp = all_donnees[frames] df_temp.columns = ['Matricule SAP', 'Affectation L', 'Affectation D'] df_temp.dropna(subset=['Affectation L'], inplace=True) all_affec.append(df_temp) all_affectation = pd.concat(all_affec, ignore_index=True) all_affectation.drop_duplicates(inplace=True, ignore_index=True) return all_affectation # Fonction pour la table affectation def to_table_diplome(df_diplome): """Création de la table Affectation à partir de la table donnee bo. Sélection et renommage des champs. :type df_diplome: dataframe :param df_diplome: table des diplomes :return: - **all_diplome** (*DataFrame*): Tableau créé contenant les information des diplomes. . """ all_dip = [] administres_fields = ('a_id_sap',) administres = pd.DataFrame.from_records(Administre.objects.all().values(*administres_fields)) all_diplomes = df_diplome.merge(administres, how='inner', left_on='Matricule SAP', right_on='a_id_sap') for i in range(1, 11): frames = ['Matricule SAP', 'Diplôme militaire -' + str(i) + ' L', 'Diplôme militaire -' + str(i) + ' D', 'Diplôme militaire -' + str(i) + ' note', 'Diplôme militaire -' + str(i) + ' niveau'] df_temp = all_diplomes[frames] df_temp.columns = ['Matricule SAP', 'Diplôme militaire L', 'Diplôme militaire D', 'Diplôme militaire note', 'Diplôme militaire niveau'] df_temp.dropna(subset=['Diplôme militaire L'], inplace=True) all_dip.append(df_temp) all_diplome = pd.concat(all_dip, ignore_index=True) all_diplome.drop_duplicates(inplace=True, ignore_index=True) all_diplome = all_diplome.groupby(['Matricule SAP', 'Diplôme militaire L', 'Diplôme militaire D'])['Diplôme militaire note'].agg('max').reset_index() return all_diplome def to_table_fud(df_fud): """Création de la table Affectation à partir de la table donnee bo. Sélection et renommage des champs. :type df_fud: dataframe :param df_fud: table fud :type to_table_administre_df: dataframe :param to_table_administre_df: table administre de la fonction to_table_administre dans extraction :return: - **all_fuds** (*DataFrame*): Tableau créé contenant les information des FUDs. . """ all_fud = [] administres_fields = ('a_id_sap',) administres = pd.DataFrame.from_records(Administre.objects.all().values(*administres_fields)) all_fuds = df_fud.merge(administres, how='inner', left_on='Matricule SAP', right_on='a_id_sap') for i in range(1, 11): frames = ['Matricule SAP', 'FUD -' + str(i) + ' L', 'FUD -' + str(i) + ' DD', 'FUD -' + str(i) + ' DF'] df_temp = all_fuds[frames] df_temp.columns = ['Matricule SAP', 'FUD L', 'FUD DD', 'FUD DF'] df_temp.dropna(subset=['FUD L'], inplace=True) all_fud.append(df_temp) all_fud = pd.concat(all_fud, ignore_index=True) all_fud.drop_duplicates(inplace=True, ignore_index=True) return all_fud def to_table_administre_notation(df_donne_bo): """Création de la table Notation à partir de la table donnee bo. Sélection et renommage des champs. :type df_donne_bo: dataframe :param df_donne_bo: table donnee bo :type to_table_administre_df: dataframe :param to_table_administre_df: table administre de la fonction to_table_administre dans extraction :return: - **all_fuds** (*DataFrame*): Tableau créé contenant les information des FUDs. . """ donne_bo = df_donne_bo.copy() donne_bo.drop_duplicates(['Matricule SAP'], inplace=True, ignore_index=True) all_notation = [] frames = ['Matricule SAP', 'Année notation A', 'IRIS / RAC retenu A', 'NR/NGC cumulé A', 'QSR A', 'Apt resp / Emp sup A', 'Potentiel responsabilités catégorie sup A', 'Age en années (au 31/12)'] df_temp = donne_bo[frames] df_temp.columns = ['Matricule SAP', 'no_annne_de_notation', 'no_rac_ou_iris_cumule', 'no_nr_ou_iris', 'no_rf_qsr', 'no_aptitude_emploie_sup', 'no_potentiel_responsabilite_sup', 'no_age_annees'] all_notation.append(df_temp) for i in range(1, 6): frames = ['Matricule SAP', 'Année notation A-' + str(i), 'IRIS / RAC retenu A-' + str(i), 'NR/NGC cumulé A-' + str(i), 'QSR A-' + str(i), 'Apt resp / Emp sup A-' + str(i), 'Potentiel responsabilités catégorie sup A-' + str(i), 'Age en années (au 31/12)'] df_temp = donne_bo[frames] df_temp.columns = ['Matricule SAP', 'no_annne_de_notation', 'no_rac_ou_iris_cumule', 'no_nr_ou_iris', 'no_rf_qsr', 'no_aptitude_emploie_sup', 'no_potentiel_responsabilite_sup', 'no_age_annees'] df_temp['no_age_annees'] = df_temp['no_age_annees'].astype(int) - i all_notation.append(df_temp) all_notations = pd.concat(all_notation, ignore_index=True) all_notations.drop_duplicates(inplace=True, ignore_index=True) all_notations = (all_notations.fillna(APP_NAN) .replace({APP_NAN: None})) administres_fields = ('a_id_sap',) administres = pd.DataFrame.from_records(Administre.objects.all().values(*administres_fields)) all_notations = all_notations.merge(administres, how='inner', left_on='Matricule SAP', right_on='a_id_sap') all_notations = all_notations.reset_index(drop= True) all_notations = all_notations.fillna(np.NAN).replace('None',np.NAN) all_notations = all_notations.rename(columns = {'Matricule SAP':'administre_id'}) all_notations['key'] = all_notations['administre_id'].astype(float).astype(str) + '_' + all_notations['no_age_annees'].astype(float).astype(str) return all_notations # Fonction d'extraction du fichier contenant les militaires à supprimer def to_table_suppression_administres(df_adm_suppr): """ Création du dataframe adm_suppr. :return: - **adm_suppr** (*DataFrame*): Tableau créé contenant les information des administrés à supprimer. """ df_adm_suppr = (df_adm_suppr.drop_duplicates(subset=[Files.AdmSuppr.ID_SAP], keep='first') .dropna(subset=[Files.AdmSuppr.ID_SAP])) administres = pd.DataFrame.from_records(Administre.objects.all().values('a_id_sap')) all_adm_suppr = df_adm_suppr.merge(administres, how='inner', left_on=Files.AdmSuppr.ID_SAP, right_on='a_id_sap') adm_suppr = pd.DataFrame(columns=['a_id_sap']) adm_suppr['a_id_sap'] = all_adm_suppr[Files.AdmSuppr.ID_SAP].astype(int) adm_suppr = adm_suppr.reset_index(drop=True) return adm_suppr # Fonction pour la table Fonction def to_table_fonctions(reo): """ Création de la table Fonction à partir dela talbe REO. Sélection et renommage des champs. :type reo: dataframe :param reo: table reo :return: - **fonctions** (*DataFrame*): Tableau créé contenant les information de fonction. """ # sélection et renommage des champs fonctions = reo[[ReoCols.FONCTION_ID.value, ReoCols.FONCTION_LIBELLE.value]] fonctions.drop_duplicates(keep='first', inplace=True) fonctions = fonctions.rename(columns={ReoCols.FONCTION_ID.value: 'fon_id', ReoCols.FONCTION_LIBELLE.value: 'fon_libelle'}) fonctions['fon_id'] = fonctions['fon_id'].astype(int, errors='ignore') fonctions = (fonctions.drop_duplicates(subset=['fon_id'], keep='first') .dropna() .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) return fonctions # Fonction pour la table RefGest def to_table_ref_gest(df_ref_gest): """ Création de la table RefGest à partir de la table Référentiel gestionnaires DRHAT anonymisé. Sélection et renommage des champs. :type df_ref_gest: dataframe :param df_ref_gest: table Référentiel gestionnaires DRHAT anonymisé :return: - **ref_gest** (*DataFrame*): Tableau créé contenant les information du référentiel gestionnaire. """ # sélection et renommage des champs ref_gest = df_ref_gest.iloc[:, 0:8] ref_gest.columns = ['ref_gest_sap', 'ref_gest_username', 'ref_gest_email', 'ref_gest_first_name', 'ref_gest_last_name', 'ref_gest_grade', 'ref_gest_niv_org', 'ref_gest_org_id'] ref_gest = (ref_gest.drop_duplicates(subset=['ref_gest_sap'], keep='first') .dropna(subset=['ref_gest_sap']) .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) return ref_gest # Fonction pour la table RefOrg def to_table_ref_org(df_ref_org): """ Création de la table RefOrg à partir de la table Référentiel organique. Sélection et renommage des champs. :type df_ref_org: dataframe :param df_ref_org: table Référentiel organique :return: - **ref_org** (*DataFrame*): Tableau créé contenant les information du référentiel organique. """ # sélection et renommage des champs ref_org = df_ref_org.iloc[:, 0:16] ref_org.columns = ['ref_org_code_niv_org1', 'ref_org_lib_niv_org1', 'ref_org_code_niv_org2', 'ref_org_lib_niv_org2', 'ref_org_code_niv_org3', 'ref_org_lib_niv_org3', 'ref_org_code_niv_org4', 'ref_org_lib_niv_org4', 'ref_org_niv_org', 'ref_org_ref_fe', 'ref_org_ref_sv_fil', 'ref_org_droit_lect', 'ref_org_droit_ecr', 'ref_org_expert_hme', 'ref_org_bvt', 'ref_org_itd'] # Création d'une clé primaire (inexistante dans le référentiel Excel importé) # Pour cela, on parcourt toutes les lignes du référentiel # A chaque ligne, la clé primaire sera le code_niv_org non vide d'indice le plus élevé list_pk = [] for i in range(ref_org.shape[0]): code_niv_org4 = ref_org.loc[i, 'ref_org_code_niv_org4'] # code_niv_org d'indice 4 code_niv_org3 = ref_org.loc[i, 'ref_org_code_niv_org3'] # code_niv_org d'indice 3 code_niv_org2 = ref_org.loc[i, 'ref_org_code_niv_org2'] # code_niv_org d'indice 2 code_niv_org1 = ref_org.loc[i, 'ref_org_code_niv_org1'] # code_niv_org d'indice 1 if not pd.isnull(code_niv_org4): list_pk.append(code_niv_org4) elif not pd.isnull(code_niv_org3): list_pk.append(code_niv_org3) elif not pd.isnull(code_niv_org2): list_pk.append(code_niv_org2) else: list_pk.append(code_niv_org1) ref_org.insert(0, 'ref_org_code', list_pk) for c in ['ref_org_ref_fe', 'ref_org_ref_sv_fil', 'ref_org_droit_lect', 'ref_org_droit_ecr', 'ref_org_expert_hme', 'ref_org_bvt', 'ref_org_itd']: ref_org[c] = pd.notnull(ref_org[c]) ref_org = (ref_org.drop_duplicates(subset=['ref_org_code'], keep='first') .dropna(subset=['ref_org_code']) .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) return ref_org # Fonction pour la table RefSvFil def to_table_ref_sv_fil(df_ref_sv_fil): """ Création de la table RefSvFil à partir de la table Référentiel sous-vivier filiere. Sélection et renommage des champs. :type df_ref_sv_fil: dataframe :param df_ref_sv_fil: table Référentiel sous-vivier filiere :return: - **ref_sv_fil** (*DataFrame*): Tableau créé contenant les information du référentiel sous-vivier filiere. """ # sélection et renommage des champs ref_sv_fil = df_ref_sv_fil.iloc[:, 0:12] ref_sv_fil.columns = ['ref_sv_fil_code_niv_org1', 'ref_sv_fil_lib_niv_org1', 'ref_sv_fil_code_niv_org2', 'ref_sv_fil_lib_niv_org2', 'ref_sv_fil_code_niv_org3', 'ref_sv_fil_lib_niv_org3', 'ref_sv_fil_code_niv_org4', 'ref_sv_fil_lib_niv_org4', 'ref_sv_fil_dom_gest', 'ref_sv_fil_dom', 'ref_sv_fil_fil', 'ref_sv_fil_cat'] # On parcourt toutes les lignes du référentiel # A chaque ligne, la clé primaire sera le code_niv_org non vide d'indice le plus élevé list_pk = [] for i in range(ref_sv_fil.shape[0]): code_niv_org4 = ref_sv_fil.loc[i, 'ref_sv_fil_code_niv_org4'] # code_niv_org d'indice 4 code_niv_org3 = ref_sv_fil.loc[i, 'ref_sv_fil_code_niv_org3'] # code_niv_org d'indice 3 code_niv_org2 = ref_sv_fil.loc[i, 'ref_sv_fil_code_niv_org2'] # code_niv_org d'indice 2 code_niv_org1 = ref_sv_fil.loc[i, 'ref_sv_fil_code_niv_org1'] # code_niv_org d'indice 1 if not pd.isnull(code_niv_org4): list_pk.append(code_niv_org4) elif not pd.isnull(code_niv_org3): list_pk.append(code_niv_org3) elif not pd.isnull(code_niv_org2): list_pk.append(code_niv_org2) else: list_pk.append(code_niv_org1) # Ici, ref_sv_fil_code n'est pas une clé primaire car pour un même code il peut y avoir plusieurs couples DOM/FIL # A la différence de ce qui est fait dans to_table_ref_org, on ne supprime donc pas les doublons de la colonne 'ref_sv_fil_code' ref_sv_fil.insert(0, 'ref_sv_fil_code', list_pk) ref_sv_fil = (ref_sv_fil.drop_duplicates(keep='first') .dropna(subset=['ref_sv_fil_code']) .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True) .drop(ref_sv_fil.columns[1:9], axis=1) .reset_index(drop=True)) ref_sv_fil['sous_vivier_id'] = ref_sv_fil['ref_sv_fil_dom'].astype(str) + ", " + ref_sv_fil["ref_sv_fil_fil"].astype(str) + ", " + ref_sv_fil["ref_sv_fil_cat"].astype(str) return ref_sv_fil # Fonction pour la table SousVivier def to_table_sous_vivier(df_ref_sv_fil): """ Création de la table SousVivier à partir de la table Référentiel sous-vivier filiere. Sélection et renommage des champs. :type df_ref_sv_fil: dataframe :param df_ref_sv_fil: table Référentiel sous-vivier filiere :return: - **sous_vivier** (*DataFrame*): Tableau créé contenant les information de la table SousVivier. """ # sélection et renommage des champs sous_vivier = df_ref_sv_fil.iloc[:, [9, 10, 11]] sous_vivier.columns = ['sv_dom', 'sv_fil', 'sv_cat'] sous_vivier = (sous_vivier.drop_duplicates(keep='first') .dropna() .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) sous_vivier.insert(0, 'sv_id', '') sous_vivier['sv_id'] = sous_vivier['sv_dom'].astype(str) + ", " + sous_vivier["sv_fil"].astype(str) + ", " + sous_vivier["sv_cat"].astype(str) sous_vivier.insert(1, 'sv_libelle', '') sous_vivier['sv_libelle'] = sous_vivier['sv_dom'].astype(str) + ", " + sous_vivier["sv_fil"].astype(str) + ", " + sous_vivier["sv_cat"].astype(str) return sous_vivier # Fonction pour le référentiel FE def to_table_ref_fe(df_ref_fe): """ Adaptation du dataframe issu de la table Référentiel FE. Sélection et renommage des champs. :type df_ref_fe: dataframe :param df_ref_fe: table Référentiel FE :return: - **ref_fe** (*DataFrame*): Tableau créé contenant les information du référentiel FE. """ # sélection et renommage des champs ref_fe = df_ref_fe.iloc[:, [1, 3, 4, 5, 9, 12, 15, 16, 17, 18]] ref_fe.columns = ['fe_credo', 'fe_mere_credo', 'fe_mere_la', 'fe_fot', 'fe_abo_fe', 'fe_pilier_niv1', 'fe_code_niv_org4', 'fe_niv_org4', 'fe_code_niv_org4_mdr', 'fe_niv_org4_mdr'] ref_fe = (ref_fe.drop_duplicates(subset=['fe_credo'], keep='first') .dropna(subset=['fe_credo']) .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) return ref_fe # Fonction pour la table Grade def to_table_grades(bo): """ Création de la table Grades à partir de la table donnee BO. Sélection et renommage des champs. :type bo: dataframe :param bo: table donnees BO :return: - **grades** (*DataFrame*): Tableau créé contenant les information de grades. """ # sélection et renommage des champs grades = pd.DataFrame(bo['GRADE TA'].value_counts().index) grades['gr_id'] = grades.index grades.columns = ["gr_code", "gr_id"] return grades # Fonction pour la table FE avec les données REO et Donnes BO def to_table_fe(df_ref_fe): ref_fe = df_ref_fe.iloc[:, [1, 2, 3, 4, 5, 9, 10, 11, 12, 15, 16, 17, 18]] ref_fe.columns = ['fe_credo', 'fe_libelle', 'fe_mere_credo', 'fe_mere_la', 'fe_fot', 'fe_abo_fe', 'fe_garnison_lieu', 'fe_code_postal', 'fe_pilier_niv1', 'fe_code_niv_org4', 'fe_niv_org4', 'fe_code_niv_org4_mdr', 'fe_niv_org4_mdr'] # vérifier que garnison_id_id existe dans table Garnison # Pour chaque garnison L, on véfie si il existe dans gar_lieu de la table Garnison, si oui on récupère l'id, si non =>erreur ref_fe['fe_nb_poste_reo_mdr'] = 0 ref_fe['fe_nb_poste_reevalue_mdr'] = 0 ref_fe['fe_nb_poste_vacant_mdr'] = 0 ref_fe['fe_nb_poste_occupe_mdr'] = 0 ref_fe['fe_nb_poste_reo_off'] = 0 ref_fe['fe_nb_poste_reevalue_off'] = 0 ref_fe['fe_nb_poste_vacant_off'] = 0 ref_fe['fe_nb_poste_occupe_off'] = 0 ref_fe['fe_nb_poste_reo_soff'] = 0 ref_fe['fe_nb_poste_reevalue_soff'] = 0 ref_fe['fe_nb_poste_vacant_soff'] = 0 ref_fe['fe_nb_poste_occupe_soff'] = 0 ref_fe = (ref_fe.drop_duplicates(subset=['fe_credo'], keep='first') .dropna(subset=['fe_credo']) .fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) return ref_fe # Fonction pour la table MarquesGroupe def to_table_groupesMarques(): """ Création de la table GroupesMarques en dur. :return: - **groupesMarques** (*DataFrame*): Tableau créé contenant les information de groupesMarques. """ groupesMarques = pd.DataFrame( columns=['gm_id', 'gm_type', 'gm_code', 'gm_libelle', 'gm_selectionMultiple', 'gm_ordre']) groupesMarques['gm_id'] = ['1', '2', '3', '4', '5'] groupesMarques['gm_code'] = ['PRIO', 'GEN', 'ADM', 'POS', 'AFF'] groupesMarques['gm_type'] = ['C', 'C', 'M', 'P', 'A'] groupesMarques['gm_libelle'] = ['CIAT', 'Générique', 'Administrés', 'Poste', 'Affectations'] groupesMarques['gm_selection_multiple'] = np.array([False, True, True, True, True], dtype=object) groupesMarques['gm_ordre'] = [20, 21, 10, 10, 10] return groupesMarques # Fonction pour la table Marques def to_table_marques(): """ Création de la table Marques en dur. :return: - **marques** (*DataFrame*): Tableau créé contenant les information de marques. """ marques = pd.DataFrame(columns=['groupe_marques_id', 'mar_id', 'mar_code', 'mar_libelle', 'mar_ordre']) marques['groupe_marques_id'] = ['1', '1', '1', '1', '2','2', '2', '2', '2', '2', '2', '2', '2', '3','3','3','3','4','4','4','4'] marques['mar_code'] = ['P1', 'P2', 'P3', 'P4','AGS','ETOILE', 'ETOILE2','DRAPEAU', 'DRAPEAU2', 'ALERTE', 'QUESTION','QUESTION2', 'COCHE', 'BDG','AGR','FDC','RDC','DRAPEAU3', 'DRAPEAU4', 'DRAPEAU5','DRAPEAU6',] marques['mar_libelle'] = ['1 Passage CIAT', '2 Passage CIAT','3 Passage CIAT','4 Passage CIAT', 'Agrément STATUT','Affectation poste ITD','Affectation poste CIAT','TRV1','TRV2', 'Attention particulière', 'Attente recrutement','Attente concours', 'PLS', 'Blessé de guerre', 'Agrément REC part', 'Fin de carrière', 'Couple','TRV1','TRV2','TCOS','C2'] marques['mar_ordre'] = [1, 2, 3, 4, 1, 2, 3, 4, 5,6,7, 8, 9, 4,3, 2, 1,1,2,3,4] marques['mar_id'] = ['1_P1', '1_P2', '1_P3', '1_P4', '2_AGS','2_E','2_E2', '2_D', '2_D2', '2_ALERTE', '2_QUESTION','2_QUESTION2', '2_COCHE', '3_BDG','3_AGR','3_FDC', '3_RDC','4_D3','4_D4','4_D5','4_D6',] return marques # Fonction pour la table Marques def to_table_zone_geographique(df_zones): """ Création de la table Zones Geographique en dur. :return: - **zones** (*DataFrame*): Tableau créé contenant les information de zones geographiques. """ df_zones.drop_duplicates(inplace=True) zones = pd.DataFrame(columns=['zone_id', 'zone_libelle']) zones['zone_id'] = df_zones['Zones géographiques SHM'].str.replace(' ', '') zones['zone_libelle'] = df_zones['Zones géographiques SHM'] return zones # Fonction pour la table Marques def to_table_commentaires(df_com): """ Création du dataframe commentaires. :return: - **commentaires** (*DataFrame*): Tableau créé contenant les information des commentaires. """ df_com.drop_duplicates(inplace=True) administres = pd.DataFrame.from_records(Administre.objects.all().values('a_id_sap')) all_commentaires = df_com.merge(administres, how='inner', left_on='IDSAP', right_on='a_id_sap') commentaires = pd.DataFrame(columns=['a_id_sap', 'a_notes_gestionnaire']) commentaires['a_id_sap'] = all_commentaires['IDSAP'].astype(int) commentaires['a_notes_gestionnaire'] = all_commentaires['COMMENTAIRES'] commentaires = commentaires.reset_index(drop=True) return commentaires # Fonction pour la table Poste def to_table_postes(reo): """ Création de la table postes à partir de la table REO. Sélection et renommage des champs. :type reo: dataframe :param reo: table REO :return: - **postes** (*dataframe*): Tableau créé contenant les information de postes. """ postes = reo.copy() # administres = pd.DataFrame.from_records(Administre.objects.all().values()) # Ancien REO postes = postes[['ID du poste /MC', "Code Poste CREDO", "FE mère CREDO", "FE CREDO", "FE mère LA", "FE LA","Fonction du Poste L", "Domaine emploi LA", "Filière emploi LA", "NF emploi C", "Emploi L", "Catégorie du poste LA", "Marquant DUO/HDUO", "Origine du poste C", "Catégorie/regroupement grade L", "Grade LA"]] postes = postes[[ ReoCols.ANNEE_PROJET.value, ReoCols.FORMATION_EMPLOI.value, ReoCols.CODE_POSTAL.value, ReoCols.CATEGORIE.value, ReoCols.EIP.value, ReoCols.DOMAINE.value, ReoCols.FILIERE.value, ReoCols.CODE_NF.value, ReoCols.ID_POSTE.value, ReoCols.FONCTION_ID.value, ReoCols.FONCTION_LIBELLE.value, ReoCols.DOMAINE_GESTION.value, ]] postes = (postes.dropna(subset=[ReoCols.ID_POSTE.value]) .drop_duplicates(subset=[ReoCols.ID_POSTE.value], keep='first') .rename(columns={ ReoCols.ID_POSTE.value: 'p_id', ReoCols.FORMATION_EMPLOI.value: 'formation_emploi_id', ReoCols.DOMAINE.value: 'p_domaine', ReoCols.ANNEE_PROJET.value: 'p_annee', ReoCols.FILIERE.value: 'p_filiere', ReoCols.CODE_NF.value: 'p_nf', ReoCols.FONCTION_ID.value: 'fonction_id', ReoCols.FONCTION_LIBELLE.value: 'p_fonction', ReoCols.CATEGORIE.value: 'p_categorie', ReoCols.DOMAINE_GESTION.value: 'p_nfs', ReoCols.CODE_POSTAL.value: 'p_dep', ReoCols.EIP.value:'p_eip'})) postes['p_dep'] = postes['p_dep'].fillna('').str.slice(0, 2, 1).replace('', None) postes = (postes.fillna(APP_NAN) .replace({APP_NAN: None}) .reset_index(drop=True)) # initialisation des colonnes vides # traitement de departement postes['p_date_fin'] = None postes['p_notes_gestionnaire'] = None postes['p_notes_partagees'] = None postes['p_liste_id_marques'] = None postes['p_flag_particulier'] = None postes['statut_decision'] = None return postes # Fonction qui permet de faire l'extraction de rèquete poste-ocv def to_table_reo_ocv(reo_ocv_df): """ Selection des colonnes des attributs . Sélection et renommage des champs. :type reo: dataframe :param reo: table REO :return: - **postes** (*dataframe*): Tableau créé contenant les information de postes. """ postes = pd.DataFrame.from_records(Poste.objects.all().values()) postes_ocv = reo_ocv_df.copy() postes_ocv = postes_ocv[['Code Poste CREDO', 'Identifiant SAP', 'Marquant DUO/HDUO']] postes_ocv.dropna(subset=['Code Poste CREDO', 'Identifiant SAP'], inplace=True) postes_ocv = postes_ocv[postes_ocv['Marquant DUO/HDUO'] != "HDUO"] postes = postes.merge(postes_ocv, how='inner', left_on='p_id', right_on='Code Poste CREDO') return postes # Fonction qui concatène l'EIP et la formation d'emploi pour avoir un champ unique de poste def concat_EIP_FE(x): """ Construit la table des postes à partir du fichier REO homologué :type x: DataFrame :param x: contient les colonnes a_eip et formationsEmploi_id à concaténer :return: - **dataframe** (*dataframe*): DataFrame avec un champ supplémentaire comportant l'information des deux colonnes. """ str_a_eip = str(x["a_eip"]) eip = str_a_eip[0:3] + str_a_eip[4:7] + str_a_eip[8:] fe = str(x["formation_emploi_id"]) return eip + '_' + fe # Fonction pour la table FMOB de fichier FMOB def to_table_fmob_fmob(FMOB): """ Construit la table FMOB à partir des tables FMOB. :type FMOB: DataFrame :param FMOB: table FMOB :return: - **fmob** (*dataframe*): Tableau créé contenant les information de FMOB provenant de FMOB. """ # import du fichier contenant les données logger.info("----- Début de l'extraction du fichier FMOB pour la table FMOB ----- ") logger.info('Nombre total de lignes du fichier FMOB : %s', FMOB.shape[0]) FMOB.dropna(subset=["Matricule SAP", "Millésime FMOB"], axis=0, inplace=True) logger.info("Nombre total de lignes du fichier FMOB après la suppression des cellules vides dans les colonnes Matricule SAP et Millésime FMOB: %s", FMOB.shape[0]) FMOB.drop_duplicates(subset=["Matricule SAP"], inplace=True) logger.info('Nombre total de lignes du fichier FMOB après suppression des doublons dans la colonne Matricule SAP : %s', FMOB.shape[0]) FMOB.reset_index(inplace=True, drop=True) # sélection et renommage des champs try: # ADD col_fmob = ["Matricule SAP", "Millésime FMOB", "Annulation FMOB","Avis CDC à la mutation de l'administré","Avis CDC Mobilité bassin externe","Avis CDC Mobilité bassin interne", "Avis CDC Mobilité centre intérêt ADT","Avis CDC Mobilité dans la spécialité","Avis CDC Mobilité hors métropole","Avis CDC Mobilité recrutement particulier administré","Sans Suite FMOB", "Date_Deb FMOB", "Date_Fin FMOB","Date de signature de l'administré", "Date de signature du chef de corps", "Date de VISA du militaire", "Départ de l'institution SOFF","Mobilité bassin externe","Mobilité bassin interne","Mobilité centre intérêt ADT","Mobilité dans la spécialité", "Mobilité hors métropole","Mobilité recrutement particulier administré","Motif d'édition LA", "Reconnaissance parcours pro administré", "Remarques éventuelles de l'intéressé","Avis du commandant de formation", 'Réception DRHAT FMOB'] fmob = FMOB[col_fmob] except Exception as e: raise Exception("Erreur lors de l'extraction du fichier FMOB : les noms de colonnes du fichier ne sont pas corrects") from e fmob['fmob_id'] = fmob.index #changer le fmob_id # ADD & rename fmob.rename(columns={'Matricule SAP': 'administre_id', 'Millésime FMOB': 'fmob_millesime', 'Annulation FMOB': "fmob_annulation_fmob", "Avis CDC à la mutation de l'administré":"fmob_avis_cdc_mutation_administre", "Avis CDC Mobilité bassin externe":"fmob_avis_cdc_mobilite_externe", "Avis CDC Mobilité bassin interne":"fmob_avis_cdc_mobilite_interne", "Avis CDC Mobilité centre intérêt ADT":"fmob_avis_cdc_mobilite_centre_interet", "Avis CDC Mobilité dans la spécialité":"fmob_avis_cdc_mobilite_specialite", "Avis CDC Mobilité hors métropole":"fmob_avis_cdc_mobilite_hors_metropole", "Avis CDC Mobilité recrutement particulier administré":"fmob_avis_cdc_mobilite_recrutement_particulier_admin", 'Sans Suite FMOB': "fmob_sans_suite_fmob", 'Date de VISA du militaire': "fmob_date_visa_militaire", "Départ de l'institution SOFF": "fmob_depart_institution_soff", "Mobilité bassin externe":"fmob_mobilite_bassin_externe", "Mobilité bassin interne":"fmob_mobilite_bassin_interne", "Mobilité centre intérêt ADT":"fmob_mobilite_centre_interet_adt", "Mobilité dans la spécialité":"fmob_mobilite_dans_specialite", "Mobilité hors métropole":"fmob_mobilite_hors_metropole", "Mobilité recrutement particulier administré":"fmob_mobilite_recrutement_particulier_administre", 'Date_Deb FMOB': "fmob_date_deb_fmob", 'Date_Fin FMOB': "fmob_date_fin_fmob", 'Réception DRHAT FMOB' : "fmob_reception_drhat_fmob", "Date de signature de l'administré": "fmob_date_signature_admin_fmob", "Date de signature du chef de corps": "fmob_date_signature_chef_de_corps", "Motif d'édition LA": "fmob_motif_edition_la", "Reconnaissance parcours pro administré": "fmob_reconnaissance_parcours_pro_administre", "Remarques éventuelles de l'intéressé" : "fmob_remarques_eventuelles_administres", "Avis du commandant de formation" : "fmob_avis_commandant_formation", }, inplace=True, errors='raise') # Check if O or N c_x = ['fmob_annulation_fmob', 'fmob_sans_suite_fmob','fmob_reconnaissance_parcours_pro_administre','fmob_reception_drhat_fmob'] for c in c_x: fmob[c] = fmob[c].apply(lambda x: True if x == 'X ' else False) #fmob['fmob_depart_institution_soff'] = fmob['fmob_depart_institution_soff'].apply(lambda x: True if x == 'O' else False) # ADD and check O or N : c_o = ['fmob_depart_institution_soff','fmob_avis_cdc_mutation_administre','fmob_avis_cdc_mobilite_externe','fmob_avis_cdc_mobilite_interne', 'fmob_avis_cdc_mobilite_centre_interet','fmob_avis_cdc_mobilite_specialite','fmob_avis_cdc_mobilite_hors_metropole', 'fmob_avis_cdc_mobilite_recrutement_particulier_admin','fmob_mobilite_bassin_externe','fmob_mobilite_bassin_interne', 'fmob_mobilite_centre_interet_adt','fmob_mobilite_dans_specialite','fmob_mobilite_hors_metropole', 'fmob_mobilite_recrutement_particulier_administre'] for c in c_o: fmob[c] = fmob[c].apply(lambda x: True if x == 'O ' else False) #fmob['fmob_depart_institution_soff'] = fmob['fmob_depart_institution_soff'].apply(lambda x: True if x == 'O' else False) fmob = (fmob.fillna(APP_NAN) .replace({APP_NAN: None})) # Colonne Statut FMOB conditions = [ (fmob['fmob_annulation_fmob'] == True), (fmob['fmob_sans_suite_fmob'] == True), (fmob['fmob_reception_drhat_fmob'] == False), (fmob['fmob_reception_drhat_fmob'] == True), ] values = ['Annulé', 'Classé sans suite', 'Non réceptionné', 'Réceptionné'] fmob['fmob_statut'] = np.select(conditions, values) logger.info("----- Fin de l'extraction du fichier FMOB pour la table FMOB -----") return fmob # Fonction pour la table FMOB du fichier FEMP def to_table_fmob_femp(FEMP): """ Construit la table FMOB à partir des tables FEMP. :type FEMP: DataFrame :param FEMP: table FEMP :return: - **femp** (*dataframe*): Tableau créé contenant les information de FMOB provenant de FEMP. """ # import du fichier contenant les données logger.info("----- Début de l'extraction du fichier FEMP pour la table FMOB ----- ") logger.info('Nombre total de lignes du fichier FEMP : %s', FEMP.shape[0]) FEMP.dropna(subset=["Matricule SAP", "Date_Deb FEMP"], axis=0, inplace=True) logger.info("Nombre total de lignes du fichier FEMP après la suppression des cellules vides dans les colonnes Matricule SAP et Date_Deb FEMP: %s", FEMP.shape[0]) FEMP.drop_duplicates(subset=["Matricule SAP"], inplace=True) logger.info('Nombre total de lignes du fichier FEMP après suppression des doublons dans la colonne Matricule SAP : %s', FEMP.shape[0]) FEMP.reset_index(inplace=True, drop=True) # sélection et renommage des champs try: col_femp = ["Matricule SAP", "Annulation FEMP", "Sans Suite FEMP", "Date de signature de l'administré", "Proposition d'affectation.Verrouillé","Commentaires AC","Millésime FEMP"] femp = FEMP[col_femp] except Exception as e: raise Exception("Erreur lors de l'extraction du fichier FEMP : les noms de colonnes du fichier ne sont pas corrects") from e femp.rename(columns={FmobCols.ID_SAP.value: "administre_id", "Annulation FEMP": "fmob_annulation_femp", "Proposition d'affectation.Verrouillé": "fmob_proposition_affectation_verrouille", "Sans Suite FEMP": "fmob_sans_suite_femp", "Date de signature de l'administré": "fmob_date_signature_admin_femp", "Commentaires AC" : "fmob_commentaire_ac", "Millésime FEMP":"fmob_millesime_femp"}, inplace=True, errors='raise') c_x = ['fmob_annulation_femp', 'fmob_sans_suite_femp', "fmob_proposition_affectation_verrouille"] for c in c_x: femp[c] = femp[c].apply(lambda x: True if x == 'X ' else False) femp = (femp.fillna(APP_NAN) .replace({APP_NAN: None})) logger.info("----- Fin de l'extraction du fichier FEMP pour la table FMOB -----") return femp # Fonction pour la table Preference list def to_table_liste_preference(pam_id,sv_id): """ Construit la table preferences en utilisant le sous-vivier id. :type sv_id: entier :param sv_id: ID du sous-vivier :return: - **new_souhaits_formates** (*dataframe*): thTableau créé contenant les preferences des administres. """ # Lire tous les administres avec les souhaits dans le sous_viviers et l'annee du Pam avec AVIS A_MUTER ou A Etudier statut_administre = [StatutPam.A_MUTER, StatutPam.A_ETUDIER] souhaits_columns = ('id','administre__a_id_sap', 'administre__a_nf_futur', 'administre__a_filiere_futur_id','a_liste_depts_souhaites_pam','pam_id') souhaits_columns_name = ('administre_pam_id','a_id_sap', 'a_nf_futur', 'a_filiere_futur_id','a_liste_depts_souhaites_pam','pam_id') souhaits_db = Administres_Pams.objects.filter(pam_id=pam_id, administre__sous_vivier_id=sv_id, a_statut_pam_annee__in=statut_administre, decision__de_decision__isnull=True).values_list(*souhaits_columns) if not souhaits_db.exists : logger.debug(f"Pas d'administrés avec le bon statut {statut_administre} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun administrés avec le bon statut {statut_administre} dans le sous vivier {sv_id} et le pam {pam_id}.") souhaits = pd.DataFrame.from_records(souhaits_db, columns = souhaits_columns_name) # Remplacement des None par un None connue souhaits = souhaits.replace('None', np.nan) # Remplacement du Vide par un None connue souhaits = souhaits.replace('', np.nan) # Lire tous les postes dans le sous_viviers et l'annee du Pam avec AVIS P1 ou P2 ou P3 ou P4 statut_poste = [AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4] poste_columns = ('id','poste__p_id', 'poste__p_dep','poste__p_filiere_id','poste__p_nf','poste__p_eip','p_pam_id') poste_columns_name = ('poste_pam_id','poste_id', 'p_dep', 'p_filiere_id','p_nf','p_eip','pam_id') postes_db = Postes_Pams.objects.filter(Q(p_pam_id=pam_id) & Q(poste__sous_viviers=sv_id) & Q(decisions__de_decision__isnull=True) & Q(p_avis_pam__in=statut_poste)).values_list(*poste_columns) if not postes_db.exists : logger.debug(f"Pas de postes avec le bon statut {statut_poste} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun postes avec le bon statut {statut_administre} dans le sous vivier {sv_id} et le pam {pam_id}.") postes = pd.DataFrame.from_records(postes_db, columns=poste_columns_name) len_administres_traites = len(souhaits) len_postes_traites = len(postes) logger.debug('Nb admin traités : ' + str(len_administres_traites)) logger.debug('Nb groupe_poste traités : ' + str(len_postes_traites)) # Supprimer tous les administrateurs sans souhaits souhaits_nonull = souhaits.dropna(subset=["a_liste_depts_souhaites_pam"], axis=0) souhaits_null = souhaits[~souhaits.index.isin(souhaits_nonull.index)]['a_id_sap'] len_administres_restants = len(souhaits_nonull) logger.debug('Nb admin sans souhaits : ' + str(len_administres_traites - len_administres_restants)) logger.debug('Les administrés sans souhaits sont : ' + str(list(souhaits_null))) if len_administres_restants == 0: logger.debug(f"Pas d'administrés avec des souhaits dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun administrés n'a renseigné de souhaits - Veuillez vérifier que (Domaine/Filière/NF) et/ou (départements) entre Postes et Administrés soient les mêmes (pas de matching parfait).") souhaits_nonull = souhaits_nonull.reset_index(drop=True) souhaits_nonull['departement'] = souhaits_nonull['a_liste_depts_souhaites_pam'].apply(lambda x : x.strip().split(',')) souhaits_nonull = souhaits_nonull.explode('departement', ignore_index = True) souhaits_nonull['departement'] = souhaits_nonull['departement'].replace('AUTRE_ETRANGER', '0') souhaits_poste = souhaits_nonull.merge(postes, how='inner', left_on=['departement', 'a_filiere_futur_id', 'a_nf_futur'], right_on=['p_dep', 'p_filiere_id', 'p_nf']) if souhaits_poste.empty: logger.debug(f"Aucun poste ne correspond au souhait du département des administrés dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun poste ne correspond au souhait des administrés dans le sous vivier - Veuillez vérifier que (Domaine/Filière/NF) et/ou (départements) entre Postes et Administrés soient les mêmes (pas de matching parfait).") souhaits_poste = souhaits_poste.dropna(subset=['p_eip']) souhaits_poste = souhaits_poste.reset_index(drop=True) a_id_sap_groupe = souhaits_poste['a_id_sap'].unique() new_souhaits_formates = pd.DataFrame(columns=['administre_pam_id', 'a_id_sap', 'departement', 'poste_id', 'poste_pam_id']) for a in a_id_sap_groupe: df = souhaits_poste[souhaits_poste['a_id_sap'] == a][ ['administre_pam_id','a_id_sap', 'departement', 'poste_id', 'poste_pam_id']] df.reset_index(drop=False, inplace=True) df['lp_rang_poste'] = df.index + 1 new_souhaits_formates = pd.concat([new_souhaits_formates, df], ignore_index=True) # comment choisir l'id ? new_souhaits_formates['lp_id'] = new_souhaits_formates.index new_souhaits_formates['pam_id'] = pam_id new_souhaits_formates = new_souhaits_formates.rename(columns={'a_id_sap': 'administre_id'}) logger.debug('Ranking ending') return new_souhaits_formates def to_table_liste_preference_selectif(sv_id,pam_id,l_a_id,l_p_id): """ Construit la table preferences en utilisant le sous-vivier id. :type sv_id: entier :param sv_id: ID du sous-vivier :return: - **new_souhaits_formates** (*dataframe*): thTableau créé contenant les preferences des administres. """ # Lire tous les administres avec les souhaits dans le sous_viviers et l'annee du Pam avec AVIS A_MUTER ou A Etudier statut_administre = [StatutPam.A_MUTER, StatutPam.A_ETUDIER] souhaits_columns = ('id','administre__a_id_sap', 'administre__a_nf_futur', 'administre__a_filiere_futur_id','a_liste_depts_souhaites_pam','pam_id') souhaits_columns_name = ('administre_pam_id','a_id_sap', 'a_nf_futur', 'a_filiere_futur_id','a_liste_depts_souhaites_pam','pam_id') souhaits_db = Administres_Pams.objects.filter(pam_id=pam_id, administre__sous_vivier_id=sv_id, a_statut_pam_annee__in=statut_administre, administre_id__in=l_a_id, decision__de_decision__isnull=True).values_list(*souhaits_columns) if not souhaits_db.exists : logger.debug(f"Pas d'administrés avec le bon statut {statut_administre} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun administrés avec le bon statut {statut_administre} dans le sous vivier {sv_id} et le pam {pam_id}.") souhaits = pd.DataFrame.from_records(souhaits_db, columns = souhaits_columns_name) # Remplacement des None par un None connue souhaits = souhaits.replace('None', np.nan) # Remplacement du Vide par un None connue souhaits = souhaits.replace('', np.nan) # Lire tous les postes dans le sous_viviers et l'annee du Pam avec AVIS P1 ou P2 ou P3 ou P4 statut_poste = [AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4] poste_columns = ('id','poste__p_id', 'poste__p_dep','poste__p_filiere_id','poste__p_nf','poste__p_eip','p_pam_id') poste_columns_name = ('poste_pam_id','poste_id', 'p_dep', 'p_filiere_id','p_nf','p_eip','pam_id') postes_db = Postes_Pams.objects.filter(Q(p_pam_id=pam_id) & Q(poste__sous_viviers=sv_id) & Q(decisions__de_decision__isnull=True) & Q(poste_id__in=l_p_id) & Q(p_avis_pam__in=statut_poste)).values_list(*poste_columns) if not postes_db.exists : logger.debug(f"Pas de postes avec le bon statut {statut_poste} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun postes avec le bon statut {statut_administre} dans le sous vivier {sv_id} et le pam {pam_id}.") postes = pd.DataFrame.from_records(postes_db, columns=poste_columns_name) len_administres_traites = len(souhaits) len_postes_traites = len(postes) logger.debug('Nb admin traités : ' + str(len_administres_traites)) logger.debug('Nb groupe_poste traités : ' + str(len_postes_traites)) # Supprimer tous les administrateurs sans souhaits souhaits_nonull = souhaits.dropna(subset=["a_liste_depts_souhaites_pam"], axis=0) souhaits_null = souhaits[~souhaits.index.isin(souhaits_nonull.index)]['a_id_sap'] len_administres_restants = len(souhaits_nonull) logger.debug('Nb admin sans souhaits : ' + str(len_administres_traites - len_administres_restants)) logger.debug('Les administrés sans souhaits sont : ' + str(list(souhaits_null))) if len_administres_restants == 0: logger.debug(f"Pas d'administrés avec des souhaits dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun administrés n'a renseigné de souhaits - Veuillez vérifier que (Domaine/Filière/NF) et/ou (départements) entre Postes et Administrés soient les mêmes (pas de matching parfait).") souhaits_nonull = souhaits_nonull.reset_index(drop=True) souhaits_nonull['departement'] = souhaits_nonull['a_liste_depts_souhaites_pam'].apply(lambda x : x.strip().split(',')) souhaits_nonull = souhaits_nonull.explode('departement', ignore_index = True) souhaits_nonull['departement'] = souhaits_nonull['departement'].replace('AUTRE_ETRANGER', '0') souhaits_poste = souhaits_nonull.merge(postes, how='inner', left_on=['departement', 'a_filiere_futur_id', 'a_nf_futur'], right_on=['p_dep', 'p_filiere_id', 'p_nf']) if souhaits_poste.empty: logger.debug(f"Aucun poste ne correspond au souhait du département des administrés dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun poste ne correspond au souhait des administrés dans le sous vivier - Veuillez vérifier que (Domaine/Filière/NF) et/ou (départements) entre Postes et Administrés soient les mêmes (pas de matching parfait).") souhaits_poste = souhaits_poste.dropna(subset=['p_eip']) souhaits_poste = souhaits_poste.reset_index(drop=True) a_id_sap_groupe = souhaits_poste['a_id_sap'].unique() new_souhaits_formates = pd.DataFrame(columns=['administre_pam_id', 'a_id_sap', 'departement', 'poste_id', 'poste_pam_id']) for a in a_id_sap_groupe: df = souhaits_poste[souhaits_poste['a_id_sap'] == a][ ['administre_pam_id','a_id_sap', 'departement', 'poste_id', 'poste_pam_id']] df.reset_index(drop=False, inplace=True) df['lp_rang_poste'] = df.index + 1 new_souhaits_formates = pd.concat([new_souhaits_formates, df], ignore_index=True) # comment choisir l'id ? new_souhaits_formates['lp_id'] = new_souhaits_formates.index new_souhaits_formates['pam_id'] = pam_id new_souhaits_formates = new_souhaits_formates.rename(columns={'a_id_sap': 'administre_id'}) logger.debug('Ranking ending') return new_souhaits_formates