Update web/core/views_user_features.py

This commit is contained in:
Joshua Laymon 2025-08-17 21:02:07 +00:00
parent f75cb316b4
commit 641e09c1fe

View File

@ -33,23 +33,28 @@ def api_get_prefs(request):
# ---------- Search history ---------- # ---------- Search history ----------
# We use Beacon here, so CSRF headers arent available -> mark this one csrf_exempt # We use Beacon here, so CSRF headers arent available -> mark this one csrf_exempt
@login_required @login_required
@csrf_exempt @csrf_exempt # keep this since you use sendBeacon
@require_POST @require_POST
def api_log_search(request): def api_log_search(request):
q = (request.POST.get("q") or "").strip() q = (request.POST.get("q") or "").strip()
# Expect JSON-like dict from the client for fields, but well accept a flat form too
# Collect selected fields from form-style keys: sel[subject]=on
selected = {} selected = {}
for k in request.POST: for k in request.POST:
if k.startswith("sel[") and k.endswith("]"): # sel[subject]=on if k.startswith("sel[") and k.endswith("]"):
name = k[4:-1] name = k[4:-1]
selected[name] = request.POST.get(k) in ("1", "true", "on", "yes") selected[name] = request.POST.get(k) in ("1", "true", "on", "yes")
# consecutive de-duplication # De-dupe consecutive identical searches; if identical, bump timestamp
last = SearchHistory.objects.filter(user=request.user).order_by("-created_at").first() last = SearchHistory.objects.filter(user=request.user).order_by("-created_at").first()
if not last or last.q != q or last.selected != selected: if last and last.q == q and last.selected == selected:
# bump to "most recent"
last.created_at = timezone.now()
last.save(update_fields=["created_at"])
else:
SearchHistory.objects.create(user=request.user, q=q, selected=selected) SearchHistory.objects.create(user=request.user, q=q, selected=selected)
# trim to last 10 # Trim to last 10
ids = list( ids = list(
SearchHistory.objects.filter(user=request.user) SearchHistory.objects.filter(user=request.user)
.order_by("-created_at") .order_by("-created_at")
@ -61,15 +66,21 @@ def api_log_search(request):
@login_required @login_required
@require_GET @require_GET
@never_cache
def api_get_search_history(request): def api_get_search_history(request):
items = ( items_qs = (
SearchHistory.objects.filter(user=request.user) SearchHistory.objects.filter(user=request.user)
.order_by("-created_at") .order_by("-created_at")
.values("q", "selected", "created_at") .values("q", "selected", "created_at")
) )
# JSON-safe data = [
data = [{"q": i["q"], "selected": i["selected"], "ts": i["created_at"].isoformat()} for i in items] {"q": i["q"], "selected": i["selected"], "ts": i["created_at"].isoformat()}
return JsonResponse({"ok": True, "items": data}) for i in items_qs
]
resp = JsonResponse({"ok": True, "items": data})
resp["Cache-Control"] = "no-store"
return resp
# ---------- Recently viewed ---------- # ---------- Recently viewed ----------
@ -83,7 +94,7 @@ def api_log_view(request, entry_id):
ViewedIllustration.objects.create(user=request.user, entry=e) ViewedIllustration.objects.create(user=request.user, entry=e)
# trim to 50 # Keep only the 50 most recent
ids = list( ids = list(
ViewedIllustration.objects.filter(user=request.user) ViewedIllustration.objects.filter(user=request.user)
.order_by("-viewed_at") .order_by("-viewed_at")
@ -95,18 +106,31 @@ def api_log_view(request, entry_id):
@login_required @login_required
@require_GET @require_GET
@never_cache
def api_get_recent_views(request): def api_get_recent_views(request):
rows = ( rows = (
ViewedIllustration.objects.filter(user=request.user) ViewedIllustration.objects.filter(user=request.user)
.select_related("entry") .select_related("entry")
.order_by("-viewed_at")[:10] # show 10 (we store 50) .order_by("-viewed_at")[:10]
) )
def first_words(text, n=20):
import re as _re
clean = _re.sub(r"\s+", " ", (text or "")).strip()
if not clean:
return ""
words = clean.split(" ")
return clean if len(words) <= n else " ".join(words[:n]) + ""
data = [ data = [
{ {
"entry_id": r.entry_id, "entry_id": r.entry_id,
"viewed_at": r.viewed_at.isoformat(), "viewed_at": r.viewed_at.isoformat(),
"subject": (r.entry.subject or ""), "illustration": r.entry.illustration or "", # <-- what the UI expects
"snippet": first_words(r.entry.illustration or "", 20),
} }
for r in rows for r in rows
] ]
return JsonResponse({"ok": True, "items": data}) resp = JsonResponse({"ok": True, "items": data})
resp["Cache-Control"] = "no-store"
return resp