from typing import Optional, Literal from ninja import Schema from uuid import UUID class SubmissionIn(Schema): task_id: int html: Optional[str] = None css: Optional[str] = None js: Optional[str] = None conversation_id: Optional[UUID] = None class SubmissionOut(Schema): id: UUID userid: int username: str task_id: int task_display: int task_title: str task_type: Literal["tutorial", "challenge"] score: float my_score: int = 0 html: Optional[str] = None css: Optional[str] = None js: Optional[str] = None conversation_id: Optional[UUID] = None flag: Optional[str] = None nominated: bool = False submit_count: int = 0 created: str modified: str @staticmethod def resolve_userid(obj): return obj.user.id @staticmethod def resolve_username(obj): return obj.user.username @staticmethod def resolve_task_id(obj): return obj.task.id @staticmethod def resolve_task_display(obj): return obj.task.display @staticmethod def resolve_task_title(obj): return obj.task.title @staticmethod def resolve_task_type(obj): return obj.task.task_type @staticmethod def resolve_my_score(obj): return getattr(obj, "my_score", None) or 0 @staticmethod def resolve_submit_count(obj): return getattr(obj, "submit_count", None) or 0 @staticmethod def resolve_created(obj): return obj.created.isoformat() @staticmethod def resolve_modified(obj): return obj.modified.isoformat() @staticmethod def get(submission, rating): return { "id": submission.id, "userid": submission.user.id, "username": submission.user.username, "task_id": submission.task.id, "task_display": submission.task.display, "task_title": submission.task.title, "task_type": submission.task.task_type, "score": submission.score, "my_score": rating.score if rating else 0, "html": submission.html, "css": submission.css, "js": submission.js, "conversation_id": submission.conversation_id, "flag": submission.flag, "nominated": submission.nominated, "created": submission.created.isoformat(), "modified": submission.modified.isoformat(), } class RatingScoreIn(Schema): score: int class SubmissionScoreOut(Schema): id: UUID score: float class SubmissionFilter(Schema): task_id: Optional[int] = None task_type: Optional[Literal["tutorial", "challenge"]] = None username: Optional[str] = None user_id: Optional[int] = None flag: Optional[Literal["red", "blue", "green", "yellow", "any"]] = None score_min: Optional[float] = None score_max_exclusive: Optional[float] = None score_lt_threshold: Optional[float] = None nominated: Optional[bool] = None ordering: Optional[str] = None grouped: Optional[bool] = True class FlagIn(Schema): flag: Optional[Literal["red", "blue", "green", "yellow"]] = None class UserTag(Schema): username: str classname: str class TopSubmission(Schema): submission_id: str # UUID as string username: str classname: str score: float rating_count: int class SubmissionCountBucket(Schema): count_1: int # users with exactly 1 submission count_2: int # users with exactly 2 submissions count_3: int # users with exactly 3 submissions count_4_plus: int # users with 4+ submissions class ScoreBucket(Schema): range_1_2: int # [1, 2) ★ range_2_3: int # [2, 3) ★★ range_3_4: int # [3, 4) ★★★ range_4_5: int # [4, 5) ★★★★ range_5: int # [5, 5] ★★★★★ class FlagStats(Schema): red: int blue: int green: int yellow: int class TaskStatsOut(Schema): submitted_count: int unsubmitted_count: int average_score: Optional[float] unrated_count: int nominated_count: int unsubmitted_users: list[UserTag] unrated_users: list[UserTag] submission_count_distribution: SubmissionCountBucket score_distribution: ScoreBucket top_submissions: list[TopSubmission] flag_stats: FlagStats classes: list[str]