|
|
""" |
|
|
Template Loader Module |
|
|
Handles loading and parsing of letter templates. |
|
|
""" |
|
|
|
|
|
import re |
|
|
import logging |
|
|
from pathlib import Path |
|
|
from typing import List, Dict, Set |
|
|
from .config import TEMPLATE_DIR |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class TemplateLoader: |
|
|
""" |
|
|
Responsible for discovering, loading, and parsing letter templates. |
|
|
""" |
|
|
|
|
|
def __init__(self, template_dir: Path = TEMPLATE_DIR): |
|
|
self.template_dir = template_dir |
|
|
|
|
|
def list_templates(self) -> List[str]: |
|
|
""" |
|
|
List all available template files in the template directory. |
|
|
Returns a list of filenames. |
|
|
""" |
|
|
if not self.template_dir.exists(): |
|
|
logger.warning(f"Template directory not found: {self.template_dir}") |
|
|
return [] |
|
|
|
|
|
|
|
|
templates = [f.name for f in self.template_dir.glob("*.txt")] |
|
|
return sorted(templates) |
|
|
|
|
|
def load_template(self, template_name: str) -> str: |
|
|
""" |
|
|
Load the content of a specific template. |
|
|
""" |
|
|
template_path = self.template_dir / template_name |
|
|
|
|
|
if not template_path.exists(): |
|
|
raise FileNotFoundError(f"Template not found: {template_name}") |
|
|
|
|
|
try: |
|
|
return template_path.read_text(encoding='utf-8') |
|
|
except Exception as e: |
|
|
logger.error(f"Error loading template {template_name}: {e}") |
|
|
raise |
|
|
|
|
|
def extract_placeholders(self, template_text: str) -> Set[str]: |
|
|
""" |
|
|
Extract placeholders from the template text. |
|
|
Supports formats: |
|
|
- [Placeholder Name] |
|
|
- {{Placeholder Name}} |
|
|
- <Placeholder Name> |
|
|
""" |
|
|
placeholders = set() |
|
|
|
|
|
|
|
|
bracket_matches = re.findall(r'\[(.*?)\]', template_text) |
|
|
placeholders.update(bracket_matches) |
|
|
|
|
|
|
|
|
curly_matches = re.findall(r'\{\{(.*?)\}\}', template_text) |
|
|
placeholders.update(curly_matches) |
|
|
|
|
|
|
|
|
angle_matches = re.findall(r'<(.*?)>', template_text) |
|
|
placeholders.update(angle_matches) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
single_curly_matches = re.findall(r'(?<!\{)\{([^{}]+)\}(?!\})', template_text) |
|
|
placeholders.update(single_curly_matches) |
|
|
|
|
|
|
|
|
return {p.strip() for p in placeholders if p.strip()} |
|
|
|