diff --git a/src/job_research/profile/cv_extractor.py b/src/job_research/profile/cv_extractor.py index 4fe83ab..7800bf9 100644 --- a/src/job_research/profile/cv_extractor.py +++ b/src/job_research/profile/cv_extractor.py @@ -35,6 +35,20 @@ EXPERIENCE_TITLE_STOPWORDS = { "because", } +EXPERIENCE_TITLE_LABELS = { + "education", + "experience", + "formation", + "languages", + "langues", + "location", + "profile", + "skills", + "compétences", + "competences", + "summary", +} + EXPERIENCE_PROSE_MARKERS = { "after", "before", @@ -279,14 +293,18 @@ def _looks_like_experience_title(title: str) -> bool: return False first_word = title_words[0] - if len(first_word) < 2 or not first_word[0].isupper(): + if len(first_word) < 2: return False - if first_word.lower() in EXPERIENCE_TITLE_STOPWORDS: + normalized_title = " ".join(title_words).casefold() + if normalized_title in EXPERIENCE_TITLE_LABELS: + return False + + if first_word.casefold() in EXPERIENCE_TITLE_STOPWORDS: return False return not any( - word.lower() in EXPERIENCE_PROSE_MARKERS for word in title_words if word + word.casefold() in EXPERIENCE_PROSE_MARKERS for word in title_words if word ) diff --git a/tests/profile/test_cv_extractor.py b/tests/profile/test_cv_extractor.py index 4c36853..5a3b0ea 100644 --- a/tests/profile/test_cv_extractor.py +++ b/tests/profile/test_cv_extractor.py @@ -169,6 +169,26 @@ def test_extract_cv_signals_parses_clear_titles_with_french_and_english_connecto ] +def test_extract_cv_signals_accepts_lowercase_mixed_case_and_numeric_titles() -> None: + text = dedent( + """ + Tonio + Location: France + data engineer at Company A + iOS Engineer at Company A + 3D Artist at Studio + """ + ).strip() + + extracted = extract_cv_signals(text) + + assert extracted["experience_entries"] == [ + {"title": "data engineer", "company": "Company A"}, + {"title": "iOS Engineer", "company": "Company A"}, + {"title": "3D Artist", "company": "Studio"}, + ] + + def test_extract_cv_signals_ignores_french_prose_continuations() -> None: text = dedent( """ @@ -183,6 +203,22 @@ def test_extract_cv_signals_ignores_french_prose_continuations() -> None: assert extracted["experience_entries"] == [] +def test_extract_cv_signals_rejects_label_like_lines_without_colons() -> None: + text = dedent( + """ + Tonio + Location at Paris + Summary at a glance + Profile at LinkedIn + Education at EPITA + """ + ).strip() + + extracted = extract_cv_signals(text) + + assert extracted["experience_entries"] == [] + + def test_extract_cv_signals_ignores_in_paris_prose_tail() -> None: text = dedent( """