Spaces:
Sleeping
Sleeping
File size: 2,022 Bytes
80a4a65 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | """Per-category completion tracker — powers certificate eligibility.
Idempotent via ``UNIQUE(user_id, category, challenge_id)`` so duplicate
``/api/training/solved`` calls never double-count.
"""
from typing import Optional
import httpx
from app.core.config import SUPABASE_URL, SUPABASE_ANON_KEY
from app.services.supabase_service import supabase_headers
async def record_user_completion(
user_id: Optional[str],
team_role: str,
category: str,
module: str,
challenge_id: str,
xp_awarded: int,
):
"""Insert (or ignore) a row in ``user_completions``."""
if not user_id:
return
if not (SUPABASE_URL and SUPABASE_ANON_KEY):
return
headers = supabase_headers(content_type=True)
headers["Prefer"] = "return=minimal,resolution=ignore-duplicates"
payload = {
"user_id": user_id,
"category": category,
"module": module,
"challenge_id": challenge_id,
"xp_awarded": int(xp_awarded or 0),
}
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.post(
f"{SUPABASE_URL}/rest/v1/user_completions",
json=payload,
headers=headers,
)
if r.status_code not in (200, 201):
print(f"[completions] insert failed {r.status_code}: {r.text[:200]}")
except Exception as e:
print(f"[completions] insert exception: {e}")
async def count_completions(user_id: str, category: str) -> int:
if not (SUPABASE_URL and SUPABASE_ANON_KEY):
return 0
url = (
f"{SUPABASE_URL}/rest/v1/user_completions"
f"?user_id=eq.{user_id}&category=eq.{category}&select=id"
)
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.get(url, headers=supabase_headers())
if r.status_code == 200:
return len(r.json())
except Exception as e:
print(f"[completions] count exception: {e}")
return 0
|