Files
mjalmoudy94 717e53a7ce نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
2026-10-13 00:39:28 +03:30

50 lines
1.4 KiB
Python

"""Shared domain primitives.
Pure Python — no framework, no I/O. The domain layer must stay importable
without FastAPI, SQLAlchemy or a database connection.
"""
from __future__ import annotations
import re
import uuid
from datetime import UTC, datetime
_SLUG_RE = re.compile(r"[^a-z0-9]+")
def new_id() -> str:
"""Opaque, URL-safe entity id (portable across SQLite and PostgreSQL)."""
return uuid.uuid4().hex
def utcnow() -> datetime:
return datetime.now(UTC)
def slugify(value: str, *, max_length: int = 48) -> str:
"""ASCII slug. Non-ASCII input (e.g. Persian) collapses to a stable fallback.
Persian organization names do not transliterate, so we keep a short random
suffix appended by the caller when the slug would be empty.
"""
slug = _SLUG_RE.sub("-", value.strip().lower()).strip("-")
slug = slug[:max_length].strip("-")
if not slug:
slug = f"x{uuid.uuid4().hex[:8]}"
return slug
def ensure_unique_slug(base: str, taken: set[str], *, max_length: int = 48) -> str:
"""Append ``-2``, ``-3`` ... until the slug is free."""
candidate = base[:max_length]
if candidate not in taken:
return candidate
n = 2
while True:
suffix = f"-{n}"
candidate = f"{base[: max_length - len(suffix)]}{suffix}"
if candidate not in taken:
return candidate
n += 1