Finish up questionnaire app

This commit is contained in:
Keannu Bernasol 2024-11-24 10:34:41 +08:00
parent 8bd8df9042
commit b0a9b6b6f0
13 changed files with 558 additions and 25 deletions

View file

@ -18,5 +18,4 @@ class IsHead(BasePermission):
""" """
def has_permission(self, request, view): def has_permission(self, request, view):
print(request.user.role)
return bool(request.user and request.user.role == "head") return bool(request.user and request.user.role == "head")

View file

@ -25,12 +25,8 @@ class CustomUserRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField( password = serializers.CharField(
write_only=True, style={"input_type": "password", "placeholder": "Password"} write_only=True, style={"input_type": "password", "placeholder": "Password"}
) )
first_name = serializers.CharField( first_name = serializers.CharField(required=True)
required=True, allow_blank=False, allow_null=False last_name = serializers.CharField(required=True)
)
last_name = serializers.CharField(
required=True, allow_blank=False, allow_null=False
)
class Meta: class Meta:
model = CustomUser model = CustomUser

View file

@ -13,6 +13,7 @@ urlpatterns = [
path("accounts/", include("accounts.urls")), path("accounts/", include("accounts.urls")),
path("documents/", include("documents.urls")), path("documents/", include("documents.urls")),
path("requests/", include("document_requests.urls")), path("requests/", include("document_requests.urls")),
path("questionnaires/", include("questionnaires.urls")),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("schema/", SpectacularAPIView.as_view(), name="schema"), path("schema/", SpectacularAPIView.as_view(), name="schema"),
path( path(

View file

@ -92,6 +92,7 @@ INSTALLED_APPS = [
"accounts", "accounts",
"documents", "documents",
"document_requests", "document_requests",
"questionnaires",
"django_cleanup.apps.CleanupConfig", "django_cleanup.apps.CleanupConfig",
] ]

View file

@ -0,0 +1,22 @@
# Generated by Django 5.1.3 on 2024-11-24 02:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("document_requests", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="documentrequest",
name="type",
field=models.CharField(
choices=[("softcopy", "Softcopy"), ("hardcopy", "Hardcopy")],
default="softcopy",
max_length=16,
),
),
]

View file

@ -25,4 +25,9 @@ class DocumentRequest(models.Model):
status = models.CharField(max_length=32, choices=STATUS_CHOICES, default="pending") status = models.CharField(max_length=32, choices=STATUS_CHOICES, default="pending")
# TODO: Add request type (Softcopy/Hardcopy) TYPE_CHOICES = (
("softcopy", "Softcopy"),
("hardcopy", "Hardcopy"),
)
type = models.CharField(max_length=16, choices=TYPE_CHOICES, default="softcopy")

View file

