import typing from django.contrib.postgres import fields from django.db import models class Quiz(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Question(models.Model): quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE) prompt = models.CharField(max_length=200) def __str__(self): return self.prompt def get_answer(self) -> typing.Union["Answer", None]: return ( getattr(self, "multiplechoiceanswer", None) or getattr(self, "freetextanswer", None) or getattr(self, "llmgradedanswer", None) ) class Answer(models.Model): question = models.OneToOneField(Question, on_delete=models.CASCADE) correct_answer = models.CharField(max_length=200, default="") rubrics = models.TextField( blank=True, null=True, verbose_name="Grading Rubrics - For LLM-graded questions only. You can leave this empty." ) class Meta: abstract = True def __str__(self) -> str: return self.correct_answer or self.rubrics or "No answer or rubrics provided" def is_correct(self, user_answer) -> bool: return user_answer == self.correct_answer class FreeTextAnswer(Answer): case_sensitive = models.BooleanField(default=False) def is_correct(self, user_answer) -> bool: if not self.case_sensitive: return user_answer.lower() == self.correct_answer.lower() return user_answer == self.correct_answer class LLMGradedAnswer(Answer): def grade(self, user_answer, rubrics) -> dict: import requests """ Grades the user's answer by calling the grading API. Args: user_answer (str): The answer provided by the user. rubrics (str): The grading rubrics. Returns: bool: True if the user's answer is correct, False otherwise. """ api_url = "http://localhost/api/grade" payload = {"user_answer": user_answer, "rubrics": rubrics} try: response = requests.post(api_url, json=payload) response.raise_for_status() # Raise an error for bad status codes result = response.json() # Assuming the API returns a JSON object with a 'correct' field return result except requests.RequestException as e: # Handle any errors that occur during the request print(f"An error occurred: {e}") return {"result": "error", "message": str(e)} class MultipleChoiceAnswer(Answer): choices = fields.ArrayField(models.CharField(max_length=200, blank=True)) def __str__(self) -> str: return f"{self.correct_answer} from {self.choices}"