from copy import deepcopy from django.shortcuts import get_object_or_404 from rest_framework import permissions, status from rest_framework.response import Response from rest_framework.views import APIView from progression.services import initialize_math_identity from .models import ( MathBTIAssessment, MathBTIResult, MathIdentity, Story, StoryRun, ) from .services import get_run_payload, make_choice, score_mathbti, start_story class MathBTIAssessmentView(APIView): permission_classes = [permissions.AllowAny] def get(self, request): assessment = ( MathBTIAssessment.objects.filter(is_published=True).order_by("-published_at").first() ) if assessment is None: return Response( {"error": {"code": "not_published", "message": "MathBTI 尚未发布"}}, status=status.HTTP_404_NOT_FOUND, ) definition = deepcopy(assessment.definition) for question in definition.get("questions", []): for option in question.get("options", []): option.pop("score", None) definition.pop("results", None) return Response({"version": assessment.version, "definition": definition}) class MathBTISubmitView(APIView): permission_classes = [permissions.AllowAny] def post(self, request): assessment = get_object_or_404( MathBTIAssessment, version=request.data.get("version"), is_published=True, ) identity_code, axis_scores = score_mathbti( assessment.definition, request.data.get("answers", []), ) identity = get_object_or_404(MathIdentity, code=identity_code) if request.user.is_authenticated: result = MathBTIResult.objects.create( user=request.user, assessment=assessment, identity=identity, answers=request.data.get("answers", []), axis_scores=axis_scores, ) initialize_math_identity(request.user, identity) result_id = result.id else: result_id = None return Response( { "id": result_id, "identity": { "code": identity.code, "name": identity.name, "clan": identity.clan, "mathematician": identity.mathematician, "description": identity.description, "portrait": identity.portrait, "initial_abilities": identity.initial_abilities, }, "axis_scores": axis_scores, }, status=status.HTTP_201_CREATED, ) class StoryListView(APIView): permission_classes = [permissions.AllowAny] def get(self, request): stories = Story.objects.filter(is_visible=True).order_by("kind", "title") return Response( [ { "slug": story.slug, "title": story.title, "summary": story.summary, "kind": story.kind, "estimated_minutes": story.estimated_minutes, "available": story.versions.filter(is_published=True).exists(), } for story in stories ] ) class StoryStartView(APIView): def post(self, request, slug): story = get_object_or_404(Story, slug=slug, is_visible=True) return Response(start_story(request.user, story), status=status.HTTP_201_CREATED) class StoryRunView(APIView): def get(self, request, run_id): run = get_object_or_404( StoryRun.objects.select_related("story_version__story"), id=run_id, user=request.user, ) return Response(get_run_payload(run)) class StoryChoiceView(APIView): def post(self, request, run_id): get_object_or_404(StoryRun, id=run_id, user=request.user) payload = make_choice( run_id=run_id, user=request.user, choice_index=request.data.get("choice_index"), idempotency_key=request.headers.get("Idempotency-Key"), ) return Response(payload)