58 lines
1.4 KiB
Python
Executable File
58 lines
1.4 KiB
Python
Executable File
"""Small formatting and parsing helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
def slugify(value: str) -> str:
|
|
slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
|
return slug or "untitled"
|
|
|
|
|
|
def normalise_tags(value: Any) -> list[str]:
|
|
if isinstance(value, str):
|
|
raw = re.split(r"[,:\s]+", value)
|
|
elif isinstance(value, list):
|
|
raw = [str(item) for item in value]
|
|
else:
|
|
raw = []
|
|
tags = []
|
|
for tag in raw:
|
|
if not tag.strip():
|
|
continue
|
|
clean = slugify(tag)
|
|
if clean and clean not in tags:
|
|
tags.append(clean)
|
|
return tags
|
|
|
|
|
|
def org_date(dt: datetime) -> str:
|
|
return dt.strftime("<%Y-%m-%d %a %H:%M>")
|
|
|
|
|
|
def parse_org_datetime(value: str | None) -> datetime | None:
|
|
if not value:
|
|
return None
|
|
match = re.search(r"<(\d{4})-(\d{2})-(\d{2})(?:\s+\w+)?(?:\s+(\d{2}):(\d{2}))?>", value)
|
|
if not match:
|
|
return None
|
|
year, month, day, hour, minute = match.groups()
|
|
return datetime(
|
|
int(year),
|
|
int(month),
|
|
int(day),
|
|
int(hour or 12),
|
|
int(minute or 0),
|
|
)
|
|
|
|
|
|
def html_escape(value: str) -> str:
|
|
return (
|
|
value.replace("&", "&")
|
|
.replace("<", "<")
|
|
.replace(">", ">")
|
|
.replace('"', """)
|
|
)
|