Talents_mapping / app.py
Eric2mangel's picture
Upload app.py
8725533 verified
import streamlit as st
import pandas as pd
import plotly.express as px
import json
import numpy as np
METIERS_CIBLES = [
{
"titre": "Data Engineer",
"requis": [
{"domaine": "Data", "methode": "Architecture", "outil": "SQL"},
{"domaine": "Informatique", "methode": "Développement", "outil": "Python"},
{"domaine": "Informatique", "methode": "Cloud", "outil": "AWS"}
]
},
{
"titre": "Responsable Transformation Digitale",
"requis": [
{"domaine": "Gestion", "methode": "Agilité", "outil": "Scrum"},
{"domaine": "Marketing", "methode": "Analyse", "outil": "Google Analytics"},
{"domaine": "Data", "methode": "Visualisation", "outil": "Power BI"}
]
},
{
"titre": "Product Owner",
"requis": [
{"domaine": "Gestion", "methode": "Agilité", "outil": "Jira"},
{"domaine": "Gestion", "methode": "Planning", "outil": "Trello"},
{"domaine": "Marketing", "methode": "Stratégie Digitale", "outil": "Figma"}
]
},
{
"titre": "Analyste Cybersécurité",
"requis": [
{"domaine": "Informatique", "methode": "Sécurité", "outil": "Firewalls"},
{"domaine": "Informatique", "methode": "Cloud", "outil": "Docker"},
{"domaine": "Informatique", "methode": "Scripting", "outil": "Bash"}
]
}
]
# --- 2. FONCTIONS DE CALCUL ---
def calculer_score(candidat, poste):
score_total = 0
max_score_possible = len(poste['requis']) * 3
for req in poste['requis']:
points_competence = 0
for comp in candidat['competences']:
if comp['outil'].lower() == req['outil'].lower():
points_competence = max(points_competence, 3)
elif comp['methode'].lower() == req['methode'].lower():
points_competence = max(points_competence, 2)
elif comp['domaine'].lower() == req['domaine'].lower():
points_competence = max(points_competence, 1)
score_total += points_competence
return (score_total / max_score_possible) * 100
# --- 3. INTERFACE PRINCIPALE ---
def main():
st.set_page_config(page_title="Skill Mapping MVP", layout="wide")
# --- VERROU DE SÉCURITÉ SÉCURISÉ ---
if "authenticated" not in st.session_state:
st.session_state["authenticated"] = False
if not st.session_state["authenticated"]:
st.title("🔐 Accès Restreint")
password_input = st.text_input("Veuillez saisir le code d'accès :", type="password")
if st.button("Se connecter"):
# On compare la saisie avec le secret caché sur Hugging Face
if password_input == st.secrets["MY_PASSWORD"]:
st.session_state["authenticated"] = True
st.rerun()
else:
st.error("Code incorrect.")
return
st.title("🎯 Mapping des talents Internes")
try:
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
collaborateurs = data['collaborateurs']
except FileNotFoundError:
st.error("Fichier 'data.json' non trouvé.")
return
# --- SIDEBAR & SÉLECTION ---
st.sidebar.header("Paramètres")
titre_metier = st.sidebar.selectbox("Poste cible :", [m['titre'] for m in METIERS_CIBLES])
poste_data = next(m for m in METIERS_CIBLES if m['titre'] == titre_metier)
# Calcul des scores (pré-traitement)
resultats = []
for c in collaborateurs:
score = calculer_score(c, poste_data)
resultats.append({
"Nom": c['nom'],
"Métier Actuel": c['metier_actuel'],
"Score de Match": round(score, 1),
"data_brute": c
})
df = pd.DataFrame(resultats).sort_values(by="Score de Match", ascending=False)
# --- CRÉATION DES ONGLETS ---
tab1, tab2, tab3 = st.tabs(["📊 Classement", "🌌 Cartographie 2D", "🔍 Analyse Individuelle"])
# --- ONGLET 1 : CLASSEMENT ---
with tab1:
st.subheader(f"Adéquation des profils pour : {titre_metier}")
st.dataframe(
df[["Nom", "Métier Actuel", "Score de Match"]],
column_config={
"Score de Match": st.column_config.ProgressColumn(
"Adéquation",
format="%.1f%%",
min_value=0,
max_value=100
),
},
hide_index=True,
use_container_width=True
)
# --- ONGLET 2 : MAPPING 2D ---
with tab2:
st.subheader("Positionnement par rapport au poste cible")
df_map = df.copy()
# Calcul des coordonnées
angles = np.linspace(0, 2 * np.pi, len(df_map), endpoint=False)
df_map['distance'] = 100 - df_map['Score de Match']
df_map['x'] = df_map['distance'] * np.cos(angles)
df_map['y'] = df_map['distance'] * np.sin(angles)
# Création du graphique de base
fig_2d = px.scatter(
df_map, x='x', y='y', text='Nom',
color='Score de Match',
color_continuous_scale="RdYlGn",
range_color=[0, 100],
template="plotly_white"
)
# Ajout manuel du point CIBLE au centre pour être sûr qu'il soit là
fig_2d.add_scatter(
x=[0], y=[0], mode='markers+text', text=['🎯 CIBLE'],
marker=dict(size=20, color='black'), name='Cible'
)
# Amélioration du texte et des marqueurs
fig_2d.update_traces(
textposition='top center',
marker=dict(size=12, line=dict(width=1, color='DarkSlateGrey'))
)
# Ajout des cercles de radar DERRIÈRE les points
for r in [25, 50, 75, 100]:
fig_2d.add_shape(
type="circle", x0=-r, y0=-r, x1=r, y1=r,
line=dict(color="lightgray", width=1, dash="dot"),
layer="below" # <--- IMPORTANT : Force les cercles en arrière-plan
)
fig_2d.update_layout(
showlegend=False, height=700,
xaxis=dict(visible=False, range=[-110, 110]),
yaxis=dict(visible=False, range=[-110, 110])
)
st.plotly_chart(fig_2d, use_container_width=True)
# --- ONGLET 3 : FOCUS CANDIDAT (ANALYSE DES ÉCARTS) ---
with tab3:
st.subheader("Détail des écarts par collaborateur")
# Sélection du candidat
nom_selectionne = st.selectbox(
"Choisir un collaborateur à analyser :",
df['Nom'].tolist()
)
cand = next(c for c in collaborateurs if c['nom'] == nom_selectionne)
st.markdown(f"**Métier actuel :** `{cand['metier_actuel']}`")
st.write(f"**Score de correspondance :** {df.loc[df['Nom'] == nom_selectionne, 'Score de Match'].values[0]}%")
st.divider() # Petite ligne de séparation
col_left, col_right = st.columns(2)
with col_left:
st.markdown(f"### ✅ Compétences actuelles")
for comp in cand['competences']:
st.markdown(f"- **{comp['outil']}** \n *{comp['domaine']} > {comp['methode']}*")
with col_right:
st.markdown("### 🚩 Comparaison avec le besoin")
for req in poste_data['requis']:
match_outil = any(c['outil'].lower() == req['outil'].lower() for c in cand['competences'])
match_methode = any(c['methode'].lower() == req['methode'].lower() for c in cand['competences'])
if match_outil:
st.success(f"**{req['outil']}** : Maîtrisé")
elif match_methode:
st.warning(f"**{req['outil']}** : Partiel (connaît la méthode '{req['methode']}')")
else:
st.error(f"**{req['outil']}** : Manquant")
if __name__ == "__main__":
main()