# retoor <retoor@molodetz.nl>
import time
from datetime import datetime, timezone
import jinja2
from fastapi.templating import Jinja2Templates
from markupsafe import Markup, escape
from devplacepy.cache import TTLCache
from devplacepy.config import STATIC_VERSION, TEMPLATES_DIR, TEMPLATE_AUTO_RELOAD
from devplacepy.constants import TOPICS, REACTION_EMOJI
from devplacepy.database import get_int_setting, get_setting, get_table
from devplacepy.avatar import avatar_url, avatar_seed
from devplacepy.utils import format_date as _format_date
from devplacepy.utils import time_ago as _time_ago
from devplacepy.utils import get_badge, is_admin, is_primary_admin, pretty_json
from devplacepy.attachments import (
IMAGE_EXTENSIONS,
allowed_extensions,
format_file_size,
file_icon_emoji,
)
from devplacepy.content import is_owner as _owns
from devplacepy.content import maturity_hidden as _maturity_hidden
from devplacepy.customization import custom_css_tag, custom_js_tag, page_type_for
from devplacepy.services import presence
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
templates.env.auto_reload = TEMPLATE_AUTO_RELOAD
def is_self(user, uid) -> bool:
return bool(user) and user["uid"] == uid
def guest_disabled(user) -> Markup:
if user:
return Markup("")
return Markup(' disabled aria-disabled="true"')
def reaction_emojis(reactions) -> list[str]:
used = dict.fromkeys((reactions or {}).get("counts", {}))
used.update(dict.fromkeys((reactions or {}).get("mine", [])))
extra = [emoji for emoji in used if emoji not in REACTION_EMOJI]
return REACTION_EMOJI + extra
def static_url(path) -> str:
if not isinstance(path, str):
return path
if not path.startswith("/static/") or path.startswith("/static/uploads/"):
return path
return f"/static/v{STATIC_VERSION}{path[len('/static'):]}"
templates.env.globals["static_url"] = static_url
templates.env.globals["static_version"] = STATIC_VERSION
templates.env.globals["is_admin"] = is_admin
templates.env.globals["is_primary_admin"] = is_primary_admin
templates.env.globals["owns"] = _owns
templates.env.globals["maturity_hidden"] = _maturity_hidden
templates.env.globals["is_self"] = is_self
templates.env.globals["guest_disabled"] = guest_disabled
templates.env.globals["is_online"] = presence.is_online
from devplacepy.docs_devrant import devrant_endpoints
templates.env.globals["devrant_endpoints"] = devrant_endpoints
_unread_cache = TTLCache(ttl=10, max_size=1000)
_messages_cache = TTLCache(ttl=10, max_size=1000)
_projects_cache = TTLCache(ttl=10, max_size=1000)
def clear_unread_cache(user_uid: str) -> None:
_unread_cache.pop(user_uid)
def clear_messages_cache(user_uid: str) -> None:
_messages_cache.pop(user_uid)
def clear_user_projects_cache(user_uid: str) -> None:
_projects_cache.pop(user_uid)
def _cached_count(cache: TTLCache, table_name: str, cache_key: str, **filters) -> int:
cached = cache.get(cache_key)
if cached is not None:
return cached
table = get_table(table_name)
count = table.count(**filters)
cache.set(cache_key, count)
return count
def jinja_unread_count(user_uid: str) -> int:
return _cached_count(_unread_cache, "notifications", user_uid, user_uid=user_uid, read=False)
def jinja_unread_messages(user_uid: str) -> int:
return _cached_count(_messages_cache, "messages", user_uid, receiver_uid=user_uid, read=False)
def jinja_user_projects(user_uid: str) -> list[dict]:
cached = _projects_cache.get(user_uid)
if cached is not None:
return cached
projects = get_table("projects")
rows = list(projects.find(user_uid=user_uid, deleted_at=None))
_projects_cache.set(user_uid, rows)
return rows
templates.env.globals["get_unread_count"] = jinja_unread_count
templates.env.globals["get_unread_messages"] = jinja_unread_messages
templates.env.globals["get_user_projects"] = jinja_user_projects
templates.env.globals["avatar_url"] = avatar_url
templates.env.globals["avatar_seed"] = avatar_seed
templates.env.globals["format_date"] = _format_date
def _normalize_iso(dt_str: str) -> str | None:
try:
parsed = datetime.fromisoformat(dt_str)
except (ValueError, TypeError):
return None
if parsed.tzinfo is None:
parsed = parsed.replace(tzinfo=timezone.utc)
return parsed.isoformat()
def local_dt(dt_str: str, mode: str = "datetime") -> Markup:
if not dt_str:
return Markup("")
iso = _normalize_iso(dt_str)
if iso is None:
return Markup(escape(dt_str))
if mode == "ago":
fallback = _time_ago(dt_str)
elif mode == "date":
fallback = _format_date(dt_str)
else:
fallback = _format_date(dt_str, include_time=True)
return Markup(
f'<time datetime="{escape(iso)}" data-dt data-dt-mode="{escape(mode)}">'
f"{escape(fallback)}</time>"
)
templates.env.globals["local_dt"] = local_dt
templates.env.globals["dt_ago"] = lambda dt_str: local_dt(dt_str, "ago")
def award_date(dt_str: str) -> Markup:
if not dt_str:
return Markup("")
iso = _normalize_iso(dt_str)
if iso is None:
return Markup(escape(dt_str))
try:
parsed = datetime.fromisoformat(iso)
if parsed.tzinfo is None:
parsed = parsed.replace(tzinfo=timezone.utc)
fallback = parsed.strftime("%A, %d-%m-%Y")
except (ValueError, TypeError):
fallback = _format_date(dt_str)
return Markup(
f'<time datetime="{escape(iso)}" data-dt data-dt-mode="award">'
f"{escape(fallback)}</time>"
)
from devplacepy.database.awards import award_is_prominent
templates.env.globals["award_date"] = award_date
templates.env.globals["award_is_prominent"] = award_is_prominent
templates.env.globals["badge_info"] = get_badge
templates.env.globals["TOPICS"] = TOPICS
templates.env.globals["REACTION_EMOJI"] = REACTION_EMOJI
templates.env.globals["reaction_emojis"] = reaction_emojis
from devplacepy.database.moderation import minimum_age, report_reason_options
from devplacepy.services.moderation.sla import sla_hours
templates.env.globals["REPORT_REASONS"] = report_reason_options()
def contact_details() -> dict[str, str]:
return {
"email": get_setting("contact_email", ""),
"phone": get_setting("contact_phone", ""),
"address": get_setting("contact_address", ""),
}
def policy_version(kind: str) -> str:
return get_setting(f"{kind}_version", "1") or "1"
def moderation_sla_hours() -> int:
return sla_hours()
def moderation_minimum_age() -> int:
return minimum_age()
def ai_provider_name() -> str:
return get_setting("ai_third_party_provider", "") or "our AI model provider"
templates.env.globals["contact_details"] = contact_details
templates.env.globals["policy_version"] = policy_version
templates.env.globals["moderation_sla_hours"] = moderation_sla_hours
templates.env.globals["moderation_minimum_age"] = moderation_minimum_age
templates.env.globals["ai_provider_name"] = ai_provider_name
def jinja_max_upload_size_mb() -> int:
return get_int_setting("max_upload_size_mb", 10)
def jinja_max_attachments() -> int:
return get_int_setting("max_attachments_per_resource", 10)
def jinja_allowed_file_types() -> str:
return get_setting("allowed_file_types", "")
def jinja_allowed_image_types() -> str:
return ",".join(sorted(allowed_extensions() & IMAGE_EXTENSIONS))
templates.env.globals["max_upload_size_mb"] = jinja_max_upload_size_mb
templates.env.globals["max_attachments_per_resource"] = jinja_max_attachments
templates.env.globals["allowed_file_types"] = jinja_allowed_file_types
templates.env.globals["allowed_image_types"] = jinja_allowed_image_types
_LANGUAGE_NAMES = {
"python": "Python",
"javascript": "JavaScript",
"typescript": "TypeScript",
"html": "HTML",
"css": "CSS",
"c": "C",
"cpp": "C++",
"java": "Java",
"go": "Go",
"rust": "Rust",
"sql": "SQL",
"bash": "Bash",
"yaml": "YAML",
"json": "JSON",
"markdown": "Markdown",
"markdown_rendered": "Markdown Rendered",
"swift": "Swift",
"php": "PHP",
"ruby": "Ruby",
"kotlin": "Kotlin",
"lua": "Lua",
"perl": "Perl",
"haskell": "Haskell",
"elixir": "Elixir",
"r": "R",
"dart": "Dart",
"scala": "Scala",
"plaintext": "Plain Text",
}
def jinja_language_name(code: str) -> str:
return _LANGUAGE_NAMES.get(code, code)
templates.env.globals["language_name"] = jinja_language_name
templates.env.globals["pretty_json"] = pretty_json
templates.env.globals["format_file_size"] = format_file_size
templates.env.globals["file_icon_emoji"] = file_icon_emoji
COMPACT_FROM = 1_000_000
COMPACT_UNITS: tuple[tuple[int, str], ...] = (
(1_000_000_000_000, "T"),
(1_000_000_000, "B"),
(1_000_000, "M"),
)
def format_number(value) -> str:
amount = int(value or 0)
magnitude = abs(amount)
if magnitude < COMPACT_FROM:
return f"{amount:,}"
for limit, suffix in COMPACT_UNITS:
if magnitude >= limit:
scaled = amount / limit
digits = 2 if abs(scaled) < 10 else 1
return f"{round(scaled, digits):g}{suffix}"
return f"{amount:,}"
def format_coins(value) -> str:
return f"{format_number(value)}c"
def format_exact(value) -> str:
return f"{int(value or 0):,}"
def format_speed(value) -> str:
return f"{round(float(value or 0), 2):g}x"
def format_duration(seconds) -> str:
total = max(0, int(seconds or 0))
hours, remainder = divmod(total, 3600)
minutes, secs = divmod(remainder, 60)
if hours:
return f"{hours}h {minutes}m"
if minutes:
return f"{minutes}m {secs}s"
return f"{secs}s"
templates.env.globals["format_number"] = format_number
templates.env.globals["format_coins"] = format_coins
templates.env.globals["format_exact"] = format_exact
templates.env.globals["format_speed"] = format_speed
templates.env.globals["format_duration"] = format_duration
templates.env.globals["custom_css_tag"] = custom_css_tag
templates.env.globals["custom_js_tag"] = custom_js_tag
templates.env.globals["page_type_for"] = page_type_for
def extra_head_tag() -> Markup:
try:
code = get_setting("extra_head", "")
if not code.strip():
return Markup("")
return Markup(code)
except Exception:
return Markup("")
templates.env.globals["extra_head_tag"] = extra_head_tag
from devplacepy.rendering import content_preview, render_content, render_title
from devplacepy.services.deepsearch.citations import link_citations
templates.env.globals["render_content"] = render_content
templates.env.globals["render_title"] = render_title
templates.env.globals["link_citations"] = link_citations
templates.env.globals["content_preview"] = content_preview
def nav_active(request, *prefixes: str, exact: bool = False, css_class: str = "active") -> str:
path = request.url.path
for prefix in prefixes:
if path == prefix:
return css_class
if exact:
continue
base = prefix.rstrip("/")
if base and path.startswith(base + "/"):
return css_class
return ""
templates.env.globals["nav_active"] = nav_active
def response_time_ms(request=None) -> str:
start = getattr(getattr(request, "state", None), "request_start", None)
if start is None:
return ""
return f"{(time.perf_counter() - start) * 1000:.1f}"
templates.env.globals["response_time_ms"] = response_time_ms