implement server mvp: fastapi app, org formatter, sqlite store, tests, dockerfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
68
server/app/org_writer.py
Normal file
68
server/app/org_writer.py
Normal file
@@ -0,0 +1,68 @@
|
||||
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"
|
||||
Reference in New Issue
Block a user