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

425 lines
17 KiB
Python

"""
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