Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(form): multiple files in file questions #1813

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
FROM
"caluma_form_document" INNER JOIN "caluma_form_form" ON ("caluma_form_document"."form_id" = "caluma_form_form"."slug") -- qs ref
),
answer_b4215 AS (SELECT "caluma_form_answer"."created_at",
answer_af542 AS (SELECT "caluma_form_answer"."created_at",
"caluma_form_answer"."modified_at",
"caluma_form_answer"."created_by_user",
"caluma_form_answer"."created_by_group",
Expand All @@ -76,8 +76,7 @@
"caluma_form_answer"."value",
"caluma_form_answer"."meta",
"caluma_form_answer"."document_id",
"caluma_form_answer"."date",
"caluma_form_answer"."file_id"
"caluma_form_answer"."date"
FROM
"caluma_form_answer" INNER JOIN "caluma_form_question" ON ("caluma_form_answer"."question_id" = "caluma_form_question"."slug") -- qs ref
)
Expand All @@ -92,22 +91,22 @@
FROM document_2a07e AS "document_2a07e"
LEFT JOIN (
SELECT DISTINCT ON (document_id)
(answer_b4215.value #>>'{}') AS "analytics_result_blablub",
(answer_af542.value #>>'{}') AS "analytics_result_blablub",
"document_id"
FROM answer_b4215 AS "answer_b4215"
FROM answer_af542 AS "answer_af542"
WHERE "question_id" = 'top_question'
ORDER BY document_id


) AS "answer_b4215_caea5" ON (document_2a07e.id = "answer_b4215_caea5".document_id)
) AS "answer_af542_caea5" ON (document_2a07e.id = "answer_af542_caea5".document_id)
WHERE form_id = 'top_form'
ORDER BY id


) AS "document_2a07e_27f15" ON (case_ac50e.document_id = "document_2a07e_27f15".id)


) AS analytics_247d1
) AS analytics_2a8de
-- PARAMS:

