2023-08-26 09:36:47 +03:00
|
|
|
from dateutil.parser import parse
|
|
|
|
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
|
2023-08-26 01:17:47 +03:00
|
|
|
from drf_spectacular.utils import extend_schema_field
|
|
|
|
from rest_framework import serializers
|
2023-08-26 09:36:47 +03:00
|
|
|
from rest_framework.generics import get_object_or_404
|
2023-08-26 01:17:47 +03:00
|
|
|
|
2023-08-26 09:36:47 +03:00
|
|
|
from pitch_deck_generator.decks.models import (
|
|
|
|
PitchDeck,
|
|
|
|
Question,
|
|
|
|
QuestionAnswer,
|
|
|
|
QuestionAnswerPhoto,
|
2023-08-26 09:53:03 +03:00
|
|
|
QuestionDeckHint,
|
2023-08-26 09:36:47 +03:00
|
|
|
)
|
2023-08-26 01:17:47 +03:00
|
|
|
|
|
|
|
|
|
|
|
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 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:
|
2023-08-26 09:36:47 +03:00
|
|
|
question_id = (
|
|
|
|
self.context["view"].kwargs["question_id"]
|
|
|
|
if "question_id" in self.context["view"].kwargs
|
|
|
|
else 1
|
|
|
|
)
|
2023-08-26 01:17:47 +03:00
|
|
|
if q := QuestionDeckHint.objects.filter(
|
2023-08-26 09:36:47 +03:00
|
|
|
question_id=question_id,
|
|
|
|
deck_id=self.context["view"].kwargs["deck_id"],
|
2023-08-26 01:17:47 +03:00
|
|
|
):
|
|
|
|
return q.first().hint
|
2023-08-26 09:36:47 +03:00
|
|
|
return {}
|
2023-08-26 01:17:47 +03:00
|
|
|
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
|
2023-08-26 09:36:47 +03:00
|
|
|
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"]
|
|
|
|
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 str:
|
|
|
|
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 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":
|
|
|
|
for slug in [x["slug"] for x in params["scrollbars"]]:
|
|
|
|
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 (params["min_value"] <= answer[slug] <= params["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, 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):
|
|
|
|
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")
|
|
|
|
|
|
|
|
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"]
|
|
|
|
q.save()
|
|
|
|
|
|
|
|
s = [
|
|
|
|
key
|
|
|
|
for key, val in validated_data.items()
|
|
|
|
if isinstance(val, TemporaryUploadedFile) and key != "file"
|
|
|
|
]
|
|
|
|
if "file" in validated_data:
|
|
|
|
QuestionAnswerPhoto.objects.create(answer=q, file=validated_data["file"])
|
|
|
|
elif s:
|
|
|
|
s.sort(key=lambda x: int(x.split("_")[1]))
|
|
|
|
for key in s:
|
|
|
|
QuestionAnswerPhoto.objects.create(answer=q, file=validated_data[key])
|
2023-08-26 10:44:23 +03:00
|
|
|
|
2023-08-26 09:36:47 +03:00
|
|
|
return q
|