Files
penracourses/survey/models.py
T
2026-06-29 09:04:28 +01:00

157 lines
4.8 KiB
Python

from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.urls import reverse
from generic.mixins import AuthorMixin
class Survey(models.Model, AuthorMixin):
name = models.CharField(max_length=200, help_text="Title of the survey")
description = models.TextField(blank=True, help_text="Introductory text or description")
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="created_surveys"
)
author = models.ManyToManyField(
User,
blank=True,
related_name="surveys",
help_text="Author/Manager(s) of the survey"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
active = models.BooleanField(
default=True,
help_text="If inactive, users will not be able to fill out the survey."
)
anonymous = models.BooleanField(
default=False,
help_text="If checked, responses will not link to the user account or candidate details."
)
authors_only = models.BooleanField(
default=False,
help_text="If true, only survey authors will be able to view or manage this survey."
)
class Meta:
ordering = ["-created_at"]
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("survey:survey_detail", kwargs={"pk": self.pk})
class SurveyQuestion(models.Model):
class QuestionType(models.TextChoices):
TEXT = "TEXT", "Free Text"
CHOICE = "CHOICE", "Multiple Choice (Single Option)"
RATING = "RATING", "Rating (1-5 Likert scale)"
survey = models.ForeignKey(
Survey,
on_delete=models.CASCADE,
related_name="questions"
)
text = models.TextField(help_text="The question text to display to users")
question_type = models.CharField(
max_length=20,
choices=QuestionType.choices,
default=QuestionType.TEXT
)
choices = models.TextField(
blank=True,
help_text="For Multiple Choice questions, enter options separated by a new line."
)
required = models.BooleanField(
default=True,
help_text="If checked, users must answer this question to submit."
)
position = models.IntegerField(
default=0,
help_text="Ordering position of the question."
)
class Meta:
ordering = ["position", "id"]
def __str__(self):
return f"{self.survey.name} - Question {self.position}: {self.text[:50]}"
def get_choices_list(self):
if not self.choices:
return []
return [c.strip() for c in self.choices.split("\n") if c.strip()]
class SurveyResponse(models.Model):
class SurveyContext(models.TextChoices):
PRE = "PRE", "Pre-take survey"
POST = "POST", "Post-take survey"
GENERAL = "GENERAL", "General feedback"
survey = models.ForeignKey(
Survey,
on_delete=models.CASCADE,
related_name="responses"
)
user = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="survey_responses"
)
cid = models.IntegerField(
null=True,
blank=True,
help_text="Candidate ID if taken by an anonymous candidate login."
)
# Generic relationship to associate the response with a CaseCollection or Exam
content_type = models.ForeignKey(
ContentType,
on_delete=models.SET_NULL,
null=True,
blank=True
)
object_id = models.PositiveIntegerField(null=True, blank=True)
content_object = GenericForeignKey("content_type", "object_id")
pre_or_post = models.CharField(
max_length=10,
choices=SurveyContext.choices,
default=SurveyContext.GENERAL
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_at"]
def __str__(self):
user_str = self.user.username if self.user else (f"CID {self.cid}" if self.cid else "Anonymous")
return f"Response for {self.survey.name} by {user_str} ({self.pre_or_post})"
class SurveyAnswer(models.Model):
response = models.ForeignKey(
SurveyResponse,
on_delete=models.CASCADE,
related_name="answers"
)
question = models.ForeignKey(
SurveyQuestion,
on_delete=models.CASCADE,
related_name="answers"
)
text_answer = models.TextField(blank=True, null=True)
choice_answer = models.CharField(max_length=255, blank=True, null=True)
rating_answer = models.IntegerField(blank=True, null=True)
def __str__(self):
return f"Answer to {self.question.id} for response {self.response.id}"