''',
Expand Down Expand Up @@ -156,7 +155,7 @@
FROM
"caluma_form_document" INNER JOIN "caluma_form_form" ON ("caluma_form_document"."form_id" = "caluma_form_form"."slug") -- qs ref
),
answer_b4215 AS (SELECT "caluma_form_answer"."created_at",
answer_af542 AS (SELECT "caluma_form_answer"."created_at",
"caluma_form_answer"."modified_at",
"caluma_form_answer"."created_by_user",
"caluma_form_answer"."created_by_group",
Expand All @@ -167,8 +166,7 @@
"caluma_form_answer"."value",
"caluma_form_answer"."meta",
"caluma_form_answer"."document_id",
"caluma_form_answer"."date",
"caluma_form_answer"."file_id"
"caluma_form_answer"."date"
FROM
"caluma_form_answer" INNER JOIN "caluma_form_question" ON ("caluma_form_answer"."question_id" = "caluma_form_question"."slug") -- qs ref
)
Expand All @@ -183,22 +181,22 @@
FROM document_2a07e AS "document_2a07e"
LEFT JOIN (
SELECT DISTINCT ON (document_id)
(answer_b4215.value #>>'{}') AS "analytics_result_blablub",
(answer_af542.value #>>'{}') AS "analytics_result_blablub",
"document_id"
FROM answer_b4215 AS "answer_b4215"
FROM answer_af542 AS "answer_af542"
WHERE "question_id" = 'top_question'
ORDER BY document_id


) AS "answer_b4215_caea5" ON (document_2a07e.id = "answer_b4215_caea5".document_id)
) AS "answer_af542_caea5" ON (document_2a07e.id = "answer_af542_caea5".document_id)
WHERE form_id = 'top_form'
ORDER BY id


) AS "document_2a07e_27f15" ON (case_ac50e.document_id = "document_2a07e_27f15".id)


) AS analytics_247d1
) AS analytics_2a8de
WHERE analytics_result_blablub IN (%(flt_analytics_result_blablub_8201d)s, %(flt_analytics_result_blablub_8e5e6)s)
-- PARAMS:
-- flt_analytics_result_blablub_8201d: Shelly Watson
Expand Down Expand Up @@ -246,7 +244,7 @@
FROM
"caluma_form_document" INNER JOIN "caluma_form_form" ON ("caluma_form_document"."form_id" = "caluma_form_form"."slug") -- qs ref
),
answer_b4215 AS (SELECT "caluma_form_answer"."created_at",
answer_af542 AS (SELECT "caluma_form_answer"."created_at",
"caluma_form_answer"."modified_at",
"caluma_form_answer"."created_by_user",
"caluma_form_answer"."created_by_group",
Expand All @@ -257,8 +255,7 @@
"caluma_form_answer"."value",
"caluma_form_answer"."meta",
"caluma_form_answer"."document_id",
"caluma_form_answer"."date",
"caluma_form_answer"."file_id"
"caluma_form_answer"."date"
FROM
"caluma_form_answer" INNER JOIN "caluma_form_question" ON ("caluma_form_answer"."question_id" = "caluma_form_question"."slug") -- qs ref
)
Expand All @@ -273,22 +270,22 @@
FROM document_2a07e AS "document_2a07e"
LEFT JOIN (
SELECT DISTINCT ON (document_id)
(answer_b4215.value #>>'{}') AS "analytics_result_blablub",
(answer_af542.value #>>'{}') AS "analytics_result_blablub",
"document_id"
FROM answer_b4215 AS "answer_b4215"
FROM answer_af542 AS "answer_af542"
WHERE "question_id" = 'top_question'
ORDER BY document_id


) AS "answer_b4215_caea5" ON (document_2a07e.id = "answer_b4215_caea5".document_id)
) AS "answer_af542_caea5" ON (document_2a07e.id = "answer_af542_caea5".document_id)
WHERE form_id = 'top_form'
ORDER BY id


) AS "document_2a07e_27f15" ON (case_ac50e.document_id = "document_2a07e_27f15".id)


) AS analytics_247d1
) AS analytics_2a8de
-- PARAMS:

''',
Expand Down Expand Up @@ -333,7 +330,7 @@
FROM
"caluma_form_document" INNER JOIN "caluma_form_form" ON ("caluma_form_document"."form_id" = "caluma_form_form"."slug") -- qs ref
),
answer_b4215 AS (SELECT "caluma_form_answer"."created_at",
answer_af542 AS (SELECT "caluma_form_answer"."created_at",
"caluma_form_answer"."modified_at",
"caluma_form_answer"."created_by_user",
"caluma_form_answer"."created_by_group",
Expand All @@ -344,8 +341,7 @@
"caluma_form_answer"."value",
"caluma_form_answer"."meta",
"caluma_form_answer"."document_id",
"caluma_form_answer"."date",
"caluma_form_answer"."file_id"
"caluma_form_answer"."date"
FROM
"caluma_form_answer" INNER JOIN "caluma_form_question" ON ("caluma_form_answer"."question_id" = "caluma_form_question"."slug") -- qs ref
)
Expand All @@ -360,22 +356,22 @@
FROM document_2a07e AS "document_2a07e"
LEFT JOIN (
SELECT DISTINCT ON (document_id)
(answer_b4215.value #>>'{}') AS "analytics_result_blablub",
(answer_af542.value #>>'{}') AS "analytics_result_blablub",
"document_id"
FROM answer_b4215 AS "answer_b4215"
FROM answer_af542 AS "answer_af542"
WHERE "question_id" = 'top_question'
ORDER BY document_id


) AS "answer_b4215_caea5" ON (document_2a07e.id = "answer_b4215_caea5".document_id)
) AS "answer_af542_caea5" ON (document_2a07e.id = "answer_af542_caea5".document_id)
WHERE form_id = 'top_form'
ORDER BY id


) AS "document_2a07e_27f15" ON (case_ac50e.document_id = "document_2a07e_27f15".id)


) AS analytics_247d1
) AS analytics_2a8de
WHERE analytics_result_blablub IN (%(flt_analytics_result_blablub_8201d)s, %(flt_analytics_result_blablub_8e5e6)s)
-- PARAMS:
-- flt_analytics_result_blablub_8201d: Shelly Watson
Expand Down
2 changes: 1 addition & 1 deletion caluma/caluma_core/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class Meta:
permission_classes = None

