backend/pitch_deck_generator/decks/api/serializers.py

281 lines
11 KiB
Python

from dateutil.parser import parse
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from rest_framework.generics import get_object_or_404
from pitch_deck_generator.decks.models import (
PdfToPPTXStorage,
PitchDeck,
Question,
QuestionAnswer,
QuestionAnswerPhoto,
QuestionDeckHint,
)
class BasePitchDeckSerializer(serializers.ModelSerializer):
class Meta:
model = PitchDeck
fields = ["id", "name", "description", "logo", "created", "updated"]
extra_kwargs = {
"id": {"read_only": True},
"logo": {"read_only": True},
"name": {"read_only": True},
"created": {"read_only": True},
"updated": {"read_only": True},
}
class PitchDeckSerializer(serializers.ModelSerializer):
class Meta:
model = PitchDeck
fields = "__all__"
class HintSerializer(serializers.Serializer):
type = serializers.ChoiceField(choices=Question.QuestionType.choices)
value = serializers.JSONField()
class PresentationAnswerSerializer(serializers.Serializer):
slug = serializers.CharField()
answer = serializers.JSONField()
photos = serializers.ListSerializer(child=serializers.ImageField())
class PitchDeckSlidePresentationSerializer(serializers.Serializer):
slide = serializers.IntegerField()
data = PresentationAnswerSerializer(many=True)
class PitchDeckPresentationSerializer(serializers.Serializer):
deck = BasePitchDeckSerializer()
slide = PitchDeckSlidePresentationSerializer(many=True)
class QuestionSerializer(serializers.ModelSerializer):
hint = serializers.SerializerMethodField(method_name="get_hint")
next_id = serializers.SerializerMethodField(method_name="get_next_id")
@extend_schema_field(HintSerializer)
def get_hint(self, obj):
if obj.hint:
question_id = (
self.context["view"].kwargs["question_id"]
if "question_id" in self.context["view"].kwargs
else 1
)
if q := QuestionDeckHint.objects.filter(
question_id=question_id,
deck_id=self.context["view"].kwargs["deck_id"],
):
return q.first().hint
return {}
return False
@extend_schema_field(serializers.IntegerField)
def get_next_id(self, obj):
if q := Question.objects.filter(order=obj.order + 1):
return q.first().id
return 0
class Meta:
model = Question
fields = ["id", "text", "hint", "required", "type", "params", "next_id"]
class AnswerSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
file_fields = kwargs.pop("file_fields", None)
super().__init__(*args, **kwargs)
if file_fields:
field_update_dict = {
field: serializers.FileField(required=False, write_only=True)
for field in file_fields
}
self.fields.update(**field_update_dict)
file = serializers.FileField(allow_null=True, required=False)
class Meta:
model = QuestionAnswer
fields = ["answer", "deck", "question", "file"]
extra_kwargs = {
"deck": {"read_only": True},
"question": {"read_only": True},
}
def validate(self, data):
answer = data["answer"] if "answer" in data else None
question = get_object_or_404(
Question, id=self.context["view"].kwargs["question_id"]
)
deck = get_object_or_404(PitchDeck, id=self.context["view"].kwargs["deck_id"])
question_type = question.type
params = question.params if question.params else {}
match question_type:
case "text":
if type(answer) is not str:
raise serializers.ValidationError("Incorrect type")
if "max_length" in params:
if len(answer) > params["max_length"]:
raise serializers.ValidationError("Text is too long")
case "number":
if type(answer) is not int:
raise serializers.ValidationError("Incorrect type")
case "text_array":
if type(answer) is not list:
raise serializers.ValidationError("Incorrect type")
if any([type(x) is not str for x in answer]):
raise serializers.ValidationError("Incorrect type")
case "range":
slug = params["slug"]
if type(answer) is not dict:
raise serializers.ValidationError("Incorrect type")
if slug not in answer:
raise serializers.ValidationError("Value to found")
if not isinstance(answer[slug], (int, float)):
raise serializers.ValidationError("Incorrect type")
if not (params["min_value"] <= answer[slug] <= params["max_value"]):
raise serializers.ValidationError("Number is too big or too small")
case "multiple_range":
scrollbars = {}
for el in params["scrollbars"]:
scrollbars[el["slug"]] = el
for slug in scrollbars.keys():
if slug not in answer:
raise serializers.ValidationError(f"Value {slug} to found")
if not isinstance(answer[slug], (int, float)):
raise serializers.ValidationError(f"Incorrect {slug} type")
if not (
scrollbars[slug]["min_value"]
<= answer[slug]
<= scrollbars[slug]["max_value"]
):
raise serializers.ValidationError(
f"Number is too big or too small for {slug}"
)
case "select":
if answer not in params["options"]:
raise serializers.ValidationError("No such option")
case "date":
try:
parse(answer)
except ValueError:
raise serializers.ValidationError("Incorrect date type")
case "photo":
if answer:
raise serializers.ValidationError("Answer should be blank")
if not data["file"]:
raise serializers.ValidationError("No file found")
case "multiple_photo":
if answer:
raise serializers.ValidationError("Answer should be blank")
for key, value in data.items():
if isinstance(value, (TemporaryUploadedFile, InMemoryUploadedFile)):
if "_" not in key:
raise serializers.ValidationError(
"You should use file_num for file keys"
)
try:
int(key.split("_")[1])
except ValueError:
raise serializers.ValidationError(
"You should use file_num for file keys"
)
case "photo_description":
if not data["file"]:
raise serializers.ValidationError("No file found")
if type(answer) is not str:
raise serializers.ValidationError("Incorrect type")
if "max_length" in params:
if len(answer) > params["max_length"]:
raise serializers.ValidationError("Text is too long")
case "multiple_photo_description":
if type(answer) is not list:
raise serializers.ValidationError("Incorrect type")
if any([type(x) is not str for x in answer]):
raise serializers.ValidationError("Incorrect type")
len_f = 0
for key, value in data.items():
if isinstance(value, (TemporaryUploadedFile, InMemoryUploadedFile)):
if "_" not in key:
raise serializers.ValidationError(
"You should use file_num for file keys"
)
try:
int(key.split("_")[1])
except ValueError:
raise serializers.ValidationError(
"You should use file_num for file keys"
)
len_f += 1
if len_f != len(answer):
raise serializers.ValidationError(
"You should provide the same amount of answers in list as photos"
)
case "multiple_link_description":
if type(answer) is not dict:
raise serializers.ValidationError("Incorrect type")
if any([type(x) is not str for x in answer.keys()]):
raise serializers.ValidationError("Incorrect type")
if any([type(x) is not str for x in answer.values()]):
raise serializers.ValidationError("Incorrect type")
case "multiple_date_description":
if type(answer) is not dict:
raise serializers.ValidationError("Incorrect type")
if not (params["min"] <= len(answer) <= params["max"]):
raise serializers.ValidationError(
"number of dates is too small or too large"
)
for date in answer.keys():
try:
parse(date)
except ValueError:
raise serializers.ValidationError("Incorrect date type")
for val in answer.values():
if type(val) is not str:
raise serializers.ValidationError("Incorrect type")
data["question_id"] = question.id
data["deck_id"] = deck.id
return data
def create(self, validated_data):
q = QuestionAnswer.objects.get_or_create(
deck_id=validated_data["deck_id"], question_id=validated_data["question_id"]
)[0]
q.answer = validated_data["answer"] if "answer" in validated_data else {}
q.save()
s = [
key
for key, val in validated_data.items()
if isinstance(val, (TemporaryUploadedFile, InMemoryUploadedFile))
and key != "file"
]
if "file" in validated_data:
QuestionAnswerPhoto.objects.create(answer=q, file=validated_data["file"])
if s:
s.sort(key=lambda x: int(x.split("_")[1]))
for key in s:
QuestionAnswerPhoto.objects.create(answer=q, file=validated_data[key])
return q
class PdfToPPTXSerializer(serializers.ModelSerializer):
class Meta:
model = PdfToPPTXStorage
fields = ["pdf", "pptx"]
extra_kwargs = {"pptx": {"read_only": True}}