File size: 3,095 Bytes
7d2fea2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import json
import os
import platform
import re
from datetime import datetime
from pathlib import Path

from models.config import AppSettings
from models.cv import CVData
from models.output import IndexRecord, OutputIndex
from models.resume import HTMLResume

TEMPLATE_DIR = Path(__file__).resolve().parent.parent / "templates"


def render_pdf(
    html_resume: HTMLResume,
    cv_data: CVData,
    settings: AppSettings,
    input_filename: str = "",
    jd_text: str = "",
    iterations_used: int = 0,
    all_filters_passed: bool = False,
    duration_seconds: float = 0.0,
) -> Path:
    settings.output_dir.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now().strftime("%m%d_%H%M")
    name = _slugify(cv_data.name or "resume")
    filename = f"{timestamp}_{name}_{settings.language}.pdf"
    output_path = settings.output_dir / filename

    try:
        _ensure_homebrew_library_path()
        from weasyprint import HTML
    except OSError as exc:
        raise RuntimeError(f"WeasyPrint system dependencies are missing: {exc}") from exc

    html_document = _wrap_resume_html(html_resume.html)
    HTML(string=html_document).write_pdf(output_path)

    if settings.debug:
        debug_dir = settings.output_dir / "debug"
        debug_dir.mkdir(parents=True, exist_ok=True)
        (debug_dir / f"iter_{html_resume.iteration}.html").write_text(html_document, encoding="utf-8")

    record = IndexRecord(
        input_filename=input_filename,
        jd_snippet=jd_text[:300],
        output_pdf=str(output_path),
        iterations_used=iterations_used,
        model=settings.model.name,
        all_filters_passed=all_filters_passed,
        duration_seconds=duration_seconds,
    )
    index_path = settings.output_dir / "index.json"
    index = _load_index(index_path)
    index.append_and_save(record, index_path)
    return output_path


def _slugify(value: str) -> str:
    return re.sub(r"[^a-z0-9]+", "_", value.lower()).strip("_") or "resume"


def _wrap_resume_html(html: str) -> str:
    if re.search(r"<\s*html[\s>]", html, re.IGNORECASE):
        return html
    wrapper_path = TEMPLATE_DIR / "resume_wrapper.html"
    if not wrapper_path.exists():
        return f"<!doctype html><html><body>{html}</body></html>"
    wrapper = wrapper_path.read_text(encoding="utf-8")
    return wrapper.replace("{{HEADER}}", "").replace("{{BODY}}", html)


def _ensure_homebrew_library_path() -> None:
    if platform.system() != "Darwin":
        return
    homebrew_lib = "/opt/homebrew/lib"
    if not Path(homebrew_lib).exists():
        homebrew_lib = "/usr/local/lib"
    existing = os.environ.get("DYLD_FALLBACK_LIBRARY_PATH", "")
    paths = [path for path in existing.split(":") if path]
    if homebrew_lib not in paths:
        paths.insert(0, homebrew_lib)
        os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = ":".join(paths)


def _load_index(path: Path) -> OutputIndex:
    if not path.exists() or not path.read_text(encoding="utf-8").strip():
        return OutputIndex()
    return OutputIndex.model_validate(json.loads(path.read_text(encoding="utf-8")))