74 lines
2.2 KiB
Python
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
|
|
|