Add document requests app

This commit is contained in:
Keannu Christian Bernasol 2024-11-24 02:20:18 +08:00
parent bb9fcc3d7c
commit ba19412d31
23 changed files with 484 additions and 53 deletions

View file

@ -0,0 +1,26 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from .models import DocumentRequestUnit, DocumentRequest
from unfold.contrib.filters.admin import RangeDateFilter
# Register your models here.
@admin.register(DocumentRequestUnit)
class DocumentRequestUnitAdmin(ModelAdmin):
search_fields = ["id"]
list_display = ["id", "get_document_title", "copies"]
def get_document_title(self, obj):
return obj.documents.title # Assuming the Document model has a 'title' field
get_document_title.short_description = "Document"
@admin.register(DocumentRequest)
class DocumentRequestAdmin(ModelAdmin):
list_filter = [
("date_requested", RangeDateFilter),
]
list_display = ["id", "date_requested", "status", "college"]

View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class DocumentRequestsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "document_requests"

View file

@ -0,0 +1,94 @@
# Generated by Django 5.1.3 on 2024-11-23 17:01
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 = [
("documents", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="DocumentRequest",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"date_requested",
models.DateTimeField(
default=django.utils.timezone.now, editable=False
),
),
("college", models.CharField(max_length=64)),
("purpose", models.TextField(max_length=512)),
(
"status",
models.CharField(
choices=[
("pending", "Pending"),
("approved", "Approved"),
("denied", "Denied"),
],
default="pending",
max_length=32,
),
),
(
"requester",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel(
name="DocumentRequestUnit",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("copies", models.IntegerField(default=1)),
(
"document",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="documents.document",
),
),
(
"document_request",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="document_requests.documentrequest",
),
),
],
),
migrations.AddField(
model_name="documentrequest",
name="documents",
field=models.ManyToManyField(to="document_requests.documentrequestunit"),
),
]

View file

@ -0,0 +1,28 @@
from django.db import models
from django.utils.timezone import now
class DocumentRequestUnit(models.Model):
document_request = models.ForeignKey(
"document_requests.DocumentRequest", on_delete=models.CASCADE
)
document = models.ForeignKey("documents.Document", on_delete=models.CASCADE)
copies = models.IntegerField(default=1, null=False, blank=False)
class DocumentRequest(models.Model):
requester = models.ForeignKey("accounts.CustomUser", on_delete=models.CASCADE)
documents = models.ManyToManyField("document_requests.DocumentRequestUnit")
date_requested = models.DateTimeField(default=now, editable=False)
college = models.CharField(max_length=64, blank=False, null=False)
purpose = models.TextField(max_length=512, blank=False, null=False)
STATUS_CHOICES = (
("pending", "Pending"),
("approved", "Approved"),
("denied", "Denied"),
)
status = models.CharField(max_length=32, choices=STATUS_CHOICES, default="pending")
# TODO: Add request type (Softcopy/Hardcopy)

View file

