From 77916a92ceb84c61c60ad256eedeafeab4fd8273 Mon Sep 17 00:00:00 2001 From: Mitan Omar Date: Thu, 5 Oct 2023 16:01:41 +0200 Subject: [PATCH] feat(caluma_form): add min/max_date to date question --- caluma/caluma_form/factories.py | 3 + caluma/caluma_form/models.py | 24 ++++ caluma/caluma_form/schema.py | 2 + caluma/caluma_form/serializers.py | 18 ++- .../tests/__snapshots__/test_question.ambr | 114 ++++++++++++++++++ caluma/caluma_form/tests/test_question.py | 78 +++++++++++- caluma/tests/__snapshots__/test_schema.ambr | 4 + 7 files changed, 241 insertions(+), 2 deletions(-) diff --git a/caluma/caluma_form/factories.py b/caluma/caluma_form/factories.py index 209a29dc5..4f99a3e5a 100644 --- a/caluma/caluma_form/factories.py +++ b/caluma/caluma_form/factories.py @@ -74,6 +74,9 @@ class QuestionFactory(DjangoModelFactory): ) calc_dependents = [] + min_date = None + max_date = None + # action button question action = Maybe( "is_action_button", diff --git a/caluma/caluma_form/models.py b/caluma/caluma_form/models.py index 2e3b3649e..c80cb802f 100644 --- a/caluma/caluma_form/models.py +++ b/caluma/caluma_form/models.py @@ -1,8 +1,10 @@ import uuid from functools import wraps +import dateutil.parser from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.indexes import GinIndex +from django.core.serializers.json import DjangoJSONEncoder, json from django.db import models, transaction from django.utils.functional import cached_property from localized_fields.fields import LocalizedField, LocalizedTextField @@ -220,6 +222,28 @@ def min_value(self): def min_value(self, value): self.configuration["min_value"] = value + @property + def max_date(self): + if (max_date := self.configuration.get("max_date")) and max_date: + return dateutil.parser.parse(json.loads(max_date)).date() + + @max_date.setter + def max_date(self, value): + self.configuration["max_date"] = ( + json.dumps(value, cls=DjangoJSONEncoder) if value is not None else None + ) + + @property + def min_date(self): + if (min_date := self.configuration.get("min_date")) and min_date: + return dateutil.parser.parse(json.loads(min_date)).date() + + @min_date.setter + def min_date(self, value): + self.configuration["min_date"] = ( + json.dumps(value, cls=DjangoJSONEncoder) if value is not None else None + ) + @property def action(self): return self.configuration.get("action") diff --git a/caluma/caluma_form/schema.py b/caluma/caluma_form/schema.py index c72597ea3..3b6cfceae 100644 --- a/caluma/caluma_form/schema.py +++ b/caluma/caluma_form/schema.py @@ -262,6 +262,8 @@ class Meta: class DateQuestion(QuestionQuerysetMixin, FormDjangoObjectType): hint_text = graphene.String() + min_date = graphene.Date() + max_date = graphene.Date() default_answer = graphene.Field("caluma.caluma_form.schema.DateAnswer") class Meta: diff --git a/caluma/caluma_form/serializers.py b/caluma/caluma_form/serializers.py index f1598c2f6..1005cbdda 100644 --- a/caluma/caluma_form/serializers.py +++ b/caluma/caluma_form/serializers.py @@ -240,12 +240,28 @@ class Meta(SaveQuestionSerializer.Meta): class SaveDateQuestionSerializer(SaveQuestionSerializer): + min_date = DateField("%Y-%m-%d", required=False, allow_null=True) + max_date = DateField("%Y-%m-%d", required=False, allow_null=True) + def validate(self, data): + if ( + (min_date := data.get("min_date")) + and (max_date := data.get("max_date")) + and max_date < min_date + ): + raise exceptions.ValidationError( + f"max_value {max_date} is smaller than {min_date}" + ) + data["type"] = models.Question.TYPE_DATE return super().validate(data) class Meta(SaveQuestionSerializer.Meta): - fields = SaveQuestionSerializer.Meta.fields + ["hint_text"] + fields = SaveQuestionSerializer.Meta.fields + [ + "hint_text", + "min_date", + "max_date", + ] class SaveQuestionOptionsMixin(object): diff --git a/caluma/caluma_form/tests/__snapshots__/test_question.ambr b/caluma/caluma_form/tests/__snapshots__/test_question.ambr index 38229c7aa..f3e5c929e 100644 --- a/caluma/caluma_form/tests/__snapshots__/test_question.ambr +++ b/caluma/caluma_form/tests/__snapshots__/test_question.ambr @@ -524,6 +524,120 @@ }), }) # --- +# name: test_save_date_question[False-date-2022-01-31-2022-12-31-True] + dict({ + 'saveDateQuestion': dict({ + 'clientMutationId': 'testid', + 'question': dict({ + '__typename': 'DateQuestion', + 'defaultAnswer': None, + 'hintText': '', + 'id': 'RGF0ZVF1ZXN0aW9uOmVudmlyb25tZW50YWwtdGVu', + 'label': 'Bonnie Moreno', + 'maxDate': '2022-12-31', + 'meta': dict({ + }), + 'minDate': '2022-01-31', + 'slug': 'environmental-ten', + }), + }), + }) +# --- +# name: test_save_date_question[False-date-2022-01-31-None-True] + dict({ + 'saveDateQuestion': dict({ + 'clientMutationId': 'testid', + 'question': dict({ + '__typename': 'DateQuestion', + 'defaultAnswer': None, + 'hintText': '', + 'id': 'RGF0ZVF1ZXN0aW9uOmVudmlyb25tZW50YWwtdGVu', + 'label': 'Bonnie Moreno', + 'maxDate': None, + 'meta': dict({ + }), + 'minDate': '2022-01-31', + 'slug': 'environmental-ten', + }), + }), + }) +# --- +# name: test_save_date_question[False-date-None-2022-12-31-True] + dict({ + 'saveDateQuestion': dict({ + 'clientMutationId': 'testid', + 'question': dict({ + '__typename': 'DateQuestion', + 'defaultAnswer': None, + 'hintText': '', + 'id': 'RGF0ZVF1ZXN0aW9uOmVudmlyb25tZW50YWwtdGVu', + 'label': 'Bonnie Moreno', + 'maxDate': '2022-12-31', + 'meta': dict({ + }), + 'minDate': None, + 'slug': 'environmental-ten', + }), + }), + }) +# --- +# name: test_save_date_question[True-date-2022-01-31-2022-12-31-True] + dict({ + 'saveDateQuestion': dict({ + 'clientMutationId': 'testid', + 'question': dict({ + '__typename': 'DateQuestion', + 'defaultAnswer': None, + 'hintText': '', + 'id': 'RGF0ZVF1ZXN0aW9uOmVudmlyb25tZW50YWwtdGVu', + 'label': 'Bonnie Moreno', + 'maxDate': '2022-12-31', + 'meta': dict({ + }), + 'minDate': '2022-01-31', + 'slug': 'environmental-ten', + }), + }), + }) +# --- +# name: test_save_date_question[True-date-2022-01-31-None-True] + dict({ + 'saveDateQuestion': dict({ + 'clientMutationId': 'testid', + 'question': dict({ + '__typename': 'DateQuestion', + 'defaultAnswer': None, + 'hintText': '', + 'id': 'RGF0ZVF1ZXN0aW9uOmVudmlyb25tZW50YWwtdGVu', + 'label': 'Bonnie Moreno', + 'maxDate': None, + 'meta': dict({ + }), + 'minDate': '2022-01-31', + 'slug': 'environmental-ten', + }), + }), + }) +# --- +# name: test_save_date_question[True-date-None-2022-12-31-True] + dict({ + 'saveDateQuestion': dict({ + 'clientMutationId': 'testid', + 'question': dict({ + '__typename': 'DateQuestion', + 'defaultAnswer': None, + 'hintText': '', + 'id': 'RGF0ZVF1ZXN0aW9uOmVudmlyb25tZW50YWwtdGVu', + 'label': 'Bonnie Moreno', + 'maxDate': '2022-12-31', + 'meta': dict({ + }), + 'minDate': None, + 'slug': 'environmental-ten', + }), + }), + }) +# --- # name: test_save_dynamic_choice_question[dynamic_choice-False] dict({ 'saveDynamicChoiceQuestion': dict({ diff --git a/caluma/caluma_form/tests/test_question.py b/caluma/caluma_form/tests/test_question.py index 3d1037a66..0e28f6891 100644 --- a/caluma/caluma_form/tests/test_question.py +++ b/caluma/caluma_form/tests/test_question.py @@ -1,4 +1,5 @@ import pytest +from django.utils.timezone import datetime from caluma.caluma_core.relay import extract_global_id from caluma.caluma_core.tests import ( @@ -369,7 +370,6 @@ def test_save_textarea_question(db, question, answer, schema_executor): } } """ - question.default_answer = answer question.hint_text = "test" question.save() @@ -389,6 +389,82 @@ def test_save_textarea_question(db, question, answer, schema_executor): assert result.data["saveTextareaQuestion"]["question"]["hintText"] == "test" +@pytest.mark.parametrize( + "question__type, min_date, max_date, success", + [ + (models.Question.TYPE_DATE, "2022-01-31", "2022-12-31", True), + (models.Question.TYPE_DATE, "2022-01-31", None, True), + (models.Question.TYPE_DATE, None, "2022-12-31", True), + (models.Question.TYPE_DATE, "2022-12-01", "2022-01-31", False), + ], +) +@pytest.mark.parametrize("existing_values", [False, True]) +def test_save_date_question( + db, + snapshot, + question, + success, + schema_executor, + min_date, + max_date, + existing_values, +): + query = """ + mutation SaveDateQuestion($input: SaveDateQuestionInput!) { + saveDateQuestion(input: $input) { + question { + id + slug + label + meta + __typename + ... on DateQuestion { + minDate + maxDate + hintText + defaultAnswer { + value + } + } + } + clientMutationId + } + } + """ + + if max_date: + question.max_date = datetime.strptime(max_date, "%Y-%m-%d").date() + if min_date: + question.min_date = datetime.strptime(min_date, "%Y-%m-%d").date() + + inp = { + "input": extract_serializer_input_fields( + serializers.SaveDateQuestionSerializer, question + ) + } + + if existing_values: + question.save() + + result = schema_executor(query, variable_values=inp) + assert not bool(result.errors) == success + if not success: + return + + snapshot.assert_match(result.data) + + question.refresh_from_db() + if not min_date: + assert question.min_date is None + else: + assert question.min_date == datetime.strptime(min_date, "%Y-%m-%d").date() + + if not max_date: + assert question.max_date is None + else: + assert question.max_date == datetime.strptime(max_date, "%Y-%m-%d").date() + + @pytest.mark.parametrize( "question__type,question__configuration,answer__value,success", [ diff --git a/caluma/tests/__snapshots__/test_schema.ambr b/caluma/tests/__snapshots__/test_schema.ambr index cd64e5396..08cb78da2 100644 --- a/caluma/tests/__snapshots__/test_schema.ambr +++ b/caluma/tests/__snapshots__/test_schema.ambr @@ -818,6 +818,8 @@ """The ID of the object""" id: ID! + minDate: Date + maxDate: Date } """ @@ -2376,6 +2378,8 @@ meta: JSONString isArchived: Boolean hintText: String + minDate: Date + maxDate: Date clientMutationId: String }