Skip to content

Commit c412940

Browse files
committed
feat(utils): ajout d'une fonction NLP pour extraire le sujet principal d'une phrase courte et utilisation possible dans l'évaluation d'une expression pour le calcul de la valeur d'une variable ou pour le plugin readcsv
1 parent 9bcc204 commit c412940

5 files changed

Lines changed: 235 additions & 4 deletions

File tree

app/js/markdown/custom/variablesDynamic/evaluateExpression.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { config } from "../../../config.mjs";
22
import { tryConvertStringToNumber } from "../../../utils/strings.mjs";
3+
import { mainTopic } from "../../../utils/nlp.mjs";
34

45
// Opérations autorisées pour le calcul des expressions complexes
56
const sanitizeCodeAllowedOperations = [
@@ -41,6 +42,7 @@ const sanitizeCodeAllowedOperations = [
4142
"false",
4243
"tryConvertStringToNumber",
4344
"dynamicVariables",
45+
"mainTopic",
4446
];
4547

4648
// Nettoie une formule de test avant exécution dynamique (new Function).
@@ -93,7 +95,8 @@ export function evaluateExpression(expression, dynamicVariables) {
9395
const result = new Function(
9496
"dynamicVariables",
9597
"tryConvertStringToNumber",
98+
"mainTopic",
9699
"return " + expression,
97-
)(dynamicVariables, tryConvertStringToNumber);
100+
)(dynamicVariables, tryConvertStringToNumber, mainTopic);
98101
return result;
99102
}

