import re from datetime import datetime, timezone from typing import Sequence from .models import CaptureRequest def normalize_tags(tags: Sequence[str]) -> list[str]: seen: set[str] = set() result: list[str] = [] for raw in tags: tag = raw.strip().lower() tag = tag.lstrip("#") tag = tag.replace(" ", "_") tag = re.sub(r"[^a-z0-9_]", "", tag) if tag and tag not in seen: seen.add(tag) result.append(tag) return result def _org_tags(tags: list[str]) -> str: if not tags: return "" return " :" + ":".join(tags) + ":" def _created_stamp(created_at: str) -> str: """Parse ISO-8601 datetime and format as org inactive timestamp.""" try: dt = datetime.fromisoformat(created_at) except ValueError: dt = datetime.now(timezone.utc) day_abbr = dt.strftime("%a").lower() return f"[{dt.strftime('%Y-%m-%d')} {day_abbr} {dt.strftime('%H:%M')}]" def _property_drawer(created_stamp: str, source: str, capture_id: str) -> str: return ( ":PROPERTIES:\n" f":CREATED: {created_stamp}\n" f":SOURCE: {source}\n" f":ID: {capture_id}\n" ":END:" ) def format_capture(capture: CaptureRequest) -> str: tags = normalize_tags(capture.tags) tag_str = _org_tags(tags) created_stamp = _created_stamp(capture.created_at) drawer = _property_drawer(created_stamp, capture.device, capture.id) if capture.kind == "todo": heading = f"* TODO {capture.body}{tag_str}" return f"{heading}\n{drawer}\n" # note lines = capture.body.split("\n") first_line = lines[0].rstrip() is_multiline = len(lines) > 1 and any(l.strip() for l in lines[1:]) if is_multiline: heading = f"* note: {first_line}{tag_str}" return f"{heading}\n{drawer}\n\n{capture.body}\n" else: heading = f"* note{tag_str}" return f"{heading}\n{drawer}\n\n{capture.body}\n"