Skip to content

Commit

Permalink
fix(historical): use matching question for historicalanswer
Browse files Browse the repository at this point in the history
Use correct historical question for finding answer type.
  • Loading branch information
Stefan Borer committed Jun 3, 2020
1 parent b5805b1 commit 097cdcb
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 34 deletions.
12 changes: 10 additions & 2 deletions caluma/caluma_form/historical_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ..caluma_core.types import ConnectionField, CountableConnectionBase
from . import models
from .schema import (
QUESTION_ANSWER_TYPES,
Answer,
DateAnswer,
FileAnswer,
Expand All @@ -16,7 +17,6 @@
ListAnswer,
StringAnswer,
TableAnswer,
resolve_answer,
)
from .storage_clients import client

Expand All @@ -39,7 +39,15 @@ def historical_qs_as_of(queryset, date, pk_attr):


def resolve_historical_answer(answer):
answer_type = resolve_answer(answer)
"""Get answer type from question as_of historical answer time."""

# as_of returns a generator rather than a queryset, see link above
question = next(
q_as_of
for q_as_of in models.Question.history.as_of(answer.history_date)
if q_as_of.pk == answer.question_id
)
answer_type = QUESTION_ANSWER_TYPES[question.type]
return eval(f"Historical{answer_type.__name__}")


Expand Down
65 changes: 33 additions & 32 deletions caluma/caluma_form/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,13 @@


def resolve_answer(answer):
question_answer_types = {
models.Question.TYPE_MULTIPLE_CHOICE: ListAnswer,
models.Question.TYPE_INTEGER: IntegerAnswer,
models.Question.TYPE_FLOAT: FloatAnswer,
models.Question.TYPE_DATE: DateAnswer,
models.Question.TYPE_CHOICE: StringAnswer,
models.Question.TYPE_TEXTAREA: StringAnswer,
models.Question.TYPE_TEXT: StringAnswer,
models.Question.TYPE_TABLE: TableAnswer,
models.Question.TYPE_FILE: FileAnswer,
models.Question.TYPE_DYNAMIC_CHOICE: StringAnswer,
models.Question.TYPE_DYNAMIC_MULTIPLE_CHOICE: ListAnswer,
}
if answer.question.type == models.Question.TYPE_STATIC:
raise Exception('Questions of type "static" should never have an answer!')

return question_answer_types[answer.question.type]
return QUESTION_ANSWER_TYPES[answer.question.type]


def resolve_question(question):
QUESTION_OBJECT_TYPE = {
models.Question.TYPE_TEXT: TextQuestion,
models.Question.TYPE_FLOAT: FloatQuestion,
models.Question.TYPE_CHOICE: ChoiceQuestion,
models.Question.TYPE_INTEGER: IntegerQuestion,
models.Question.TYPE_MULTIPLE_CHOICE: MultipleChoiceQuestion,
models.Question.TYPE_DYNAMIC_CHOICE: DynamicChoiceQuestion,
models.Question.TYPE_DYNAMIC_MULTIPLE_CHOICE: DynamicMultipleChoiceQuestion,
models.Question.TYPE_TEXTAREA: TextareaQuestion,
models.Question.TYPE_DATE: DateQuestion,
models.Question.TYPE_TABLE: TableQuestion,
models.Question.TYPE_FORM: FormQuestion,
models.Question.TYPE_FILE: FileQuestion,
models.Question.TYPE_STATIC: StaticQuestion,
}

return QUESTION_OBJECT_TYPE[question.type]
return QUESTION_OBJECT_TYPES[question.type]


class FormDjangoObjectType(DjangoObjectType):
Expand Down Expand Up @@ -963,3 +933,34 @@ def resolve_all_format_validators(self, info):

def resolve_document_validity(self, info, id):
return validate_document(info, id)


QUESTION_ANSWER_TYPES = {
models.Question.TYPE_MULTIPLE_CHOICE: ListAnswer,
models.Question.TYPE_INTEGER: IntegerAnswer,
models.Question.TYPE_FLOAT: FloatAnswer,
models.Question.TYPE_DATE: DateAnswer,
models.Question.TYPE_CHOICE: StringAnswer,
models.Question.TYPE_TEXTAREA: StringAnswer,
models.Question.TYPE_TEXT: StringAnswer,
models.Question.TYPE_TABLE: TableAnswer,
models.Question.TYPE_FILE: FileAnswer,
models.Question.TYPE_DYNAMIC_CHOICE: StringAnswer,
models.Question.TYPE_DYNAMIC_MULTIPLE_CHOICE: ListAnswer,
}

