modularize service layer, add mobile nav drawer, and sync docs

This commit is contained in:
Justin Visser 2026-02-20 01:28:20 +01:00
parent 7f7a8ce2b0
commit 6b6ed91b92
72 changed files with 8122 additions and 7501 deletions

View file

@ -0,0 +1,38 @@
from __future__ import annotations
from urllib.parse import urlparse
from app.services.domains.scholars.constants import MAX_IMAGE_URL_LENGTH, SCHOLAR_ID_PATTERN
from app.services.domains.scholars.exceptions import ScholarServiceError
def validate_scholar_id(value: str) -> str:
scholar_id = value.strip()
if not SCHOLAR_ID_PATTERN.fullmatch(scholar_id):
raise ScholarServiceError("Scholar ID must match [a-zA-Z0-9_-]{12}.")
return scholar_id
def normalize_display_name(value: str) -> str | None:
normalized = value.strip()
return normalized if normalized else None
def normalize_profile_image_url(value: str | None) -> str | None:
if value is None:
return None
candidate = value.strip()
if not candidate:
return None
if len(candidate) > MAX_IMAGE_URL_LENGTH:
raise ScholarServiceError(
f"Image URL must be {MAX_IMAGE_URL_LENGTH} characters or fewer."
)
parsed = urlparse(candidate)
if parsed.scheme.lower() not in {"http", "https"} or not parsed.netloc:
raise ScholarServiceError("Image URL must be an absolute http(s) URL.")
return candidate