@classmethod
def __init_subclass_with_meta__(
def __init_subclass_with_meta__( # noqa: C901
cls,
lookup_field=None,
lookup_input_kwarg=None,
Expand Down
88 changes: 66 additions & 22 deletions caluma/caluma_form/domain_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rest_framework.exceptions import ValidationError

from caluma.caluma_core.models import BaseModel
from caluma.caluma_core.relay import extract_global_id
from caluma.caluma_form import models, validators
from caluma.caluma_user.models import BaseUser
from caluma.utils import update_model
Expand Down Expand Up @@ -42,13 +43,63 @@ def get_new_answer(cls, data, user, answer):
cls.validate_for_save(data, user, answer=answer, origin=True)
)

files = validated_data.pop("files", None)
if answer is None:
answer = cls.create(validated_data, user)
else:
answer = cls.update(answer, validated_data, user)

# FILES type needs a bit more work due to the reverse
# relation of file -> answer
if answer.question.type == models.Question.TYPE_FILES:
if files is None:
raise ValidationError("Files input must be a list")
cls.update_answer_files(answer, files)

return cls.post_save(answer)

@classmethod
def update_answer_files(cls, answer: models.Answer, files: list):
"""Update the files of a "FILES" answer.

The files parameter is expected to be a list of dicts, where
each entry has a "name" (the file name), and optionally an "id"
for the case when the given file already exists.
"""
if not files:
files = []

updated = []

for file_ in files:
file_id = extract_global_id(file_["id"]) if "id" in file_ else None
file_name = file_["name"]
file_model = (
answer.files.filter(pk=file_id).first()
if file_id
else models.File(answer=answer, name=file_name)
)
if not file_model:
# Client wants to update a file that doesn't exist anymore.
# Reject call - this is an inconsistency as either
# the file was never created, or was deleted in the mean
# time.
raise ValidationError(
f"File with id={file_id} for given answer not found"
)

if file_model.name != file_name and file_id:
# Renaming existing file
file_model.rename(file_name)
file_model.save()
elif not file_id:
# New file
file_model.save()
updated.append(file_model.pk)

for file in answer.files.exclude(pk__in=updated):
file.delete()

@staticmethod
def validate_for_save(
data: dict, user: BaseUser, answer: models.Answer = None, origin: bool = False
Expand All @@ -68,8 +119,8 @@ def validate_for_save(
else models.Document.objects.none()
)
del data["value"]
elif question.type == models.Question.TYPE_FILE and not data.get("file"):
data["file"] = data["value"]
elif question.type == models.Question.TYPE_FILES and not data.get("files"):
data["files"] = data["value"]
del data["value"]
elif question.type == models.Question.TYPE_DATE and not data.get("date"):
data["date"] = data["value"]
Expand All @@ -91,35 +142,35 @@ def post_save(answer: models.Answer) -> models.Answer:
# TODO emit events
return answer

@staticmethod
@classmethod
@transaction.atomic
def create(validated_data: dict, user: Optional[BaseUser] = None) -> models.Answer:
if validated_data["question"].type == models.Question.TYPE_FILE:
validated_data = __class__.set_file(validated_data)
def create(
cls, validated_data: dict, user: Optional[BaseUser] = None
) -> models.Answer:

if validated_data["question"].type == models.Question.TYPE_TABLE:
documents = validated_data.pop("documents")

files = validated_data.pop("files", None)
answer = BaseLogic.create(models.Answer, validated_data, user)

if validated_data["question"].type == models.Question.TYPE_FILES:
cls.update_answer_files(answer, files)

if answer.question.type == models.Question.TYPE_TABLE:
answer.create_answer_documents(documents)

return answer

@staticmethod
@classmethod
@transaction.atomic
def update(answer, validated_data, user: Optional[BaseUser] = None):
def update(cls, answer, validated_data, user: Optional[BaseUser] = None):
if answer.question.type == models.Question.TYPE_TABLE:
documents = validated_data.pop("documents")
answer.unlink_unused_rows(docs_to_keep=documents)

if (
answer.question.type == models.Question.TYPE_FILE
and answer.file.name is not validated_data["file"]
):
answer.file.delete()
validated_data = __class__.set_file(validated_data)
if answer.question.type == models.Question.TYPE_FILES:
cls.update_answer_files(answer, validated_data.pop("files", None))

BaseLogic.update(answer, validated_data, user)

Expand All @@ -129,21 +180,14 @@ def update(answer, validated_data, user: Optional[BaseUser] = None):
answer.refresh_from_db()
return answer

@staticmethod
def set_file(validated_data):
file_name = validated_data.get("file")
file = models.File.objects.create(name=file_name)
validated_data["file"] = file
return validated_data


class SaveDefaultAnswerLogic(SaveAnswerLogic):
@staticmethod
def validate_for_save(
data: dict, user: BaseUser, answer: models.Answer = None, origin: bool = False
) -> dict:
if data["question"].type in [
models.Question.TYPE_FILE,
models.Question.TYPE_FILES,
models.Question.TYPE_STATIC,
models.Question.TYPE_DYNAMIC_CHOICE,
models.Question.TYPE_DYNAMIC_MULTIPLE_CHOICE,
Expand Down
Loading