Spaces:
Sleeping
Sleeping
import json | |
import re | |
import logging | |
from datetime import datetime | |
from typing import Dict, List, Any | |
logger = logging.getLogger(__name__) | |
class SimpleScoringAgent: | |
def calculate_scores(self, candidat_data: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]: | |
if not candidat_data or not isinstance(candidat_data, dict): | |
return {"analyse_competences": []} | |
skills_data = candidat_data.get("compétences", {}) | |
skills_list = self._extract_skills_list(skills_data) | |
if not skills_list: | |
return {"analyse_competences": []} | |
skill_analysis = [] | |
for skill in skills_list: | |
level = self._determine_skill_level(skill, candidat_data) | |
skill_analysis.append({ | |
"skill": skill, | |
"level": level | |
}) | |
return {"analyse_competences": skill_analysis} | |
def _extract_skills_list(self, skills_data: Dict[str, Any]) -> List[str]: | |
"""Extrait la liste des compétences""" | |
skills_list = [] | |
if isinstance(skills_data, dict): | |
skills_list.extend(skills_data.get("hard_skills", [])) | |
skills_list.extend(skills_data.get("soft_skills", [])) | |
elif isinstance(skills_data, list): | |
skills_list = [item.get("nom") for item in skills_data if item.get("nom")] | |
return [skill for skill in skills_list if skill and isinstance(skill, str) and skill.strip()] | |
def _determine_skill_level(self, skill: str, candidat_data: Dict[str, Any]) -> str: | |
"""Détermine le niveau d'une compétence selon des règles simples""" | |
frequency = self._count_skill_mentions(skill, candidat_data) | |
max_duration = self._get_max_duration_for_skill(skill, candidat_data) | |
has_pro_experience = self._has_professional_experience(skill, candidat_data) | |
# Règles simples de classification | |
if has_pro_experience and max_duration >= 3.0: | |
return "expert" | |
elif has_pro_experience and max_duration >= 1.0: | |
return "avance" | |
elif frequency >= 3 or max_duration >= 0.5: | |
return "intermediaire" | |
else: | |
return "debutant" | |
def _count_skill_mentions(self, skill: str, candidat_data: Dict[str, Any]) -> int: | |
"""Compte le nombre de mentions de la compétence""" | |
skill_lower = skill.lower() | |
total_mentions = 0 | |
# Recherche dans toutes les sections | |
all_text = self._get_all_text_content(candidat_data).lower() | |
total_mentions = all_text.count(skill_lower) | |
return total_mentions | |
def _get_max_duration_for_skill(self, skill: str, candidat_data: Dict[str, Any]) -> float: | |
"""Trouve la durée maximum d'utilisation de la compétence""" | |
skill_lower = skill.lower() | |
max_duration = 0.0 | |
experiences_key = "expériences" if "expériences" in candidat_data else "experiences_professionnelles" | |
experiences = candidat_data.get(experiences_key, []) | |
if not isinstance(experiences, list): | |
return 0.0 | |
for exp in experiences: | |
if not isinstance(exp, dict): | |
continue | |
exp_text = json.dumps(exp, ensure_ascii=False).lower() | |
if skill_lower in exp_text: | |
duration = self._calculate_experience_duration(exp) | |
max_duration = max(max_duration, duration) | |
return max_duration | |
def _has_professional_experience(self, skill: str, candidat_data: Dict[str, Any]) -> bool: | |
"""Vérifie si la compétence a été utilisée en contexte professionnel""" | |
skill_lower = skill.lower() | |
experiences_key = "expériences" if "expériences" in candidat_data else "experiences_professionnelles" | |
experiences = candidat_data.get(experiences_key, []) | |
if not isinstance(experiences, list): | |
return False | |
for exp in experiences: | |
if not isinstance(exp, dict): | |
continue | |
exp_text = json.dumps(exp, ensure_ascii=False).lower() | |
if skill_lower in exp_text: | |
return True | |
return False | |
def _get_all_text_content(self, candidat_data: Dict[str, Any]) -> str: | |
"""Récupère tout le contenu textuel du CV""" | |
all_content = [] | |
# Expériences | |
experiences_key = "expériences" if "expériences" in candidat_data else "experiences_professionnelles" | |
for exp in candidat_data.get(experiences_key, []): | |
if isinstance(exp, dict): | |
all_content.append(json.dumps(exp, ensure_ascii=False)) | |
# Projets | |
projects = candidat_data.get("projets", {}) | |
if isinstance(projects, dict): | |
for project_type in ["professional", "personal"]: | |
for project in projects.get(project_type, []): | |
if isinstance(project, dict): | |
all_content.append(json.dumps(project, ensure_ascii=False)) | |
# Formations | |
for formation in candidat_data.get("formations", []): | |
if isinstance(formation, dict): | |
all_content.append(json.dumps(formation, ensure_ascii=False)) | |
return " ".join(all_content) | |
def _calculate_experience_duration(self, exp: Dict[str, Any]) -> float: | |
"""Calcule la durée d'une expérience en années""" | |
start_date_str = exp.get("date_debut", exp.get("start_date", "")) | |
end_date_str = exp.get("date_fin", exp.get("end_date", "")) | |
if not isinstance(start_date_str, str): | |
start_date_str = str(start_date_str) if start_date_str else "" | |
if not isinstance(end_date_str, str): | |
end_date_str = str(end_date_str) if end_date_str else "" | |
return self._calculate_duration_in_years(start_date_str, end_date_str) | |
def _calculate_duration_in_years(self, start_date_str: str, end_date_str: str) -> float: | |
"""Calcule la durée entre deux dates en années""" | |
start_date = self._parse_date(start_date_str) | |
end_date = self._parse_date(end_date_str) | |
if start_date and end_date: | |
if end_date < start_date: | |
return 0.0 | |
return (end_date - start_date).days / 365.25 | |
return 0.0 | |
def _parse_date(self, date_str: str) -> datetime: | |
"""Parse une date de manière simple""" | |
if not date_str or not isinstance(date_str, str): | |
return None | |
date_str_lower = date_str.lower().strip() | |
if date_str_lower in ["aujourd'hui", "maintenant", "en cours", "current", "présent", "actuellement"]: | |
return datetime.now() | |
# Extraction simple de l'année | |
year_match = re.search(r'\b(20\d{2}|19\d{2})\b', date_str) | |
if year_match: | |
year = int(year_match.group(1)) | |
return datetime(year, 1, 1) | |
return None | |
# Alias pour maintenir la compatibilité | |
ScoringAgent = SimpleScoringAgent | |
ImprovedScoringAgent = SimpleScoringAgent |