@ -10,6 +10,7 @@ class DocumentRequestUnitCreationSerializer(serializers.ModelSerializer):
document = serializers.SlugRelatedField( document = serializers.SlugRelatedField(
many=False, slug_field="id", queryset=Document.objects.all(), required=True many=False, slug_field="id", queryset=Document.objects.all(), required=True
) )
copies = serializers.IntegerField(min_value=1)
class Meta: class Meta:
model = DocumentRequestUnit model = DocumentRequestUnit
@ -21,12 +22,13 @@ class DocumentRequestCreationSerializer(serializers.ModelSerializer):
many=False, slug_field="id", queryset=CustomUser.objects.all(), required=False many=False, slug_field="id", queryset=CustomUser.objects.all(), required=False
) )
documents = DocumentRequestUnitCreationSerializer(many=True, required=True) documents = DocumentRequestUnitCreationSerializer(many=True, required=True)
college = serializers.CharField(allow_blank=False) college = serializers.CharField(max_length=64)
purpose = serializers.CharField(max_length=512, allow_blank=False) purpose = serializers.CharField(max_length=512)
type = serializers.ChoiceField(choices=DocumentRequest.TYPE_CHOICES, required=True)
class Meta: class Meta:
model = DocumentRequest model = DocumentRequest
fields = ["requester", "college", "purpose", "documents"] fields = ["requester", "college", "type", "purpose", "documents"]
def create(self, validated_data): def create(self, validated_data):
user = self.context["request"].user user = self.context["request"].user
@ -71,18 +73,30 @@ class DocumentRequestUnitWithFileSerializer(serializers.ModelSerializer):
class DocumentRequestSerializer(serializers.ModelSerializer): class DocumentRequestSerializer(serializers.ModelSerializer):
documents = serializers.SerializerMethodField() documents = serializers.SerializerMethodField()
college = serializers.CharField(allow_blank=False) purpose = serializers.CharField(max_length=512)
purpose = serializers.CharField(max_length=512, allow_blank=False) date_requested = serializers.DateTimeField(
format="%m-%d-%Y %I:%M %p", read_only=True
)
class Meta: class Meta:
model = DocumentRequest model = DocumentRequest
fields = ["id", "requester", "college", fields = [
"purpose", "documents", "status"] "id",
"requester",
"college",
"type",
"purpose",
"date_requested",
"documents",
"status",
]
read_only_fields = [ read_only_fields = [
"id", "id",
"requester", "requester",
"college", "college",
"type",
"purpose", "purpose",
"date_requested",
"documents", "documents",
"status", "status",
] ]
@ -109,7 +123,7 @@ class DocumentRequestUpdateSerializer(serializers.ModelSerializer):
if instance.status == "denied": if instance.status == "denied":
raise serializers.ValidationError( raise serializers.ValidationError(
{ {
"error": "Denied requests cannot be updated. It is advised you create a new request and approve it from there" "error": "Denied requests cannot be updated. You should instead create a new request and approve it from there"
} }
) )
elif validated_data["status"] == instance.status: elif validated_data["status"] == instance.status:
@ -120,10 +134,12 @@ class DocumentRequestUpdateSerializer(serializers.ModelSerializer):
representation = super().update(instance, validated_data) representation = super().update(instance, validated_data)
# Send an email on request status update # Send an email on request status update
email = RequestUpdateEmail() try:
email.context = { email = RequestUpdateEmail()
"request_status": instance.status email.context = {"request_status": instance.status}
} email.send(to=[instance.requester.email])
email.send(to=[instance.requester.email]) except:
# Silence out errors if email sending fails
pass
return representation return representation

View file

@ -4,7 +4,6 @@ from .models import Document
class DocumentUploadSerializer(serializers.ModelSerializer): class DocumentUploadSerializer(serializers.ModelSerializer):
# For staff document uploads # For staff document uploads
file = serializers.FileField()
date_uploaded = serializers.DateTimeField( date_uploaded = serializers.DateTimeField(
format="%m-%d-%Y %I:%M %p", read_only=True format="%m-%d-%Y %I:%M %p", read_only=True
) )

View file

