from django.shortcuts import get_object_or_404 from rest_framework import permissions, status, viewsets from rest_framework.response import Response from rest_framework.views import APIView from .models import LatexAttempt, LatexCourse, LatexExercise from .serializers import FormulaDocumentSerializer class FormulaDocumentViewSet(viewsets.ModelViewSet): serializer_class = FormulaDocumentSerializer def get_queryset(self): return self.request.user.formulas.all() class CourseListView(APIView): permission_classes = [permissions.AllowAny] def get(self, request): courses = LatexCourse.objects.filter(is_published=True).prefetch_related( "lessons__exercises" ) return Response( [ { "slug": course.slug, "title": course.title, "description": course.description, "lessons": [ { "slug": lesson.slug, "title": lesson.title, "content": lesson.content, "example_source": lesson.example_source, "exercise_count": lesson.exercises.count(), } for lesson in course.lessons.all() ], } for course in courses ] ) class ExerciseSubmitView(APIView): @staticmethod def normalize(source): source = source or "" text_commands = (r"\text", r"\mbox", r"\textrm", r"\textsf", r"\texttt") normalized = [] index = 0 while index < len(source): command = next( (item for item in text_commands if source.startswith(item, index)), None, ) if command is None: if not source[index].isspace(): normalized.append(source[index]) index += 1 continue command_end = index + len(command) group_start = command_end while group_start < len(source) and source[group_start].isspace(): group_start += 1 if group_start >= len(source) or source[group_start] != "{": normalized.append(command) index = command_end continue depth = 0 group_end = group_start while group_end < len(source): if source[group_end] == "{": depth += 1 elif source[group_end] == "}": depth -= 1 if depth == 0: group_end += 1 break group_end += 1 normalized.append(command) normalized.append(source[group_start:group_end]) index = group_end return "".join(normalized) def post(self, request, exercise_id): exercise = get_object_or_404(LatexExercise, id=exercise_id) submitted = str(request.data.get("source", ""))[:50_000] is_correct = self.normalize(submitted) == self.normalize(exercise.expected_source) LatexAttempt.objects.create( user=request.user, exercise=exercise, submitted_source=submitted, is_correct=is_correct, ) return Response( { "is_correct": is_correct, "expected_source": exercise.expected_source if not is_correct else None, "explanation": exercise.explanation, }, status=status.HTTP_201_CREATED, )