QuentinL52 commited on
Commit
2f3b36f
·
verified ·
1 Parent(s): 8d38cd3

Delete src/cv_parsing_agents.py

Browse files
Files changed (1) hide show
  1. src/cv_parsing_agents.py +0 -264
src/cv_parsing_agents.py DELETED
@@ -1,264 +0,0 @@
1
- """
2
- Module pour le parsing de CV optimisé avec CrewAI
3
- """
4
- import os
5
- import json
6
- import logging
7
-
8
- logger = logging.getLogger(__name__)
9
-
10
- from src.crew.crew_pool import analyse_cv
11
- from src.config import load_pdf
12
-
13
- def clean_dict_keys(data):
14
- """
15
- Nettoie les clés d'un dictionnaire en les convertissant en string.
16
-
17
- Args:
18
- data: Données à nettoyer (dict, list, ou autre)
19
-
20
- Returns:
21
- Données nettoyées avec des clés string
22
- """
23
- if isinstance(data, dict):
24
- return {str(key): clean_dict_keys(value) for key, value in data.items()}
25
- elif isinstance(data, list):
26
- return [clean_dict_keys(element) for element in data]
27
- else:
28
- return data
29
-
30
- class OptimizedCvParserAgent:
31
- """
32
- Agent de parsing de CV optimisé utilisant CrewAI avec découpage intelligent.
33
-
34
- Cette classe traite un fichier PDF de CV en le découpant en sections
35
- pour optimiser le traitement par les agents spécialisés.
36
- """
37
-
38
- def __init__(self, pdf_path: str):
39
- """
40
- Initialise l'agent de parsing de CV optimisé.
41
-
42
- Args:
43
- pdf_path (str): Chemin vers le fichier PDF à traiter
44
-
45
- Raises:
46
- ValueError: Si le chemin du fichier est invalide
47
- """
48
- if not pdf_path or not isinstance(pdf_path, str):
49
- raise ValueError("Le chemin du fichier PDF doit être une chaîne non vide")
50
-
51
- self.pdf_path = pdf_path
52
-
53
- def process(self) -> dict:
54
- """
55
- Traite le fichier PDF pour en extraire le contenu sous forme de JSON optimisé.
56
-
57
- Returns:
58
- dict: Dictionnaire contenant les données extraites du CV
59
-
60
- Raises:
61
- FileNotFoundError: Si le fichier PDF n'existe pas
62
- ValueError: Si le PDF est vide ou illisible
63
- json.JSONDecodeError: Si le résultat n'est pas un JSON valide
64
- Exception: Pour toute autre erreur de traitement
65
- """
66
- logger.info(f"Début du traitement optimisé du CV : {self.pdf_path}")
67
-
68
- if not os.path.exists(self.pdf_path):
69
- raise FileNotFoundError(f"Fichier PDF non trouvé: {self.pdf_path}")
70
-
71
- cv_text_content = load_pdf(self.pdf_path)
72
- if not cv_text_content or not cv_text_content.strip():
73
- raise ValueError("Le PDF semble vide ou illisible")
74
-
75
- logger.info(f"PDF chargé, {len(cv_text_content)} caractères extraits")
76
-
77
- crew_output = analyse_cv(cv_text_content)
78
-
79
- if not crew_output or not hasattr(crew_output, 'raw') or not crew_output.raw.strip():
80
- raise Exception("L'analyse par le crew n'a pas retourné de résultat.")
81
-
82
- raw_string = crew_output.raw
83
- logger.info(f"Résultat brut du crew optimisé: {raw_string[:200]}...")
84
-
85
- json_string_cleaned = self._clean_json_string(raw_string)
86
-
87
- profile_data = json.loads(json_string_cleaned)
88
- logger.info("Parsing JSON optimisé réussi")
89
-
90
- optimized_data = self._validate_and_enhance_data(profile_data)
91
-
92
- return clean_dict_keys(optimized_data)
93
-
94
- def _validate_and_enhance_data(self, profile_data: dict) -> dict:
95
- """
96
- Valide et enrichit les données extraites du CV.
97
-
98
- Args:
99
- profile_data (dict): Données brutes extraites
100
-
101
- Returns:
102
- dict: Données validées et enrichies
103
-
104
- Raises:
105
- ValueError: Si la structure de données est invalide
106
- """
107
- if not isinstance(profile_data, dict) or "candidat" not in profile_data:
108
- raise ValueError("Structure de données invalide - clé 'candidat' manquante")
109
-
110
- candidat = profile_data["candidat"]
111
-
112
- required_sections = [
113
- "informations_personnelles", "compétences", "expériences",
114
- "projets", "formations", "reconversion"
115
- ]
116
-
117
- for section in required_sections:
118
- if section not in candidat or not candidat[section]:
119
- logger.warning(f"Section manquante ou vide: {section}")
120
- candidat[section] = self._get_default_section_data(section)
121
-
122
- self._normalize_competences(candidat.get("compétences", {}))
123
- self._normalize_experiences(candidat.get("expériences", []))
124
-
125
- logger.info("Validation et enrichissement des données terminés")
126
- return profile_data
127
-
128
- def _normalize_competences(self, competences: dict):
129
- """Normalise la section compétences"""
130
- if not isinstance(competences, dict):
131
- return
132
-
133
- if "hard_skills" not in competences:
134
- competences["hard_skills"] = []
135
- if "soft_skills" not in competences:
136
- competences["soft_skills"] = []
137
-
138
- competences["hard_skills"] = [skill.strip() for skill in competences["hard_skills"] if skill and skill.strip()]
139
- competences["soft_skills"] = [skill.strip() for skill in competences["soft_skills"] if skill and skill.strip()]
140
-
141
- def _normalize_experiences(self, experiences: list):
142
- """Normalise la section expériences"""
143
- if not isinstance(experiences, list):
144
- return
145
-
146
- required_fields = ["Poste", "Entreprise", "start_date", "end_date", "responsabilités"]
147
-
148
- for exp in experiences:
149
- if not isinstance(exp, dict):
150
- continue
151
-
152
- for field in required_fields:
153
- if field not in exp or exp[field] in [None, "", []]:
154
- exp[field] = "Non spécifié" if field != "responsabilités" else []
155
-
156
- def _get_default_section_data(self, section: str):
157
- """Retourne des données par défaut pour une section manquante"""
158
- defaults = {
159
- "informations_personnelles": {
160
- "nom": "Non spécifié",
161
- "email": "Non spécifié",
162
- "numero_de_telephone": "Non spécifié",
163
- "localisation": "Non spécifiée"
164
- },
165
- "compétences": {
166
- "hard_skills": [],
167
- "soft_skills": []
168
- },
169
- "expériences": [],
170
- "projets": {
171
- "professional": [],
172
- "personal": []
173
- },
174
- "formations": [],
175
- "reconversion": {
176
- "is_reconversion": False,
177
- "analysis": "Analyse non disponible"
178
- }
179
- }
180
- return defaults.get(section, {})
181
-
182
- def _clean_json_string(self, raw_string: str) -> str:
183
- """
184
- Nettoie une chaîne JSON brute en supprimant les blocs de code markdown.
185
-
186
- Args:
187
- raw_string (str): Chaîne brute à nettoyer
188
-
189
- Returns:
190
- str: Chaîne JSON nettoyée
191
- """
192
- json_string_cleaned = raw_string.strip()
193
-
194
- if '```' in raw_string:
195
- try:
196
- if '```json' in raw_string:
197
- json_part = raw_string.split('```json')[1].split('```')[0]
198
- json_string_cleaned = json_part.strip()
199
- else:
200
- parts = raw_string.split('```')
201
- if len(parts) >= 3:
202
- json_string_cleaned = parts[1].strip()
203
- except IndexError:
204
- logger.warning("Format de code block détecté mais mal formé")
205
-
206
- return json_string_cleaned
207
-
208
- def _clean_json_string(self, raw_string: str) -> str:
209
- """
210
- Nettoie une chaîne JSON brute en supprimant les blocs de code markdown.
211
-
212
- Args:
213
- raw_string (str): Chaîne brute à nettoyer
214
-
215
- Returns:
216
- str: Chaîne JSON nettoyée
217
- """
218
- json_string_cleaned = raw_string.strip()
219
-
220
- if '```' in raw_string:
221
- try:
222
- if '```json' in raw_string:
223
- json_part = raw_string.split('```json')[1].split('```')[0]
224
- json_string_cleaned = json_part.strip()
225
- else:
226
- parts = raw_string.split('```')
227
- if len(parts) >= 3:
228
- json_string_cleaned = parts[1].strip()
229
- except IndexError:
230
- logger.warning("Format de code block détecté mais mal formé")
231
-
232
- return json_string_cleaned
233
-
234
- def get_processing_stats(self) -> dict:
235
- """
236
- Retourne des statistiques sur l'optimisation du traitement.
237
-
238
- Returns:
239
- dict: Statistiques d'optimisation
240
- """
241
- return {
242
- "optimization_enabled": True,
243
- "section_based_processing": True,
244
- "estimated_token_reduction": "85%",
245
- "processing_approach": "Optimized Agent-based with Section Splitting"
246
- }
247
-
248
- class CvParserAgent(OptimizedCvParserAgent):
249
- """
250
- Alias pour maintenir la compatibilité avec l'ancien nom de classe.
251
- Redirige vers la version optimisée.
252
- """
253
- pass
254
-
255
- if __name__ == "__main__":
256
- logger.info("Test du module cv_parsing_agents optimisé")
257
-
258
- try:
259
- agent = OptimizedCvParserAgent("/tmp/test.pdf")
260
- stats = agent.get_processing_stats()
261
- logger.info("✅ OptimizedCvParserAgent créé avec succès")
262
- logger.info(f"✅ Statistiques d'optimisation: {stats}")
263
- except Exception as e:
264
- logger.error(f"❌ Erreur création OptimizedCvParserAgent: {e}")