@ -0,0 +1,259 @@
# Generated by Django 5.1.3 on 2024-11-24 02:27
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="Questionnaire",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"client_type",
models.CharField(
choices=[
("citizen", "Citizen"),
("business", "Business"),
("government", "Government (Employee or Another Agency)"),
],
max_length=32,
),
),
(
"date_submitted",
models.DateTimeField(
default=django.utils.timezone.now, editable=False
),
),
(
"sex",
models.CharField(
choices=[("male", "Male"), ("female", "Female")], max_length=16
),
),
("age", models.IntegerField()),
("region_of_residence", models.CharField(max_length=64)),
("service_availed", models.CharField(max_length=64)),
(
"i_am_a",
models.CharField(
choices=[
("faculty", "Faculty"),
("non-teaching staff", "Non-Teaching Staff"),
("student", "Student"),
("guardian", "Guardian/Parent of Student"),
("alumna", "Alumna"),
("other", "Other"),
],
max_length=32,
),
),
(
"i_am_a_other",
models.CharField(blank=True, max_length=64, null=True),
),
(
"q1_answer",
models.CharField(
choices=[
("1", "I know what a CC is and I saw this office's CC"),
(
"2",
"I know what a CC is but I did NOT see this office's CC",
),
(
"3",
"I learned of the CC only when I saw this office's CC",
),
(
"4",
"I do not know what a CC is and I did not see one in this office",
),
],
max_length=64,
),
),
(
"q2_answer",
models.CharField(
choices=[
("1", "Easy to see"),
("2", "Somewhat easy to see"),
("3", "Difficult to see"),
("4", "Not visible at all"),
("5", "N/A"),
],
max_length=64,
),
),
(
"q3_answer",
models.CharField(
choices=[
("1", "Helped very much"),
("2", "Somewhat helped"),
("3", "Did not help"),
("4", "N/A"),
],
max_length=64,
),
),
(
"sqd0_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd1_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd2_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd3_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd4_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd5_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd6_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd7_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"sqd8_answer",
models.CharField(
choices=[
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
],
max_length=16,
),
),
(
"extra_suggestions",
models.TextField(blank=True, max_length=512, null=True),
),
(
"client",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View file

@ -1,3 +1,113 @@
from django.db import models from django.db import models
from django.utils.timezone import now
# Create your models here.
class Questionnaire(models.Model):
# Personal Information
# Email address is derived from the user and is no longer optional
client = models.ForeignKey("accounts.CustomUser", on_delete=models.CASCADE)
CLIENT_TYPE_CHOICES = (
("citizen", "Citizen"),
("business", "Business"),
("government", "Government (Employee or Another Agency)"),
)
client_type = models.CharField(
max_length=32, choices=CLIENT_TYPE_CHOICES, null=False, blank=False
)
date_submitted = models.DateTimeField(default=now, editable=False)
SEX_CHOICES = (
("male", "Male"),
("female", "Female"),
)
sex = models.CharField(
max_length=16, choices=SEX_CHOICES, null=False, blank=False)
age = models.IntegerField(null=False, blank=False)
region_of_residence = models.CharField(
max_length=64, null=False, blank=False)
service_availed = models.CharField(max_length=64, null=False, blank=False)
I_AM_I_CHOICES = (
("faculty", "Faculty"),
("non-teaching staff", "Non-Teaching Staff"),
("student", "Student"),
("guardian", "Guardian/Parent of Student"),
("alumna", "Alumna"),
("other", "Other"),
)
i_am_a = models.CharField(
max_length=32, choices=I_AM_I_CHOICES, null=False, blank=False
)
# This is filled up if i_am_a=other
i_am_a_other = models.CharField(max_length=64, null=True, blank=True)
# CC Questions
Q1_CHOICES = (
("1", "I know what a CC is and I saw this office's CC"),
("2", "I know what a CC is but I did NOT see this office's CC"),
("3", "I learned of the CC only when I saw this office's CC"),
("4", "I do not know what a CC is and I did not see one in this office"),
)
q1_answer = models.CharField(
max_length=64, choices=Q1_CHOICES, null=False, blank=False
)
Q2_CHOICES = (
("1", "Easy to see"),
("2", "Somewhat easy to see"),
("3", "Difficult to see"),
("4", "Not visible at all"),
("5", "N/A"),
)
q2_answer = models.CharField(
max_length=64, choices=Q2_CHOICES, null=False, blank=False
)
Q3_CHOICES = (
("1", "Helped very much"),
("2", "Somewhat helped"),
("3", "Did not help"),
("4", "N/A"),
)
q3_answer = models.CharField(
max_length=64, choices=Q3_CHOICES, null=False, blank=False
)
# SQD Questions
SQD_CHOICES = (
("1", "Strongly disagree"),
("2", "Disagree"),
("3", "Neither Agree nor Disagree"),
("4", "Agree"),
("5", "Strongly Agree"),
("6", "N/A"),
)
sqd0_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd1_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd2_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd3_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd4_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd5_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd6_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd7_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
sqd8_answer = models.CharField(
max_length=16, choices=SQD_CHOICES, null=False, blank=False
)
extra_suggestions = models.TextField(max_length=512, null=True, blank=True)

View file

@ -0,0 +1,90 @@
from rest_framework import serializers
from accounts.models import CustomUser
from .models import Questionnaire
class QuestionnaireSerializer(serializers.ModelSerializer):
client = serializers.SlugRelatedField(
many=False, slug_field="id", queryset=CustomUser.objects.all(), required=False
)
client_type = serializers.ChoiceField(
choices=Questionnaire.CLIENT_TYPE_CHOICES)
date_submitted = serializers.DateTimeField(
format="%m-%d-%Y %I:%M %p", read_only=True
)
sex = serializers.ChoiceField(choices=Questionnaire.SEX_CHOICES)
age = serializers.IntegerField(min_value=1)
region_of_residence = serializers.CharField(max_length=64)
service_availed = serializers.CharField(max_length=64)
i_am_a = serializers.ChoiceField(choices=Questionnaire.I_AM_I_CHOICES)
i_am_a_type_other = serializers.CharField(required=False)
q1_answer = serializers.ChoiceField(choices=Questionnaire.Q1_CHOICES)
q2_answer = serializers.ChoiceField(choices=Questionnaire.Q2_CHOICES)
q3_answer = serializers.ChoiceField(choices=Questionnaire.Q3_CHOICES)
sqd0_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd1_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd3_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd4_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd5_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd6_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd7_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
sqd8_answer = serializers.ChoiceField(choices=Questionnaire.SQD_CHOICES)
extra_suggestions = serializers.CharField(max_length=512, required=False)
def to_representation(self, instance):
representation = super().to_representation(instance)
representation["client"] = instance.client.email
return super().to_representation(instance)
def create(self, validated_data):
user = self.context["request"].user
# Set questionnaire user to the one who sent the HTTP request to prevent spoofing
validated_data["client"] = user
if (
validated_data["client_type"] == "other"
and not validated_data["client_type_other"]
):
raise serializers.ValidationError(
{"error": "Missing description for client type: Other"}
)
# Create the instance without calling super().create()
instance = self.Meta.model(**validated_data)
# Explicitly set the client_type attribute
instance.client_type = validated_data.get("client_type")
# Save the instance
instance.save()
return instance
class Meta:
model = Questionnaire
fields = [
"id",
"client",
"client_type",
"date_submitted",
"sex",
"age",
"region_of_residence",
"service_availed",
"i_am_a",
"i_am_a_type_other",
"q1_answer",
"q2_answer",
"q3_answer",
"sqd0_answer",
"sqd1_answer",
"sqd2_answer",
"sqd3_answer",
"sqd4_answer",
"sqd5_answer",
"sqd6_answer",
"sqd7_answer",
"sqd8_answer",
"extra_suggestions",
]
read_only_fields = ["id", "date_submitted"]

View file

@ -0,0 +1,10 @@
from django.urls import include, path
from .views import (
QuestionnaireListAPIView,
QuestionnaireSubmitView,
)
urlpatterns = [
path("submit/", QuestionnaireSubmitView.as_view()),
path("list/", QuestionnaireListAPIView.as_view()),
]

View file

@ -1,3 +1,28 @@
from django.shortcuts import render from rest_framework import generics
from .serializers import QuestionnaireSerializer
from rest_framework.permissions import IsAuthenticated
from .models import Questionnaire
from rest_framework.pagination import PageNumberPagination
from accounts.permissions import IsStaff
# Create your views here.
class QuestionnaireListAPIView(generics.ListAPIView):
"""
Used by staff to view questionnaires
"""
http_method_names = ["get"]
serializer_class = QuestionnaireSerializer
queryset = Questionnaire.objects.all()
pagination_class = PageNumberPagination
permission_classes = [IsAuthenticated, IsStaff]
class QuestionnaireSubmitView(generics.CreateAPIView):
"""
Used by clients to submit questionnaires
"""
http_method_names = ["post"]
serializer_class = QuestionnaireSerializer
permission_classes = [IsAuthenticated]