QUESTION_OBJECT_TYPES = {
models.Question.TYPE_TEXT: TextQuestion,
models.Question.TYPE_FLOAT: FloatQuestion,
models.Question.TYPE_CHOICE: ChoiceQuestion,
models.Question.TYPE_INTEGER: IntegerQuestion,
models.Question.TYPE_MULTIPLE_CHOICE: MultipleChoiceQuestion,
models.Question.TYPE_DYNAMIC_CHOICE: DynamicChoiceQuestion,
models.Question.TYPE_DYNAMIC_MULTIPLE_CHOICE: DynamicMultipleChoiceQuestion,
models.Question.TYPE_TEXTAREA: TextareaQuestion,
models.Question.TYPE_DATE: DateQuestion,
models.Question.TYPE_TABLE: TableQuestion,
models.Question.TYPE_FORM: FormQuestion,
models.Question.TYPE_FILE: FileQuestion,
models.Question.TYPE_STATIC: StaticQuestion,
}
136 changes: 136 additions & 0 deletions caluma/caluma_form/tests/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,139 @@ def test_historical_table_answer(
result = schema_executor(historical_query, variables=variables)
assert not result.errors
snapshot.assert_match(result.data)


def test_history_answer_type(
db,
answer_factory,
form_question_factory,
document_factory,
form,
schema_executor,
admin_schema_executor,
):
"""Test that the answer type is resolved correctly through history.
The answer type should resolve to the type of the question of the given time (as_of).
Resolve type correctly even if the question type of a given question-slug changed.
"""
document = document_factory(form=form)

old_question = form_question_factory(
question__type=models.Question.TYPE_TEXT, form=form
).question

# create first answer type text
query = """
mutation saveAnswer($input: SaveDocumentStringAnswerInput!) {
saveDocumentStringAnswer(input: $input) {
clientMutationId
}
}
"""

result = admin_schema_executor(
query,
variables={
"input": {
"question": str(old_question.pk),
"value": "some text answer",
"document": str(document.pk),
}
},
)

old_date = timezone.now()
old_answer = models.Answer.objects.get()

historical_answer = old_answer.history.first()
historical_question = old_question.history.first()

# delete old_question and create one with identical slug but different type
old_question.delete()
new_question = form_question_factory(
form=form,
question__slug=historical_question.slug,
question__type=models.Question.TYPE_INTEGER,
).question

query = """
mutation MyIntegerAnswer($input: SaveDocumentIntegerAnswerInput!) {
saveDocumentIntegerAnswer (input: $input) {
answer {
id
}
}
}
"""

# add new integer answer
result = admin_schema_executor(
query,
variables={
"input": {
"question": str(new_question.pk),
"value": 123,
"document": str(document.pk),
}
},
)
assert not result.errors

historical_query = """
query documentAsOf($id: ID!, $asOf: DateTime!) {
documentAsOf (id: $id, asOf: $asOf) {
meta
documentId
historicalAnswers (asOf: $asOf) {
edges {
node {
__typename
...on HistoricalStringAnswer {
stringValue: value
}
...on HistoricalIntegerAnswer {
integerValue: value
}
}
}
}
}
}
"""

# old date resolves to the string value
variables = {"id": document.pk, "asOf": old_date}
result = admin_schema_executor(historical_query, variables=variables)

assert not result.errors
assert (
result.data["documentAsOf"]["historicalAnswers"]["edges"][0]["node"][
"stringValue"
]
== historical_answer.value
)
assert (
result.data["documentAsOf"]["historicalAnswers"]["edges"][0]["node"][
"__typename"
]
== "HistoricalStringAnswer"
)

# current date resolves to integer value
variables = {"id": document.pk, "asOf": timezone.now()}
result = admin_schema_executor(historical_query, variables=variables)

assert not result.errors
assert (
result.data["documentAsOf"]["historicalAnswers"]["edges"][0]["node"][
"integerValue"
]
== 123
)
assert (
result.data["documentAsOf"]["historicalAnswers"]["edges"][0]["node"][
"__typename"
]
== "HistoricalIntegerAnswer"
)

0 comments on commit 097cdcb

Please sign in to comment.