Sanad_Package_3/routes_memory.py

107 lines
2.9 KiB
Python

"""/api/memory — visitor-memory CRUD (P3, package-local, mounted by app_p3.py).
The VisitorMemory singleton is resolved lazily from the Project.Sanad.main shim
(app_p3 sets it), so a missing store degrades to 503 rather than crashing.
Kept Python-3.8 compatible.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
router = APIRouter()
def _mem():
try:
from Project.Sanad.main import memory
except Exception:
memory = None
if memory is None:
raise HTTPException(503, "visitor memory store unavailable")
return memory
class ProfileCreate(BaseModel):
name: str
attributes: Optional[Dict[str, Any]] = None
notes: Optional[str] = ""
tags: Optional[List[str]] = None
linked_face_id: Optional[str] = ""
class ProfileUpdate(BaseModel):
name: Optional[str] = None
attributes: Optional[Dict[str, Any]] = None
notes: Optional[str] = None
tags: Optional[List[str]] = None
linked_face_id: Optional[str] = None
@router.get("/")
async def list_profiles():
return {"ok": True, "profiles": _mem().list()}
@router.post("/")
async def create_profile(payload: ProfileCreate):
if not (payload.name or "").strip():
raise HTTPException(400, "name is required")
prof = _mem().add(payload.name.strip(), attributes=payload.attributes,
notes=payload.notes or "", tags=payload.tags,
linked_face_id=payload.linked_face_id or "")
return {"ok": True, "profile": prof}
@router.get("/status")
async def status():
return _mem().status()
@router.get("/primer")
async def primer():
"""The compact known-visitors summary fed into personalized greetings."""
return {"ok": True, "primer": _mem().load_for_primer()}
@router.get("/by-face/{face_id}")
async def by_face(face_id: str):
prof = _mem().find_by_face(face_id)
if prof is None:
raise HTTPException(404, "no visitor linked to face %s" % face_id)
return {"ok": True, "profile": prof}
@router.get("/{pid}")
async def get_profile(pid: str):
prof = _mem().get(pid)
if prof is None:
raise HTTPException(404, "no visitor %s" % pid)
return {"ok": True, "profile": prof}
@router.put("/{pid}")
async def update_profile(pid: str, payload: ProfileUpdate):
prof = _mem().update(pid, **payload.dict(exclude_unset=True))
if prof is None:
raise HTTPException(404, "no visitor %s" % pid)
return {"ok": True, "profile": prof}
@router.post("/{pid}/touch")
async def touch_profile(pid: str):
prof = _mem().touch(pid)
if prof is None:
raise HTTPException(404, "no visitor %s" % pid)
return {"ok": True, "profile": prof}
@router.delete("/{pid}")
async def delete_profile(pid: str):
ok = _mem().delete(pid)
if not ok:
raise HTTPException(404, "no visitor %s" % pid)
return {"ok": True, "deleted": pid}