HR-ATS-Portal/backend/job/candidate/plugins.py

74 lines
2.2 KiB
Python

"""CV text cleanup helpers for the PDF extractor.
Pure module: no FastAPI imports and no HTTPException.
Designer-made resumes position every glyph individually, so pypdf hands back
"S K I L L S" instead of "SKILLS". In that layout a single space is glyph
padding and a run of two or more spaces is the real word gap, which is what
the `despace_line` decorator keys off to rebuild readable lines.
"""
from __future__ import annotations
import re
from job.candidate.decorators import despace_line, normalize_unicode
@normalize_unicode
@despace_line
def normalize_spaced_text(text) -> str:
"""Turn raw pypdf output into readable text, leaving normal lines untouched.
The decorators have already folded the unicode and rebuilt the glyph-padded
lines; what is left is the whitespace tidy-up that every line wants.
"""
if not text:
return ""
lines = [re.sub(r" {2,}", " ", line).strip() for line in text.splitlines()]
return re.sub(r"\n{3,}", "\n\n", "\n".join(lines)).strip()
# Mirrors frontend Inbox.jsx sourceFrom — board name is tagged in the To address.
INBOX_SOURCES = (
"Microsoft Outlook",
"Career Portal",
"Manual CV Upload",
"LinkedIn",
"Indeed",
"Rozee",
"Mustakbil",
"Employee Referral",
"Recruitment Agency",
"Campus Hiring",
"Walk-in",
)
def _letters_only(value: str) -> str:
return re.sub(r"[^a-z]", "", (value or "").lower())
def source_from_message_to(message_to: str | None) -> str:
raw = (message_to or "").strip()
if not raw:
return "Unknown"
flat = _letters_only(raw)
for name in INBOX_SOURCES:
if _letters_only(name) and _letters_only(name) in flat:
return name
return raw.split(",")[0].strip()
def documents_from_message(file_name: str | None, file_path: str | None) -> list[dict]:
names = [n.strip() for n in (file_name or "").split(",") if n.strip()]
paths = [p.strip() for p in (file_path or "").split(",") if p.strip()]
out = []
for i, name in enumerate(names):
out.append({"name": name, "path": paths[i] if i < len(paths) else None})
if not out and paths:
for path in paths:
out.append({"name": path.rsplit("/", 1)[-1].rsplit("\\", 1)[-1], "path": path})
return out