@ -0,0 +1,119 @@
from rest_framework import serializers
from documents.models import Document
from documents.serializers import DocumentSerializer, DocumentFileSerializer
from accounts.models import CustomUser
from .models import DocumentRequest, DocumentRequestUnit
class DocumentRequestUnitCreationSerializer(serializers.ModelSerializer):
document = serializers.SlugRelatedField(
many=False, slug_field="id", queryset=Document.objects.all(), required=True
)
class Meta:
model = DocumentRequestUnit
fields = ["document", "copies"]
class DocumentRequestCreationSerializer(serializers.ModelSerializer):
requester = serializers.SlugRelatedField(
many=False, slug_field="id", queryset=CustomUser.objects.all(), required=False
)
documents = DocumentRequestUnitCreationSerializer(many=True, required=True)
college = serializers.CharField(allow_blank=False)
purpose = serializers.CharField(max_length=512, allow_blank=False)
class Meta:
model = DocumentRequest
fields = ["requester", "college", "purpose", "documents"]
def create(self, validated_data):
user = self.context["request"].user
documents_data = validated_data.pop("documents")
# Set requester to user who sent HTTP request to prevent spoofing
validated_data["requester"] = user
DOCUMENT_REQUEST = DocumentRequest.objects.create(**validated_data)
DOCUMENT_REQUEST_UNITS = []
for DOCUMENT_REQUEST_UNIT in documents_data:
DOCUMENT_REQUEST_UNIT = DocumentRequestUnit.objects.create(
document_request=DOCUMENT_REQUEST,
document=DOCUMENT_REQUEST_UNIT["document"],
copies=DOCUMENT_REQUEST_UNIT["copies"],
)
DOCUMENT_REQUEST_UNITS.append(DOCUMENT_REQUEST_UNIT)
DOCUMENT_REQUEST.documents.set(DOCUMENT_REQUEST_UNITS)
DOCUMENT_REQUEST.save()
return DOCUMENT_REQUEST
class DocumentRequestUnitSerializer(serializers.ModelSerializer):
document = DocumentSerializer(many=False)
class Meta:
model = DocumentRequestUnit
fields = ["document", "copies"]
read_only_fields = ["document", "copies"]
class DocumentRequestUnitWithFileSerializer(serializers.ModelSerializer):
document = DocumentFileSerializer(many=False)
class Meta:
model = DocumentRequestUnit
fields = ["document", "copies"]
read_only_fields = ["document", "copies"]
class DocumentRequestSerializer(serializers.ModelSerializer):
documents = serializers.SerializerMethodField()
college = serializers.CharField(allow_blank=False)
purpose = serializers.CharField(max_length=512, allow_blank=False)
class Meta:
model = DocumentRequest
fields = ["id", "requester", "college",
"purpose", "documents", "status"]
read_only_fields = [
"id",
"requester",
"college",
"purpose",
"documents",
"status",
]
def get_documents(self, obj):
if obj.status != "approved":
serializer_class = DocumentRequestUnitSerializer
else:
serializer_class = DocumentRequestUnitWithFileSerializer
return serializer_class(obj.documents, many=True).data
class DocumentRequestUpdateSerializer(serializers.ModelSerializer):
status = serializers.ChoiceField(
choices=DocumentRequest.STATUS_CHOICES, required=True
)
class Meta:
model = DocumentRequest
fields = ["id", "status"]
read_only_fields = ["id", "status"]
def update(self, instance, validated_data):
if instance.status == "denied":
raise serializers.ValidationError(
{
"error": "Denied requests cannot be updated. It is advised you create a new request and approve it from there"
}
)
elif validated_data["status"] == instance.status:
raise serializers.ValidationError(
{"error": "Request form status provided is the same as current status"}
)
return super().update(instance, validated_data)

View file

@ -0,0 +1,12 @@
from django.urls import path, include
from .views import (
DocumentRequestCreateView,
DocumentRequestListView,
DocumentRequestUpdateView,
)
urlpatterns = [
path("create/", DocumentRequestCreateView.as_view()),
path("list/", DocumentRequestListView.as_view()),
path("update/<int:pk>/", DocumentRequestUpdateView.as_view()),
]

View file

@ -0,0 +1,53 @@
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from rest_framework.pagination import PageNumberPagination
from accounts.permissions import IsHead
from rest_framework.pagination import PageNumberPagination
from .serializers import (
DocumentRequestCreationSerializer,
DocumentRequestSerializer,
DocumentRequestUpdateSerializer,
)
from .models import DocumentRequest
class DocumentRequestCreateView(generics.CreateAPIView):
"""
Used by clients to create document requests. Requires passing in request information in addition to the documents themselves
"""
http_method_names = ["post"]
serializer_class = DocumentRequestCreationSerializer
permission_classes = [IsAuthenticated]
class DocumentRequestListView(generics.ListAPIView):
"""
Returns document requests. If document requests are approved, also returns the link to download the document.
Staff are able to view all document requests here. Clients are only able to view their own requests.
"""
http_method_names = ["get"]
serializer_class = DocumentRequestSerializer
pagination_class = PageNumberPagination
permission_classes = [IsAuthenticated]
def get_queryset(self):
user = self.request.user
if user.role == "client":
queryset = DocumentRequest.objects.filter(requester=user)
else:
queryset = DocumentRequest.objects.all()
return queryset
class DocumentRequestUpdateView(generics.UpdateAPIView):
"""
Used by head approve or deny document requests.
"""
http_method_names = ["patch"]
serializer_class = DocumentRequestUpdateSerializer
permission_classes = [IsAuthenticated, IsHead]
queryset = DocumentRequest.objects.all()