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