From 00670dd16f85aeb8efbb416819f55fb7e2fdad67 Mon Sep 17 00:00:00 2001 From: Mike Olund Date: Mon, 27 Mar 2017 07:52:45 -0700 Subject: [PATCH] DIV-185 - split out step_completeness from user_response lib --- edivorce/apps/core/tests.py | 2 +- edivorce/apps/core/utils/step_completeness.py | 82 ++++++++++++++++++ edivorce/apps/core/utils/user_response.py | 86 ++----------------- edivorce/apps/core/views/main.py | 4 +- 4 files changed, 90 insertions(+), 84 deletions(-) create mode 100644 edivorce/apps/core/utils/step_completeness.py diff --git a/edivorce/apps/core/tests.py b/edivorce/apps/core/tests.py index 4338cf0e..a77de648 100644 --- a/edivorce/apps/core/tests.py +++ b/edivorce/apps/core/tests.py @@ -1,6 +1,6 @@ from django.test import TestCase from edivorce.apps.core.models import UserResponse, Question, BceidUser -from edivorce.apps.core.utils.user_response import is_complete +from edivorce.apps.core.utils.step_completeness import is_complete from edivorce.apps.core.utils.question_step_mapping import question_step_mapping diff --git a/edivorce/apps/core/utils/step_completeness.py b/edivorce/apps/core/utils/step_completeness.py new file mode 100644 index 00000000..02896d7e --- /dev/null +++ b/edivorce/apps/core/utils/step_completeness.py @@ -0,0 +1,82 @@ +from edivorce.apps.core.models import Question +from edivorce.apps.core.utils.question_step_mapping import question_step_mapping + + +def get_step_status(responses_by_step): + status_dict = {} + for step, lst in responses_by_step.items(): + if not lst: + status_dict[step] = "Not started" + else: + if is_complete(step, lst)[0]: + status_dict[step] = "Complete" + else: + status_dict[step] = "Started" + return status_dict + + +def is_complete(step, lst): + """ + Check required field of question for complete state + Required: question is always require user response to be complete + Conditional: question itself is required and depends on the response to this question, + optional question may be also required + """ + if not lst: + return False, [] + question_list = Question.objects.filter(key__in=question_step_mapping[step]) + required_list = list(question_list.filter(required='Required').values_list("key", flat=True)) + conditional_list = list(question_list.filter(required='Conditional')) + + complete = True + missing_responses = [] + + for question_key in required_list: + # everything in the required_list is required + if not __has_value(question_key, lst): + complete = False + missing_responses += [question_key] + + for question in conditional_list: + # find the response to the conditional target + for target in lst: + if target["question_id"] == question.conditional_target: + if __condition_met(question.reveal_response, target, lst): + # the condition was met then the question is required. + # ... so check if it has a value + if not __has_value(question.key, lst): + complete = False + missing_responses += [question.key] + + return complete, missing_responses + + +def __condition_met(reveal_response, target, lst): + # return false if the condition was not met + if target["value"] != reveal_response: + return False + + # return true if the target is not Conditional + if target['question__required'] != 'Conditional': + return True + else: + # if the target is Conitional and the condition was met, check the target next + reveal_response = target["question__reveal_response"] + conditional_target = target["question__conditional_target"] + for new_target in lst: + if new_target["question_id"] == conditional_target: + # recursively search up the tree + return __condition_met(reveal_response, new_target, lst) + + # if the for loop above didn't find the target, then the target question + # is unanswered and the condition was not met + return False + + +def __has_value(key, lst): + for user_response in lst: + if user_response["question_id"] == key: + answer = user_response["value"] + if answer != "" and answer != "[]" and answer != '[["",""]]': + return True + return False diff --git a/edivorce/apps/core/utils/user_response.py b/edivorce/apps/core/utils/user_response.py index ae7d59be..11a53b57 100644 --- a/edivorce/apps/core/utils/user_response.py +++ b/edivorce/apps/core/utils/user_response.py @@ -15,23 +15,14 @@ def get_responses_from_db_grouped_by_steps(bceid_user): responses = UserResponse.objects.filter(bceid_user=bceid_user) responses_dict = {} for step, questions in question_step_mapping.items(): - responses_dict[step] = responses.filter(question_id__in=questions).exclude(value__in=['', '[]', '[["",""]]']).order_by('question').values('question_id', 'value', 'question__name', 'question__required', 'question__conditional_target', 'question__reveal_response') + responses_dict[step] = responses.filter(question_id__in=questions).exclude( + value__in=['', '[]', '[["",""]]']).order_by('question').values('question_id', 'value', 'question__name', + 'question__required', + 'question__conditional_target', + 'question__reveal_response') return responses_dict -def get_step_status(responses_by_step): - status_dict = {} - for step, lst in responses_by_step.items(): - if not lst: - status_dict[step] = "Not started" - else: - if is_complete(step, lst)[0]: - status_dict[step] = "Complete" - else: - status_dict[step] = "Started" - return status_dict - - def get_responses_from_session(request): return sorted(request.session.items()) @@ -84,70 +75,3 @@ def copy_session_to_db(request, bceid_user): # clear the response from the session request.session[q.key] = None - - -def is_complete(step, lst): - """ - Check required field of question for complete state - Required: question is always require user response to be complete - Conditional: question itself is required and depends on the response to this question, - optional question may be also required - """ - if not lst: - return False, [] - question_list = Question.objects.filter(key__in=question_step_mapping[step]) - required_list = list(question_list.filter(required='Required').values_list("key", flat=True)) - conditional_list = list(question_list.filter(required='Conditional')) - - complete = True - missing_responses = [] - - for question_key in required_list: - # everything in the required_list is required - if not has_value(question_key, lst): - complete = False - missing_responses += [question_key] - - for question in conditional_list: - # find the response to the conditional target - for target in lst: - if target["question_id"] == question.conditional_target: - if condition_met(question.reveal_response, target, lst): - # question is required - if not has_value(question.key, lst): - complete = False - missing_responses += [question.key] - - return complete, missing_responses - - -def condition_met(reveal_response, target, lst): - - # return false if the condition was not met - if target["value"] != reveal_response: - return False - - # return true if the target is not Conditional - if target['question__required'] != 'Conditional': - return True - else: - # if the target is Conitional and the condition was met, check the target next - reveal_response = target["question__reveal_response"] - conditional_target = target["question__conditional_target"] - for new_target in lst: - if new_target["question_id"] == conditional_target: - # recursively search up the tree - return condition_met(reveal_response, new_target, lst) - - # if the for loop above didn't find the target, then the target question - # is unanswered and the condition was not met - return False - -def has_value(key, lst): - for user_response in lst: - if user_response["question_id"] == key: - answer = user_response["value"] - if answer != "" and answer != "[]" and answer != '[["",""]]': - return True - return False - diff --git a/edivorce/apps/core/views/main.py b/edivorce/apps/core/views/main.py index ead73337..70419e7a 100644 --- a/edivorce/apps/core/views/main.py +++ b/edivorce/apps/core/views/main.py @@ -7,8 +7,8 @@ from ..decorators import bceid_required import datetime from ..models import BceidUser from ..utils.user_response import get_responses_from_db, get_responses_from_db_grouped_by_steps, \ - get_responses_from_session, copy_session_to_db, get_step_status, is_complete, \ - get_responses_from_session_grouped_by_steps + get_responses_from_session, copy_session_to_db, get_responses_from_session_grouped_by_steps +from ..utils.step_completeness import get_step_status, is_complete from edivorce.apps.core.utils.question_step_mapping import list_of_registries