-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtemplate_reader.py
More file actions
125 lines (100 loc) · 3.78 KB
/
Copy pathtemplate_reader.py
File metadata and controls
125 lines (100 loc) · 3.78 KB
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""Read trusted LaTeX resume templates for the resume-generation agent."""
from __future__ import annotations
from pathlib import Path
from typing import Any
from .config import Settings, get_settings
DEFAULT_TEMPLATE_NAME = "base_resume_template.tex"
SUPPORTED_TEMPLATE_EXTENSIONS = {".tex"}
def _is_within(path: Path, root: Path) -> bool:
"""Return True when path is inside root after both are resolved."""
try:
path.resolve().relative_to(root.resolve())
return True
except ValueError:
return False
def _resolve_template_path(
template_name: str,
settings: Settings,
) -> Path:
"""Resolve a template only from the configured template directory."""
cleaned = template_name.strip().strip('"').strip("'")
if not cleaned:
cleaned = DEFAULT_TEMPLATE_NAME
raw_path = Path(cleaned).expanduser()
if raw_path.is_absolute():
raise PermissionError(
"Absolute template paths are not allowed. Place templates in the "
"configured templates folder and provide a relative path."
)
candidates = [
settings.template_dir / raw_path,
settings.package_dir / raw_path,
]
searched: list[str] = []
for candidate in candidates:
resolved = candidate.resolve()
searched.append(str(candidate))
if not _is_within(resolved, settings.template_dir):
continue
if resolved.exists() and resolved.is_file():
return resolved
raise FileNotFoundError(
"Resume template not found. Place it in the templates folder. "
f"Checked: {', '.join(searched)}"
)
def read_resume_latex_template(
template_name: str = DEFAULT_TEMPLATE_NAME,
max_chars: int = 0,
) -> dict[str, Any]:
"""
Read a trusted LaTeX resume template.
Args:
template_name: Filename or templates-folder-relative path to a .tex
template. Defaults to base_resume_template.tex.
max_chars: Optional character limit. Use 0 for MAX_TEMPLATE_CHARS.
Returns:
A dictionary containing template metadata and template_text.
"""
settings = get_settings()
try:
path = _resolve_template_path(template_name, settings)
suffix = path.suffix.lower()
if suffix not in SUPPORTED_TEMPLATE_EXTENSIONS:
return {
"status": "error",
"document_type": "resume template",
"message": "Only .tex resume templates are supported.",
}
character_limit = (
max_chars if max_chars > 0 else settings.max_template_chars
)
character_limit = min(character_limit, settings.max_template_chars)
text = path.read_text(encoding="utf-8", errors="replace")
if not text.strip():
return {
"status": "error",
"document_type": "resume template",
"message": "The selected LaTeX template is empty.",
}
was_truncated = len(text) > character_limit
template_text = text[:character_limit]
return {
"status": "success",
"document_type": "resume template",
"file_name": path.name,
"file_type": suffix,
"characters_returned": len(template_text),
"truncated": was_truncated,
"template_text": template_text,
"template_policy": (
"Use this as a visual and structural base. Sections and "
"subsections may be added, removed, renamed, combined, or "
"reordered to fit verified candidate facts and the target job."
),
}
except Exception as exc:
return {
"status": "error",
"document_type": "resume template",
"message": str(exc),
}