|
- using Services.ReActAgent;
- using System.Text.RegularExpressions;
- using System.Xml.Linq;
- using ToolsServices;
-
- namespace Services
- {
- public static class GenererCVService
- {
- #region Méthodes publiques
- public static async Task<bool> GenerateOneCV(string fullnameCV, string fullnameTemplateCV, string fullPathOutput, string titreCV, int nbrCompetencesMax, bool isCVAnonyme, bool isUsingIA)
- {
- LoggerService.LogInfo("GenererCVService.GenerateOneCV");
- try
- {
- var reActRagAgent = new Services.ReActAgent.ReActAgent();
-
- #region Logs + vérification de l'existence des fichiers
- LoggerService.LogInfo($"Génération d'un CV");
- LoggerService.LogInfo($"Fichier CV : {fullnameCV}");
-
- if (!File.Exists(fullnameCV))
- {
- LoggerService.LogWarning($"Le fichier CV n'existe pas : {fullnameCV}.");
- return false;
- }
-
- if (!File.Exists(fullnameTemplateCV))
- {
- LoggerService.LogWarning($"Le fichier Template de CV n'existe pas : {fullnameTemplateCV}.");
- return false;
- }
-
- if (!Directory.Exists(fullPathOutput))
- {
- LoggerService.LogWarning($"Le dossier des exports des nouveaux CV n'existe pas : {fullPathOutput}.");
- return false;
- }
- #endregion
-
- string nameNewFileXML = "";
- if (isUsingIA)
- {
- #region Vérification si injection de prompt
- bool isPromptInjectionCV = false;
- var cvText = reActRagAgent.CleanUserInput(FilesService.ExtractText(fullnameCV), out isPromptInjectionCV);
- if (isPromptInjectionCV)
- {
- LoggerService.LogWarning("Attention, le texte CV contient des éléments suspects");
- }
- #endregion
-
- #region Appel au LLM pour extraire les données et les adapter
- /*
- var prompt1 = $@"
- Tu es un assistant RH.
-
- Voici un CV brut :
- <<<
- {cvText}
- >>>
-
- Voici une offre d’emploi :
- <<<
- {ficheMissionText}
- >>>
-
- Ta mission :
- - Réécris le CV de façon à ce qu’il corresponde au mieux à l’offre
- - Mets en avant les compétences pertinentes
- - Réorganise les expériences importantes
- - Reformule le résumé professionnel avec les bons mots-clés
- - Supprime les parties non utiles pour ce poste
-
- Rends un CV **entièrement réécrit** au format texte brut. Ne commente pas.
- ";
- */
- var prompt1 = PromptService.GetPrompt(PromptService.ePrompt.GenererCVService_GenerateOneCV_Extract, cvText);
-
- LoggerService.LogInfo($"Génération du CV - phase A (extraction des infos) : {fullnameCV}");
- var (reponse1, m1) = await reActRagAgent.AppelerLLMAsync(ModelsUseCases.TypeUseCase.CV_To_Xml, true, prompt1, "Génération de 1 CV - phase A");
- var reponseCompleteClean1 = reponse1.Replace("\\n", "\n").Replace("\\r", "\r");
- #endregion
-
- #region Appel au LLM pour mettre au format XML
- /*
- var prompt2 = $@"
- Voici un CV rédigé en texte. Reformate-le en XML structuré selon ce modèle :
-
- <CV>
- <Identite>
- <Nom></Nom>
- <Prenom></Prenom>
- <Email></Email>
- <Telephone></Telephone>
- <Adresse></Adresse>
- <DateDeNaissance></DateDeNaissance>
- <Nationalite></Nationalite>
- </Identite>
- <Profil></Profil>
- <Competences>
- <Competence nom="" niveau="" />
- ...
- </Competences>
- <Langues>
- <Langue nom="" niveau="" />
- ...
- </Langues>
- <Experiences>
- <Experience>
- <Poste></Poste>
- <Entreprise></Entreprise>
- <Lieu></Lieu>
- <DateDebut></DateDebut>
- <DateFin></DateFin>
- <Description></Description>
- </Experience>
- ...
- </Experiences>
- <Formations>
- <Formation>
- <Diplome></Diplome>
- <Etablissement></Etablissement>
- <DateDebut></DateDebut>
- <DateFin></DateFin>
- </Formation>
- ...
- </Formations>
- <Certifications>
- <Certification>
- <Nom></Nom>
- <Organisme></Organisme>
- <Date></Date>
- </Certification>
- ...
- </Certifications>
- </CV>
-
- Si une information est absente, laisse le champ vide. Ne commente pas.
-
- CV :
- <<<
- {reponseCompleteClean1}
- >>>
-
- ";
- */
- var prompt2 = PromptService.GetPrompt(PromptService.ePrompt.GenererCVService_GenerateOneCV_Format, reponseCompleteClean1);
-
- LoggerService.LogInfo($"Génération du CV - phase B (formattage en XML) : {fullnameCV}");
- var (reponse,m2) = await reActRagAgent.AppelerLLMAsync(ModelsUseCases.TypeUseCase.CV_To_Xml, true, prompt2, "Génération de 1 CV - phase B");
- var reponseCompleteClean = reponse.Replace("\\n", "\n").Replace("\\r", "\r");
- reponseCompleteClean = reponseCompleteClean.Replace("&", " et ");
- reponseCompleteClean = reponseCompleteClean.Replace("</start_of_turn>", "");
- #endregion
-
- #region Enregistrer le XML
- // Création du XML
- nameNewFileXML = $"{fullnameCV}_{DateTime.Now.ToString("yyyyMMdd_hhmmss")}.xml";
- if (File.Exists(nameNewFileXML))
- File.Delete(nameNewFileXML);
- System.IO.File.WriteAllText(nameNewFileXML, reponseCompleteClean);
- #endregion
-
- }
- else
- {
- LoggerService.LogInfo($"Pas de modèle IA utilisé");
-
- #region Extraction du texte + générer un XML
- var cv = FilesService.ExtractText(fullnameCV);
- cv = Regex.Replace(cv, @"\s{2,}", " ").Trim();
- var xml = GenerateCvXml(cv);
- nameNewFileXML = $"{fullnameCV}_{DateTime.Now.ToString("yyyyMMdd_hhmmss")}.xml";
- if (File.Exists(nameNewFileXML))
- File.Delete(nameNewFileXML);
- xml.Save(nameNewFileXML);
- #endregion
- }
-
- #region Génération du CV sur la base du XML et d'un template
-
- // Génération du Word
- var t1 = System.IO.Path.GetFileNameWithoutExtension(fullnameCV);
- var t2 = System.IO.Path.GetFileNameWithoutExtension(fullnameTemplateCV);
- var fullnameNewCV = System.IO.Path.Combine(fullPathOutput, $"{t2}_{t1}.docx");
- var b = DocxService.Genere_Docx(fullnameTemplateCV, nameNewFileXML, fullnameNewCV, titreCV, nbrCompetencesMax, isCVAnonyme);
- if (File.Exists(nameNewFileXML))
- File.Delete(nameNewFileXML);
- #endregion
-
- return b;
- }
- catch (Exception ex)
- {
- LoggerService.LogError($"Erreur dans la génération du CV (GenererCVService.GenerateOneCV) : {ex.Message}");
- return false;
- }
- }
- #endregion
-
- #region Méthodes privées
- private static XElement GenerateCvXml(string text)
- {
- LoggerService.LogInfo("GenererCVService.GenerateCvXml");
-
- // === Nom / Prénom ===
- var nameMatch = Regex.Match(text, @"\b([A-Z][a-zéèàêâïî\-']+)\s+([A-ZÉÈÊËÀÂÄÎÏÔÖÙÛÜÇ\-']{2,})\b");
- string prenom = nameMatch.Success ? nameMatch.Groups[1].Value : "";
- string nom = nameMatch.Success ? nameMatch.Groups[2].Value : "";
-
- // === Email ===
- var emailMatch = Regex.Match(text, @"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-z]{2,}");
- string email = emailMatch.Success ? emailMatch.Value : "";
-
- // === Téléphone ===
- var telMatch = Regex.Match(text, @"0[1-9](?:[\s.-]?\d{2}){4}");
- string telephone = telMatch.Success ? telMatch.Value : "";
-
- // === Profil ===
- string profil = ExtractSection(text, "PROFIL", "FORMATIONS");
-
- // === Compétences ===
- var competenceMatches = Regex.Matches(text, @"•\s?(.*?)(?=•|PROJECT MANAGER|EXPERIENCES CLES|PROFIL|$)");
- var competences = new XElement("Competences");
- foreach (Match match in competenceMatches)
- {
- string label = match.Groups[1].Value.Trim();
- if (!string.IsNullOrWhiteSpace(label) && label.Contains(":"))
- {
- string domaine = label.Split(":")[0].Trim();
- competences.Add(new XElement("Competence",
- new XAttribute("nom", domaine),
- new XAttribute("niveau", "Non précisé")
- ));
- }
- }
-
- // === Langues ===
- var langues = new XElement("Langues");
- if (text.Contains("Anglais")) langues.Add(new XElement("Langue", new XAttribute("nom", "Anglais"), new XAttribute("niveau", "Professionnel (Bulats B1)")));
- if (text.Contains("Espagnol")) langues.Add(new XElement("Langue", new XAttribute("nom", "Espagnol"), new XAttribute("niveau", "Scolaire")));
-
- // === Expériences ===
- var experiences = new XElement("Experiences");
- var expMatches = Regex.Matches(text, @"(\d{4})\s*-\s*(.+?)\s+([A-Z].*?)\s+-\s+(.+?)\s+(.*?)\s(?=\d{4}\s*-|$)", RegexOptions.Singleline);
- foreach (Match match in expMatches)
- {
- experiences.Add(new XElement("Experience",
- new XElement("Poste", match.Groups[2].Value.Trim()),
- new XElement("Entreprise", match.Groups[3].Value.Trim()),
- new XElement("Lieu", match.Groups[4].Value.Trim()),
- new XElement("DateDebut", match.Groups[1].Value.Trim()),
- new XElement("DateFin", ""), // à adapter si fin détectable
- new XElement("Description", match.Groups[5].Value.Trim())
- ));
- }
-
- // === Formations ===
- var formations = new XElement("Formations");
- var formationMatches = Regex.Matches(text, @"(\d{4})\s*–\s*(\d{4})\s*---\s*(.+?)\((\d{2})\)");
- foreach (Match match in formationMatches)
- {
- formations.Add(new XElement("Formation",
- new XElement("Diplome", "Non précisé"),
- new XElement("Etablissement", match.Groups[3].Value.Trim()),
- new XElement("DateDebut", match.Groups[1].Value.Trim()),
- new XElement("DateFin", match.Groups[2].Value.Trim())
- ));
- }
-
- // Ajout manuel du Master si non capturé
- if (!text.Contains("IAE de Montpellier"))
- {
- formations.Add(new XElement("Formation",
- new XElement("Diplome", "Master 2 : Administration des Entreprises"),
- new XElement("Etablissement", "IAE de Montpellier"),
- new XElement("DateDebut", "2009"),
- new XElement("DateFin", "2011")
- ));
- }
-
- return new XElement("CV",
- new XElement("Identite",
- new XElement("Nom", nom),
- new XElement("Prenom", prenom),
- new XElement("Email", email),
- new XElement("Telephone", telephone),
- new XElement("Adresse", ""),
- new XElement("DateDeNaissance", ""),
- new XElement("Nationalite", "")
- ),
- new XElement("Profil", profil),
- competences,
- langues,
- experiences,
- formations,
- new XElement("Certifications")
- );
- }
-
- private static string ExtractSection(string text, string startLabel, string endLabel)
- {
- LoggerService.LogInfo("GenererCVService.ExtractSection");
-
- int start = text.IndexOf(startLabel, StringComparison.OrdinalIgnoreCase);
- int end = text.IndexOf(endLabel, start + startLabel.Length, StringComparison.OrdinalIgnoreCase);
- if (start >= 0 && end > start)
- {
- return text.Substring(start + startLabel.Length, end - start - startLabel.Length).Trim();
- }
- return "";
- }
- #endregion
- }
- }
|