init
This commit is contained in:
424
backend-django/backend/utils_scoring.py
Normal file
424
backend-django/backend/utils_scoring.py
Normal file
@@ -0,0 +1,424 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user