LibBee / src /api /feedback.py
nikeshn's picture
Upload 5 files
7dfd4df verified
Raw History Blame Contribute Delete
1.71 kB
import json
import time
import re
from fastapi import APIRouter
from pydantic import BaseModel, ConfigDict, Field, field_validator
from src.config import get_settings
router = APIRouter()
class FeedbackRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
request_id: str = Field(pattern=r"^[A-Za-z0-9_-]{8,80}$")
feedback: str
comment: str = Field(default="", max_length=500)
# The existing LibGuides client sends this field to its Worker. Accept it
# if forwarded, but do not store it in the local feedback fallback.
session_id: str | None = Field(default=None, pattern=r"^[A-Za-z0-9_-]{8,80}$")
@field_validator("feedback")
@classmethod
def valid_feedback(cls, v: str) -> str:
if v not in {"up", "down"}:
raise ValueError("feedback must be up or down")
return v
@router.post("")
async def save_feedback(req: FeedbackRequest):
settings = get_settings()
comment = re.sub(r"[\w.+-]+@[\w-]+\.[\w.-]+", "[email]", req.comment)
comment = re.sub(r"\b\d{6,}\b", "[number]", comment)
payload = {
"timestamp": time.time(),
"request_id": req.request_id,
"feedback": req.feedback,
"comment": comment[:500],
}
settings.feedback_path.parent.mkdir(parents=True, exist_ok=True)
with settings.feedback_path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(payload, ensure_ascii=False) + "\n")
try:
from app import metrics_service
metrics_service.incr("feedback_total")
metrics_service.incr_bucket("feedback_breakdown", req.feedback)
except Exception:
pass
return {"status": "ok"}