from __future__ import annotations from typing import Any, Dict, List, Optional from fastapi import APIRouter, HTTPException from pydantic import BaseModel, Field from codestar.problems.loader import repo router = APIRouter(prefix="/api/problems", tags=["Problems"]) class ProblemListItem(BaseModel): slug: str title: str difficulty: str difficulty_breakdown: Optional[Dict[str, int]] = None category: str tags: List[str] time_limit: float memory_limit_mb: int is_featured: bool = False class ProblemDetail(BaseModel): slug: str title: str difficulty: str difficulty_breakdown: Optional[Dict[str, int]] = None category: str tags: List[str] description_md: str entry_point: str starter_code: str reference_code: Optional[str] = None time_limit: float memory_limit_mb: int atol: float rtol: float is_featured: bool = False editorial_md: Optional[str] = None test_cases: List[Dict[str, Any]] = Field(default_factory=list) @router.get("", response_model=List[ProblemListItem]) def list_problems(category: Optional[str] = None): return repo.get_list_items(category) @router.get("/random", response_model=ProblemListItem) def get_random_problem(category: Optional[str] = None): import random items = repo.get_list_items(category) if not items: raise HTTPException(status_code=404, detail="No problems found") return random.choice(items) @router.get("/daily") def get_daily_challenge(): import datetime import hashlib today_str = datetime.date.today().isoformat() featured = repo.get_list_items("featured") pool = featured if featured else repo.get_list_items() if not pool: raise HTTPException(status_code=404, detail="No problems found") hash_val = int(hashlib.md5(today_str.encode("utf-8")).hexdigest(), 16) picked = pool[hash_val % len(pool)] return { "date": today_str, "problem": picked, } @router.get("/{slug}", response_model=ProblemDetail) def get_problem(slug: str): problem = repo.get(slug) if not problem: raise HTTPException(status_code=404, detail=f"Problem '{slug}' not found") return ProblemDetail( slug=problem.slug, title=problem.title, difficulty=problem.difficulty, difficulty_breakdown=repo.get_difficulty_breakdown(slug), category=problem.category, tags=problem.tags, description_md=problem.description_md, entry_point=problem.entry_point, starter_code=problem.starter_code, reference_code=problem.reference_code, time_limit=problem.time_limit, memory_limit_mb=problem.memory_limit_mb, atol=problem.atol, rtol=problem.rtol, is_featured=problem.is_featured, editorial_md=problem.editorial_md, test_cases=repo.get_test_cases(slug), )