app/js/utils/nlp.mjs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,119 @@ export function cosineSimilarity(str, vector, options) {
295295
return dot / (mag1 * mag2);
296296
}
297297
}
298+
299+
export function mainTopic(str, specificExpressionsToRemove = []) {
300+
// Extrait le sujet principal d'une chaîne de caractères qui représente une courte phrase
301+
if (!str || typeof str !== "string") return "";
302+
// On supprime les caractères spéciaux
303+
str = str.replace(/[.,!?';:]/g, " ");
304+
// On supprime les expressions spécifiques
305+
const baseExpressionsToRemove = [
306+
"-moi",
307+
"-tu",
308+
"en savoir plus",
309+
"à propos de",
310+
];
311+
specificExpressionsToRemove = specificExpressionsToRemove.concat(
312+
baseExpressionsToRemove,
313+
);
314+
for (const expr of specificExpressionsToRemove) {
315+
const regex = new RegExp(`\\b${expr}\\b`, "gi");
316+
str = str.replace(regex, " ");
317+
}
318+
// CAS 1 : s'il y a un seul mot, c'est le sujet principal
319+
const words = str.trim().split(/\s+/);
320+
if (words.length === 1) {
321+
return words[0];
322+
}
323+
// CAS 2 : s'il y a plusieurs mots, on enlève les stop-words et on retourne le mot le plus long restant
324+
const stopWords = new Set([
325+
"le",
326+
"la",
327+
"les",
328+
"l",
329+
"un",
330+
"une",
331+
"des",
332+
"de",
333+
"du",
334+
"d",
335+
"ce",
336+
"cette",
337+
"cet",
338+
"ces",
339+
"c",
340+
"je",
341+
"j",
342+
"il",
343+
"et",
344+
"à",
345+
"au",
346+
"aux",
347+
"pour",
348+
"dans",
349+
"en",
350+
"pour",
351+
"avec",
352+
"sur",
353+
"sous",
354+
"par",
355+
"dans",
356+
"que",
357+
"qui",
358+
"quelques",
359+
"qu",
360+
"est",
361+
"suis",
362+
"sont",
363+
"ne",
364+
"n",
365+
"pas",
366+
"me",
367+
"m",
368+
"surtout",
369+
"principalement",
370+
"beaucoup",
371+
"bien",
372+
"vraiment",
373+
"avant",
374+
"tout",
375+
"plus",
376+
"moins",
377+
"très",
378+
"aussi",
379+
"infos",
380+
"information",
381+
"informations",
382+
"thème",
383+
"sujet",
384+
"thématique",
385+
"trouve",
386+
"trouver",
387+
"donne",
388+
"donner",
389+
"intéresse",
390+
"intéressé",
391+
"intéressée",
392+
"intéressent",
393+
"cherche",
394+
"aime",
395+
"aimerais",
396+
"veux",
397+
"voudrais",
398+
"besoin",
399+
"ai",
400+
"souhaite",
401+
"souhaiterais",
402+
"pourrais",
403+
"peux",
404+
]);
405+
const filteredWords = words.filter(
406+
(word) => !stopWords.has(word.toLowerCase()),
407+
);
408+
if (filteredWords.length === 0) {
409+
return "";
410+
}
411+
const mainTopic = filteredWords.join(" ");
412+
return mainTopic;
413+
}

app/script.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/script.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/unit/utils/nlp.spec.mjs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
tokenize,
1010
createVector,
1111
cosineSimilarity,
12+
mainTopic,
1213
} from "../../../app/js/utils/nlp.mjs";
1314

1415
describe("longestCommonSubstringWeightedLength", function () {
@@ -491,3 +492,114 @@ describe("cosineSimilarity", function () {
491492
expect(result).toBeLessThan(1);
492493
});
493494
});
495+
496+
describe("mainTopic", function () {
497+
it("returns an empty string for empty input", function () {
498+
const result = mainTopic("");
499+
expect(result).toBe("");
500+
});
501+
502+
it("returns the single word when input is a single word", function () {
503+
const result = mainTopic("vélo");
504+
expect(result).toBe("vélo");
505+
});
506+
507+
it("returns the noun without the article when input is a noun with article", function () {
508+
const result1 = mainTopic("le vélo");
509+
expect(result1).toBe("vélo");
510+
511+
const result2 = mainTopic("la philosophie");
512+
expect(result2).toBe("philosophie");
513+
514+
const result3 = mainTopic("les chats");
515+
expect(result3).toBe("chats");
516+
});
517+
518+
it("returns the noun when input is a noun with an adverb and article", function () {
519+
const result1 = mainTopic("surtout le vélo");
520+
expect(result1).toBe("vélo");
521+
522+
const result2 = mainTopic("principalement la philosophie");
523+
expect(result2).toBe("philosophie");
524+
});
525+
526+
it("returns the noun when input is in a short answer format", function () {
527+
const result1 = mainTopic("C'est le vélo");
528+
expect(result1).toBe("vélo");
529+
530+
const result2 = mainTopic("Ce sont les chats");
531+
expect(result2).toBe("chats");
532+
});
533+
534+
it("extracts the main topic from a short sentence expressing interest or a desire for information", function () {
535+
const result1 = mainTopic(
536+
"Je veux des informations sur le vélo électrique",
537+
);
538+
expect(result1).toBe("vélo électrique");
539+
540+
const result2 = mainTopic("Je m'intéresse beaucoup à la photographie");
541+
expect(result2).toBe("photographie");
542+
543+
const result3 = mainTopic("J'aimerais bien en savoir plus sur les voyages");
544+
expect(result3).toBe("voyages");
545+
546+
const result4 = mainTopic("Je suis intéressé par l'astronomie");
547+
expect(result4).toBe("astronomie");
548+
549+
const result5 = mainTopic("Je cherche des infos sur la cuisine italienne");
550+
expect(result5).toBe("cuisine italienne");
551+
552+
const result6 = mainTopic("Ce qui m'intéresse, c'est la musique classique");
553+
expect(result6).toBe("musique classique");
554+
555+
const result7 = mainTopic(
556+
"J'aimerais des infos sur le développement durable",
557+
);
558+
expect(result7).toBe("développement durable");
559+
560+
const result8 = mainTopic(
561+
"le vélo électrique, c'est ce qui m'intéresse le plus",
562+
);
563+
expect(result8).toBe("vélo électrique");
564+
});
565+
566+
it("extracts the main topic without stop words", function () {
567+
const result1 = mainTopic("j'aime bien les films d'action");
568+
expect(result1).toBe("films action");
569+
570+
const result2 = mainTopic(
571+
"Je m'intéresse aux résultats de la recherche scientifique",
572+
);
573+
expect(result2).toBe("résultats recherche scientifique");
574+
});
575+
576+
it("extract the main topic without specific expressions", function () {
577+
const result1 = mainTopic("trouve-moi un jeu de données sur le climat", [
578+
"jeu de données",
579+
]);
580+
expect(result1).toBe("climat");
581+
582+
const result2 = mainTopic(
583+
"je cherche des jeux de données sur la ville de Paris",
584+
["jeu de données", "jeux de données", "ville de"],
585+
);
586+
expect(result2).toBe("Paris");
587+
});
588+
589+
it("extracts main topic when input is a question", function () {
590+
const result1 = mainTopic("Peux-tu me donner des infos sur l'Égypte ?");
591+
expect(result1).toBe("Égypte");
592+
593+
const result2 = mainTopic(
594+
"Pourrais-tu me trouver des informations sur la physique quantique ?",
595+
);
596+
expect(result2).toBe("physique quantique");
597+
});
598+
599+
it("extracts main topic in a complex sentence", function () {
600+
const result = mainTopic(
601+
"J'aimerais bien en savoir plus sur les impacts du changement climatique sur la biodiversité marine.",
602+
);
603+
expect(result).toBe("impacts changement climatique biodiversité marine");
604+
});
605+
});

0 commit comments

Comments
 (0)