from django.db import models from django.utils import timezone from django.core.files.storage import FileSystemStorage from django.conf import settings from django.utils.html import format_html from django.urls import reverse from django.utils.translation import gettext_lazy as _ from generic.models import CidUser, CidUserGroup, ExamBase, ExamCollection, ExamUserStatus, QuestionBase, QuestionNote, UserAnswerBase, UserUserGroup import reversion from django.contrib.contenttypes.fields import GenericRelation class Category(models.Model): category = models.CharField(max_length=200) def __str__(self): return self.category class Question(QuestionBase): stem = models.TextField( blank=False, help_text="Stem of the question", ) answer_help_text = "Answer text" BEST_ANSWER_CHOICES = ( ("a", "a_answer"), ("b", "b_answer"), ("c", "c_answer"), ("d", "d_answer"), ("e", "e_answer"), ) # I doubt this is the best way to structure the model # (but it should work) a_answer = models.TextField(help_text=answer_help_text) a_feedback = models.TextField( blank=True, help_text="Feedback for answer", ) b_answer = models.TextField(help_text=answer_help_text) b_feedback = models.TextField( blank=True, help_text="Feedback for answer", ) c_answer = models.TextField(help_text=answer_help_text) c_feedback = models.TextField( blank=True, help_text="Feedback for answer", ) d_answer = models.TextField(help_text=answer_help_text) d_feedback = models.TextField( blank=True, help_text="Feedback for answer", ) e_answer = models.TextField(help_text=answer_help_text) e_feedback = models.TextField( blank=True, help_text="Feedback for answer", ) feedback = models.TextField( blank=True, help_text="General question feedback", ) best_answer = models.CharField( max_length=20, choices=BEST_ANSWER_CHOICES, help_text="The correct answer" ) author = models.ManyToManyField( settings.AUTH_USER_MODEL, blank=True, help_text="Author(s) of question", related_name="sbas_authored_questions", ) category = models.ForeignKey( Category, on_delete=models.SET_NULL, null=True, blank=True ) #notes = GenericRelation(QuestionNote) def __str__(self): return self.stem def clean(self): if self.a_answer: self.a_answer = self.a_answer.strip() if self.b_answer: self.b_answer = self.b_answer.strip() if self.c_answer: self.c_answer = self.c_answer.strip() if self.d_answer: self.d_answer = self.d_answer.strip() if self.e_answer: self.e_answer = self.e_answer.strip() def get_app_name(self): return "sbas" def get_absolute_url(self): return reverse("sbas:question_detail", kwargs={"pk": self.pk}) def get_answers(self): return ( self.a_answer, self.b_answer, self.c_answer, self.d_answer, self.e_answer, ) def get_answer_by_choice(self, choice): if choice == "a": return self.a_answer if choice == "b": return self.b_answer if choice == "c": return self.c_answer if choice == "d": return self.d_answer if choice == "e": return self.e_answer def get_correct_answer(self): if self.best_answer == "a": return self.a_answer if self.best_answer == "b": return self.b_answer if self.best_answer == "c": return self.c_answer if self.best_answer == "d": return self.d_answer if self.best_answer == "e": return self.e_answer def get_correct_answer_stripped(self): return self.get_correct_answer().replace("

", "", 1).replace("

", "", 1) def get_stem_stripped(self): return self.stem.replace("

", "", 1).replace("

", "", 1) class ExamQuestionDetail(models.Model): sort_order = models.IntegerField(default=1000) exam = models.ForeignKey("Exam", on_delete=models.CASCADE) rapid = models.ForeignKey(Question, on_delete=models.CASCADE) class Meta: ordering = ("sort_order",) @reversion.register class Exam(ExamBase): app_name = "sbas" exam_questions = models.ManyToManyField(Question, through=ExamQuestionDetail, related_name="exams") time_limit = models.IntegerField( help_text="Exam time limit (in seconds)", blank=True, null=True, ) recreate_json = None author = models.ManyToManyField( settings.AUTH_USER_MODEL, blank=True, help_text="Author of exam", related_name="sba_exam_author", ) markers = models.ManyToManyField( settings.AUTH_USER_MODEL, blank=True, null=True, help_text="Authorised markers for the exam", related_name="sba_exam_markers", ) valid_cid_users = models.ManyToManyField(CidUser, blank=True, related_name="sba_exams") valid_user_users = models.ManyToManyField( settings.AUTH_USER_MODEL, blank=True, related_name="user_sba_exams" ) cid_user_groups = models.ManyToManyField( CidUserGroup, blank=True, help_text="These groups define which candidates are able to be added to the exams/collection.", related_name="sba_cid_user_groups" ) user_user_groups = models.ManyToManyField( UserUserGroup, blank=True, help_text="These groups define which candidates are able to be added to the exams/collection.", related_name="sba_user_user_groups" ) exam_user_status = GenericRelation(ExamUserStatus, related_query_name="sbas_exams") cid_user_exam = GenericRelation("generic.CidUserExam", related_query_name="sbas_exams") examcollection = models.ForeignKey(ExamCollection, blank=True, null=True, on_delete=models.SET_NULL, related_name="sbas_exams") def get_take_url(self): return reverse("sbas:exam_start", kwargs={"pk": self.pk}) def get_app_name(self): return "sbas" @reversion.register class UserAnswer(UserAnswerBase): """User answers by candidate""" app_name = "sbas" question = models.ForeignKey( Question, related_name="cid_user_answers", on_delete=models.CASCADE ) answer = models.CharField( max_length=20, choices=Question.BEST_ANSWER_CHOICES, help_text="The user selected answer", ) user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True, related_name="user_sbas_user_answers", ) # Each user answer is associated with a particular exam exam = models.ForeignKey( Exam, related_name="cid_user_answers", on_delete=models.CASCADE, null=True ) def get_answer_string(self): return self.answer def get_answer(self): return self.answer def get_correct(self): return self.answer == self.question.best_answer def get_answer_score(self): if self.get_correct(): return 1 return 0