""" Ce module contient les Utilitaires de scoring """ # import des pré requis import json from datetime import date from django.db.models import Q import time import pandas as pd import numpy as np from django.utils import timezone from .models import Administre, Domaine, FMOB, Fonction, FormationEmploi, Garnison, Grade, Poste, Notation, Postes_Pams, \ PreferencesListe, Marque, MarquesGroupe, Filiere, Competence, StatutPamChoices, AvisPosteChoices as AvisPoste, \ Administres_Pams from .utils.logging import get_logger import itertools logger = get_logger(__name__) # calcul de la date today = date.today() # Fonction de lancement du preprocessing et d'encoding def encoding(a_fil, a_nf, a_comp, p_fil, p_nf, p_comp): """ Retourne l'encodage de l' "eip + département" par rapport à un poste donné :type a_fil: numpy list :param a_fil: filière de l'eip actuel militaire :type a_nf: numpy list :param a_nf: nf de l'eip actuel militaire :type a_comp: ? :param a_comp: ? :type p_fil: numpy list :param p_fil: filière du poste vacant :type p_nf: numpy list :param p_nf: nf du poste vacant :type p_comp: ? :param p_comp: ? :return: - **encoding** (*numpy list*): encoding de l'eip actuel du militaire et du département où il souhaite travailler par rapport à un poste vacant """ b_fil = (a_fil == p_fil).astype(int) b_nf = (a_nf == p_nf).astype(int) b_comp = [len(list(filter(i.__contains__, j)))/len(j) if j!=[None] else 0 for i,j in zip(a_comp,p_comp)] logger.debug('Calcul des compétences') encoding = (b_fil, b_nf, b_comp) return encoding # fonction de calcul du scores def notePonderee(a_fil, a_nf, a_comp, p_fil, p_nf, p_comp, p_poids_fil, p_poids_nf, p_poids_comp): """ Retourne la note d'une combinaison eip+département par rapport à un poste donné :type a_dom: numpy list :param a_dom: domaine de l'eip actuel du militaire :type a_fil: numpy list :param a_fil: filière de l'eip actuel militaire :type a_dep: numpy list :param a_dep: département où souhaite travailler le militaire :type a_nf: numpy list :param a_nf: nf de l'eip actuel militaire :type p_dom: numpy list :param p_dom: domaine du poste vacant :type p_fil: numpy list :param p_fil: filière du poste vacant :type p_dep: numpy list :param p_dep: département du poste vacant :type p_nf: numpy list :param p_nf: nf du poste vacant :type p_poids_dom: numpy list :param p_poids_dom: poids du domaine :type p_poids_fil: numpy list :param p_poids_fil: poids de la filière :type p_poids_dep: numpy list :param p_poids_dep: poids du département :type p_poids_nf: numpy list :param p_poids_nf: poids du nf :return: - **note** (*float*): note de l'eip par rapport au poste à pourvoir """ e = encoding(a_fil, a_nf, a_comp, p_fil, p_nf, p_comp) note = p_poids_fil * e[0] + p_poids_nf * e[1] + p_poids_comp * e[2] return note def notations(pam_id, sv_id): """ Remplit la colonne note de la table Notations :type sv_id: char :param sv_id: sous vivier étudié :return: - **notations** (*dataframe*): table des notations avec les notes complétées """ # Sélection des postes sur lesquels aucune décision n'est prise logger.debug('start scoring') colonnes_pos = ('id','poste_id', 'poste__competences', 'poste__p_filiere_id', 'poste__p_nf', 'p_avis_pam') colonnes_pos_name = ('poste_pam_id','poste_id', 'competences', 'p_filiere_id', 'p_nf', 'p_avis_pam') statut_poste =[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4] postes = Postes_Pams.objects.values_list(*colonnes_pos).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)) if not postes.exists: logger.debug(f"Pas de poste avec le bon statut {statut_poste} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception("Aucun poste avec le bon statut.") pos = pd.DataFrame.from_records(postes, columns=colonnes_pos_name) pos=pos.groupby(['poste_pam_id','poste_id','p_filiere_id', 'p_nf', 'p_avis_pam'])['competences'].apply(list).reset_index(name='competences') # Remplire les poids des postes p_poids = ['p_poids_filiere', 'p_poids_nf', 'p_poids_competences'] try: pos['p_poids'] = pos.apply(lambda x: Poste.objects.get(p_id=x['poste_id']).p_poids_filiere_nf_competences, axis=1) pos[p_poids] = pd.DataFrame(pos['p_poids'].tolist(), index=pos.index) except Exception as e: raise Exception("Tous les postes sont déjà affectés à un adminstré : Veuillez en sélectionner de nouveau") # Sélection des administrés dont le statut PAM est "à muter" et sur lesquels aucune décision n'est prise colonnes_adm = ('id','administre__a_id_sap', 'administre__a_filiere_futur_id', 'administre__a_nf_futur', 'administre__a_liste_id_competences') colonnes_adm_name = ('administre_pam_id', 'a_id_sap', 'a_filiere_futur_id', 'a_nf_futur', 'a_liste_id_competences') statut = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER] administres = Administres_Pams.objects.filter(pam_id=pam_id, administre__sous_vivier_id=sv_id, a_statut_pam_annee__in=statut, decision__de_decision__isnull=True).values_list(*colonnes_adm) if not administres.exists: logger.debug(f"Pas de poste avec le bon statut {statut} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun administrés avec le bon statut.") adm = pd.DataFrame.from_records(administres, columns=colonnes_adm_name) if len(adm) == 0: raise Exception("Arret du scoring 0 administrés dont le statut PAM est à muter et sur lesquels aucune décision n'est prise") logger.debug('Nb admin traités : %s', len(adm)) logger.debug('Nb groupe_poste traités : %s', len(pos)) adm=adm.groupby(['administre_pam_id','a_id_sap','a_filiere_futur_id','a_nf_futur'])['a_liste_id_competences'].apply(list).reset_index(name='a_liste_id_competences') # Ajout de la colonne Key avec la valeur 1 aux deux bases de données, pour pouvoir effectuer la jointure croisée. adm['key'] = 1 pos['key'] = 1 # Jointure croisée et suppression de la colonne clé result = pd.merge(pos, adm, on='key').drop("key", 1) # Ajout de la date d'exécution now = timezone.now() result['no_date_execution'] = now # Calcul de la notePondéré result['no_score_administre'] = notePonderee(result['a_filiere_futur_id'].to_numpy(), result['a_nf_futur'].to_numpy(), result['a_liste_id_competences'].to_numpy(), result['p_filiere_id'].to_numpy(), result['p_nf'].to_numpy(), result['competences'].to_numpy(), result['p_poids_filiere'].to_numpy(), result['p_poids_nf'].to_numpy(), result['p_poids_competences'].to_numpy()) # Suppression de toutes les colonnes inutiles result.drop(result.columns.difference(['poste_pam_id','administre_pam_id','poste_id','a_id_sap','no_score_administre','no_date_execution']), 1, inplace=True) # Création de la colonne no_flag_cple_ideal result['no_flag_cple_ideal'] = False # Changer le nom des colonnes result = result.rename(columns={'a_id_sap': 'administre_id'}) result = result.astype({'administre_id': 'object', 'no_flag_cple_ideal': 'object'}) result['pam_id'] = pam_id logger.debug('end scoring') return result def notations_liste(sv_id, pam_id, l_a_id, l_p_id): """ Remplit la colonne note de la table Notations :type sv_id: char :param sv_id: sous vivier étudié :return: - **notations** (*dataframe*): table des notations avec les notes complétées """ # Sélection des postes sur lesquels aucune décision n'est prise logger.debug('start scoring') colonnes_pos = ('id','poste_id', 'poste__competences', 'poste__p_filiere_id', 'poste__p_nf', 'p_avis_pam') colonnes_pos_name = ('poste_pam_id','poste_id', 'competences', 'p_filiere_id', 'p_nf', 'p_avis_pam') statut_poste =[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4] postes = Postes_Pams.objects.values_list(*colonnes_pos).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) & Q(poste_id__in=l_p_id)) if not postes.exists: logger.debug(f"Pas de poste avec le bon statut {statut_poste} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception("Aucun poste avec le bon statut.") pos = pd.DataFrame.from_records(postes, columns=colonnes_pos_name) pos=pos.groupby(['poste_pam_id','poste_id','p_filiere_id', 'p_nf', 'p_avis_pam'])['competences'].apply(list).reset_index(name='competences') # Remplire les poids des postes p_poids = ['p_poids_filiere', 'p_poids_nf', 'p_poids_competences'] try: pos['p_poids'] = pos.apply(lambda x: Poste.objects.get(p_id=x['poste_id']).p_poids_filiere_nf_competences, axis=1) pos[p_poids] = pd.DataFrame(pos['p_poids'].tolist(), index=pos.index) except Exception as e: raise Exception("Tous les postes sont déjà affectés à un adminstré : Veuillez en sélectionner de nouveau") # Sélection des administrés dont le statut PAM est "à muter" et sur lesquels aucune décision n'est prise colonnes_adm = ('id','administre__a_id_sap', 'administre__a_filiere_futur_id', 'administre__a_nf_futur', 'administre__a_liste_id_competences') colonnes_adm_name = ('administre_pam_id', 'a_id_sap', 'a_filiere_futur_id', 'a_nf_futur', 'a_liste_id_competences') statut = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER] administres = Administres_Pams.objects.filter(pam_id=pam_id, administre__sous_vivier_id=sv_id, a_statut_pam_annee__in=statut,administre_id__in=l_a_id, decision__de_decision__isnull=True).values_list(*colonnes_adm) if not administres.exists: logger.debug(f"Pas de poste avec le bon statut {statut} dans le sous vivier {sv_id} et le pam {pam_id}.") raise Exception(f"Aucun administrés avec le bon statut.") adm = pd.DataFrame.from_records(administres, columns=colonnes_adm_name) if len(adm) == 0: raise Exception("Arret du scoring 0 administrés dont le statut PAM est à muter et sur lesquels aucune décision n'est prise") logger.debug('Nb admin traités : %s', len(adm)) logger.debug('Nb groupe_poste traités : %s', len(pos)) adm=adm.groupby(['administre_pam_id','a_id_sap','a_filiere_futur_id','a_nf_futur'])['a_liste_id_competences'].apply(list).reset_index(name='a_liste_id_competences') # Ajout de la colonne Key avec la valeur 1 aux deux bases de données, pour pouvoir effectuer la jointure croisée. adm['key'] = 1 pos['key'] = 1 # Jointure croisée et suppression de la colonne clé result = pd.merge(pos, adm, on='key').drop("key", 1) # Ajout de la date d'exécution now = timezone.now() result['no_date_execution'] = now # Calcul de la notePondéré result['no_score_administre'] = notePonderee(result['a_filiere_futur_id'].to_numpy(), result['a_nf_futur'].to_numpy(), result['a_liste_id_competences'].to_numpy(), result['p_filiere_id'].to_numpy(), result['p_nf'].to_numpy(), result['competences'].to_numpy(), result['p_poids_filiere'].to_numpy(), result['p_poids_nf'].to_numpy(), result['p_poids_competences'].to_numpy()) # Suppression de toutes les colonnes inutiles result.drop(result.columns.difference(['poste_pam_id','administre_pam_id','poste_id','a_id_sap','no_score_administre','no_date_execution']), 1, inplace=True) # Création de la colonne no_flag_cple_ideal result['no_flag_cple_ideal'] = False # Changer le nom des colonnes result = result.rename(columns={'a_id_sap': 'administre_id'}) result = result.astype({'administre_id': 'object', 'no_flag_cple_ideal': 'object'}) result['pam_id'] = pam_id logger.debug('end scoring') return result # TODO:Revoir les entrées de la fonction notation_test # fonction notation pour le test def notations_test(): """ Remplit la colonne note de la table Notations :return: - **notations** (*dataframe*): table des notations avec les notes complétées """ # test dataframes p = pd.DataFrame( columns=["p_id", "fonction_id", "sous_vivier_id", "formation_emploi_id", "p_liste_id_marques", "p_eip", "p_date_fin", "p_flag_pam", "p_notes_gestionnaire", "p_notes_partagees", "p_statut_pam", "p_poids_domaine", "p_poids_filiere", "p_poids_nf", "p_poids_garnison", "p_gar"]) p['p_id'] = [1, 2, 3] p['fonction_id'] = [2, 4, 5] p['sous_vivier_id'] = [6, 7, 8] p['formation_emploi_id'] = [9, 5, 10] p['p_liste_id_marques'] = ["1_P1,1_P2,1_P4", "1_P1,2_C,1_P4", "2_T,1_P2,1_P4"] p['p_eip'] = ['MAIGMA3a', 'ADMAES3b', 'SICEDR3a'] p['p_date_fin'] = ['', '', ''] p['p_flag_pam'] = [1, 1, 0] p['p_gar'] = [13, 14, 18] p['p_notes_gestionnaire'] = ['', '', ''] p['p_notes_partagees'] = ['', '', ''] p['p_statut_pam'] = ['P4', 'P4', 'P4'] p['p_poids_domaine'] = [0.25, 0.25, 0.25] p['p_poids_filiere'] = [0.25, 0.25, 0.25] p['p_poids_nf'] = [0.25, 0.25, 0.25] p['p_poids_garnison'] = [0.25, 0.25, 0.25] a_bis = {'a_id_sap': [9984, 56754, 78905], 'formation_emploi_id': [2, 4, 6], 'fonction_id': [3, 78, 90], 'sous_vivier_id': [5, 8, 98], 'grade_id': [4, 9, 5], 'a_liste_id_marques': ['[,,,]', '[,,,]', '[,,,]'], 'a_nom': ['Nom_1', 'Nom_2', 'Nom_3'], 'a_prenom': ['Prenom_1', 'Prenom_2', 'Prenom_3'], 'a_sexe': ['M', 'F', 'M'], 'a_id_def': [4, 5, 6], 'a_eip': ['MAIGMA3a', 'ADMAES3b', 'SICEDR3a'], 'a_domaine': ['MAI', 'ADM', 'SIC'], 'a_filiere': ['GMA', 'AES', 'EDR'], 'a_nf': ['3a', '3b', '3a'], 'a_domaine_gestion': ['', '', ''], 'a_date_entree_service': ['', '', ''], 'a_gar': [13, 14, 18], 'a_arme': ['', '', ''], 'a_rg_origine_recrutement': ['', '', ''], 'a_date_naissance': ['', '', ''], 'a_diplome_HL': ['', '', ''], 'a_dernier_diplome': ['', '', ''], 'a_credo_fe': ['', '', ''], 'a_date_arrivee_fe': ['', '', ''], 'a_date_pos_statuaire': ['', '', ''], 'a_pos_statuaire': ['', '', ''], 'a_interuption_service': ['', '', ''], 'a_situation_fam': ['', '', ''], 'a_nombre_enfants': [2, 6, 9], 'a_eis': ['', '', ''], 'a_sap_conjoint': [987, 456, 908], 'a_flag_particulier': [0, 1, 0], 'a_flag_pam': [1, 0, 1], 'a_statut_pam': ['non etudié', 'affecté', 'affecté'], 'a_notes_gestionnaire': ['', '', ''], 'a_notes_partagees': ['', '', ''], 'a_eip_futur': ['', '', ''], 'a_affectation1': ['', '', ''], 'a_affectation2': ['', '', ''], 'a_affectation3': ['', '', ''] } a = pd.DataFrame(data=a_bis) notation = pd.DataFrame( columns=["poste_id", "administres_id", "pam_id", "no_date_execution", "no_score_administre", "no_flag_cple_ideal"]) for i in range(len(p)): p_id = p.loc[i, "p_id"] p_dom = p.loc[i, "p_eip"][0:3] p_fil = p.loc[i, "p_eip"][3:6] p_nf = p.loc[i, "p_eip"][6:] p_gar = p.loc[i, "p_gar"] p_poids_dom = 0.25 p_poids_fil = 0.25 p_poids_nf = 0.25 p_poids_gar = 0.25 for j in range(len(a)): a_id_sap = a.at[j, 'a_id_sap'] a_dom = a.at[j, 'a_eip'][0:3] a_fil = a.at[j, 'a_eip'][4:7] a_nf = a.at[j, 'a_eip'][8:] a_gar = a.at[j, 'a_gar'] n = notePonderee(a_dom, a_fil, a_nf, a_gar, p_dom, p_fil, p_nf, p_gar, p_poids_dom, p_poids_fil, p_poids_nf, p_poids_gar) new_row = {'administres_id': a_id_sap, 'poste_id': p_id, 'no_date_execution': '14/06/2021', 'no_note_militaire': n, "no_flag_cple_ideal": 0 } notation = notation.append(new_row, ignore_index=True) return notation