diff --git a/mpbackend/settings.py b/mpbackend/settings.py index bff8222..bfa5e8e 100644 --- a/mpbackend/settings.py +++ b/mpbackend/settings.py @@ -54,7 +54,6 @@ "drf_spectacular", "corsheaders", "django_filters", - "memoize", ] MIDDLEWARE = [ diff --git a/profiles/api/views.py b/profiles/api/views.py index 988b772..3f61bf8 100644 --- a/profiles/api/views.py +++ b/profiles/api/views.py @@ -127,21 +127,28 @@ def update_postal_code_result(user): return postal_code = None postal_code_type = None - if user.profile.postal_code: - postal_code, _ = PostalCode.objects.get_or_create( - postal_code=user.profile.postal_code - ) - postal_code_type, _ = PostalCodeType.objects.get_or_create( - type_name=PostalCodeType.HOME_POSTAL_CODE - ) - if user.profile.optional_postal_code: - postal_code, _ = PostalCode.objects.get_or_create( - postal_code=user.profile.optional_postal_code - ) - postal_code_type, _ = PostalCodeType.objects.get_or_create( - type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + postal_code, _ = PostalCode.objects.get_or_create( + postal_code=user.profile.postal_code + ) + postal_code_type, _ = PostalCodeType.objects.get_or_create( + type_name=PostalCodeType.HOME_POSTAL_CODE + ) + try: + postal_code_result, _ = PostalCodeResult.objects.get_or_create( + postal_code=postal_code, postal_code_type=postal_code_type, result=result ) + except IntegrityError as e: + logger.error(f"IntegrityError while creating PostalCodeResult: {e}") + return + postal_code_result.count += 1 + postal_code_result.save() + postal_code, _ = PostalCode.objects.get_or_create( + postal_code=user.profile.optional_postal_code + ) + postal_code_type, _ = PostalCodeType.objects.get_or_create( + type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + ) try: postal_code_result, _ = PostalCodeResult.objects.get_or_create( postal_code=postal_code, postal_code_type=postal_code_type, result=result diff --git a/profiles/management/commands/import_questions.py b/profiles/management/commands/import_questions.py index e40b82b..b28ac45 100644 --- a/profiles/management/commands/import_questions.py +++ b/profiles/management/commands/import_questions.py @@ -121,6 +121,13 @@ def get_and_create_results(data: pd.DataFrame) -> list: return results +@db.transaction.atomic +def update_results_num_options(): + for result in Result.objects.all(): + result.num_options = Option.objects.filter(results=result).count() + result.save() + + @db.transaction.atomic def create_sub_question_condition(row_data: str, sub_question: SubQuestion): question_number, option_order_number = row_data.split(".") @@ -305,3 +312,4 @@ def handle(self, *args, **options): excel_data = excel_data.fillna("").replace([""], [None]) results = get_and_create_results(excel_data) save_questions(excel_data, results) + update_results_num_options() diff --git a/profiles/migrations/0021_result_num_options.py b/profiles/migrations/0021_result_num_options.py new file mode 100644 index 0000000..d539c84 --- /dev/null +++ b/profiles/migrations/0021_result_num_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2 on 2024-03-05 07:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("profiles", "0020_postalcode_postalcodetype_add_ordering"), + ] + + operations = [ + migrations.AddField( + model_name="result", + name="num_options", + field=models.PositiveSmallIntegerField(null=True), + ), + ] diff --git a/profiles/models.py b/profiles/models.py index 2bb56c8..04486f1 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -60,6 +60,7 @@ class Result(models.Model): topic = models.CharField(max_length=64, null=True) description = models.TextField(null=True) value = models.CharField(max_length=64, null=True) + num_options = models.PositiveSmallIntegerField(null=True) class Meta: ordering = ["id"] @@ -96,6 +97,7 @@ class Meta: class AnswerOther(Answer): # Proxy model that allows registerin Answer model twice to the Admin + class Meta: proxy = True diff --git a/profiles/tests/api/test_answer.py b/profiles/tests/api/test_answer.py index e356819..eb3af19 100644 --- a/profiles/tests/api/test_answer.py +++ b/profiles/tests/api/test_answer.py @@ -4,7 +4,6 @@ from account.models import User from profiles.models import Answer, Option, Question, SubQuestion -from profiles.tests.utils import delete_memoized_functions_cache def test_answer_post_unauthenticated(api_client): @@ -14,16 +13,15 @@ def test_answer_post_unauthenticated(api_client): @pytest.mark.django_db -def test_poll_start(api_client): +def test_start_poll(api_client): User.objects.all().count() == 0 url = reverse("profiles:question-start-poll") response = api_client.post(url) assert response.status_code == 200 - User.objects.all().count() == 1 + assert User.objects.all().count() == 1 @pytest.mark.django_db -@delete_memoized_functions_cache def test_post_answer(api_client_authenticated, users, questions, options): user = users.get(username="test1") assert Answer.objects.count() == 0 @@ -40,7 +38,6 @@ def test_post_answer(api_client_authenticated, users, questions, options): @pytest.mark.django_db -@delete_memoized_functions_cache def test_post_answer_with_other_option(api_client, users, answers, questions, options): user = users.get(username="no answers user") token = Token.objects.create(user=user) @@ -66,7 +63,6 @@ def test_post_answer_with_other_option(api_client, users, answers, questions, op @pytest.mark.django_db -@delete_memoized_functions_cache def test_post_answer_answer_is_updated( api_client_authenticated, users, answers, questions, options ): @@ -129,7 +125,6 @@ def test_post_answer_where_question_not_related_to_option( @pytest.mark.django_db -@delete_memoized_functions_cache def test_answer_get_result(api_client_authenticated, users, answers): url = reverse("profiles:answer-get-result") response = api_client_authenticated.get(url) @@ -138,7 +133,6 @@ def test_answer_get_result(api_client_authenticated, users, answers): @pytest.mark.django_db -@delete_memoized_functions_cache def test_post_answer_where_condition_not_met( api_client, users, @@ -163,7 +157,6 @@ def test_post_answer_where_condition_not_met( @pytest.mark.django_db -@delete_memoized_functions_cache def test_post_answer_where_condition_is_met( api_client, users, diff --git a/profiles/tests/api/test_postal_code_result.py b/profiles/tests/api/test_postal_code_result.py index 35fef6f..358b378 100644 --- a/profiles/tests/api/test_postal_code_result.py +++ b/profiles/tests/api/test_postal_code_result.py @@ -1,148 +1,268 @@ import pytest from django.db.models import Sum +from rest_framework.authtoken.models import Token from rest_framework.reverse import reverse -from account.models import User -from profiles.models import ( - Answer, - PostalCode, - PostalCodeResult, - PostalCodeType, - SubQuestion, -) -from profiles.tests.utils import delete_memoized_functions_cache +from account.models import Profile, User +from profiles.models import Answer, PostalCode, PostalCodeResult, PostalCodeType +from profiles.tests.conftest import NEG, POS + +ANSWER_URL = reverse("profiles:answer-list") @pytest.mark.django_db -@delete_memoized_functions_cache -def test_postal_code_result(api_client, questions, sub_questions, options, results): - num_users = 21 - num_answers = 0 - start_poll_url = reverse("profiles:question-start-poll") - end_poll_url = reverse("profiles:question-end-poll") - answer_url = reverse("profiles:answer-list") - positive_result = results.get(topic="positive") - negative_result = results.get(topic="negative") +def test_postal_code_result_with_postal_code_and_optional_postal_code( + api_client, users, questions, options, results +): + user = users.get(username="no answers user") + token = Token.objects.create(user=user) + api_client.credentials(HTTP_AUTHORIZATION="Token " + token.key) + Profile.objects.filter(user=user).update( + postal_code="20210", optional_postal_code="20220" + ) + question1 = questions.get(number="1") + option = options.get(question=question1, value="no") + response = api_client.post( + ANSWER_URL, {"option": option.id, "question": question1.id} + ) + assert response.status_code == 201 + assert Answer.objects.count() == 1 + response = api_client.post(reverse("profiles:question-end-poll")) + assert response.status_code == 200 + assert PostalCodeResult.objects.count() == 2 + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20210") + ).count + == 1 + ) + assert PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20210") + ).postal_code_type == PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20220") + ).count + == 1 + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20220") + ).result.topic + == "negative" + ) + assert PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20220") + ).postal_code_type == PostalCodeType.objects.get( + type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + ) + user.refresh_from_db() + assert user.postal_code_result_saved is True + + +@pytest.mark.django_db +def test_postal_code_result_with_postal_code_and_without_optional_postal_code( + api_client, users, questions, options, results +): + user = users.get(username="no answers user") + Profile.objects.filter(user=user).update(postal_code="20210") + token = Token.objects.create(user=user) + api_client.credentials(HTTP_AUTHORIZATION="Token " + token.key) question1 = questions.get(number="1") - question2 = questions.get(number="2") - car_sub_q = sub_questions.get(question=question2, description="car") - car_sub_q_options = options.filter(sub_question=car_sub_q) - q1_options = options.filter(question=question1) - questions = {question1: q1_options, car_sub_q: car_sub_q_options} - postal_codes = [None, "20100", "20200", "20210", "20100"] - postal_code_types = [ - None, - PostalCodeType.HOME_POSTAL_CODE, - PostalCodeType.HOME_POSTAL_CODE, - PostalCodeType.OPTIONAL_POSTAL_CODE, - PostalCodeType.OPTIONAL_POSTAL_CODE, - ] + option = options.get(question=question1, value="no") + response = api_client.post( + ANSWER_URL, {"option": option.id, "question": question1.id} + ) + assert response.status_code == 201 + assert Answer.objects.count() == 1 + response = api_client.post(reverse("profiles:question-end-poll")) + assert response.status_code == 200 + assert PostalCodeResult.objects.count() == 2 + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code=20210), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ), + ).count + == 1 + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code=None), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + ), + ).count + == 1 + ) + user.refresh_from_db() + assert user.postal_code_result_saved is True + + +@pytest.mark.django_db +def test_postal_code_result_without_postal_code_and_optional_postal_code( + api_client, users, questions, options, results +): + user = users.get(username="no answers user") + token = Token.objects.create(user=user) + api_client.credentials(HTTP_AUTHORIZATION="Token " + token.key) + question1 = questions.get(number="1") + option = options.get(question=question1, value="no") + response = api_client.post( + ANSWER_URL, {"option": option.id, "question": question1.id} + ) + assert response.status_code == 201 + assert Answer.objects.count() == 1 + response = api_client.post(reverse("profiles:question-end-poll")) + assert response.status_code == 200 + assert PostalCodeResult.objects.count() == 2 + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code=None), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ), + ).count + == 1 + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code=None), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + ), + ).count + == 1 + ) + user.refresh_from_db() + assert user.postal_code_result_saved is True + + +@pytest.mark.django_db +def test_postal_code_result( + api_client, results_test_result, options_test_result, questions_test_result +): + num_users = 5 + num_answers = 0 start_poll_url = reverse("profiles:question-start-poll") + postal_codes = [None, "20100", "20200", "20100", None] + q1 = questions_test_result.get(number="1") + q1_option_pos = options_test_result.get(question=q1, value=POS) + q1_option_neg = options_test_result.get(question=q1, value=NEG) + + # post positive for i in range(num_users): response = api_client.post(start_poll_url) + token = response.json()["token"] assert response.status_code == 200 - token = response.json()["token"] user_id = response.json()["id"] - User.objects.all().count() == 1 + i + assert User.objects.all().count() == 1 + i user = User.objects.get(id=user_id) - index = i % 5 - postal_code_location = postal_code_types[index] - if postal_code_location == PostalCodeType.HOME_POSTAL_CODE: - user.profile.postal_code = postal_codes[index] - elif postal_code_location == PostalCodeType.OPTIONAL_POSTAL_CODE: - user.profile.optional_postal_code = postal_codes[index] + user.profile.postal_code = postal_codes[i] + user.profile.optional_postal_code = postal_codes[i] user.profile.save() - # negative options(answer) has index 0, positive 1 in fixures - # negative are no/never and positive are yes/daily - # Make 2/3 of answers negative - if i % 3 < 2: - option_index = 0 - else: - option_index = 1 - for q_item in questions.items(): - body = {"option": q_item[1][option_index].id} - if isinstance(q_item[0], SubQuestion): - body["sub_question"] = q_item[0].id - body["question"] = q_item[0].question.id - else: - body["question"] = q_item[0].id - - api_client.credentials(HTTP_AUTHORIZATION=f"Token {token}") - response = api_client.post(answer_url, body) - num_answers += 1 - assert Answer.objects.count() == num_answers - - user = User.objects.get(id=user_id) - if option_index == 0: - assert user.result == negative_result - else: - assert user.result == positive_result + api_client.credentials(HTTP_AUTHORIZATION=f"Token {token}") + api_client.post(ANSWER_URL, {"option": q1_option_pos.id, "question": q1.id}) + num_answers += 1 + assert Answer.objects.count() == num_answers + response = api_client.post(reverse("profiles:question-end-poll")) + api_client.credentials() + assert PostalCodeResult.objects.count() == 6 + assert PostalCode.objects.count() == 3 + assert PostalCodeType.objects.count() == 2 + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code=None), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ), + ).count + == 2 + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20100"), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ), + ).count + == 2 + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code="20200"), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ), + ).count + == 1 + ) + assert ( + PostalCodeResult.objects.get( + postal_code=PostalCode.objects.get(postal_code=None), + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.HOME_POSTAL_CODE + ), + ).result.topic + == POS + ) - response = api_client.post(end_poll_url) + # post negative, but only to user Home postal code + for i in range(num_users): + response = api_client.post(start_poll_url) + token = response.json()["token"] assert response.status_code == 200 + token = response.json()["token"] + user_id = response.json()["id"] + assert User.objects.all().count() == num_users + 1 + i user = User.objects.get(id=user_id) - assert user.postal_code_result_saved is True + user.profile.postal_code = postal_codes[i] + user.profile.optional_postal_code = None + user.profile.save() + api_client.credentials(HTTP_AUTHORIZATION=f"Token {token}") + api_client.post(ANSWER_URL, {"option": q1_option_neg.id, "question": q1.id}) + num_answers += 1 + assert Answer.objects.count() == num_answers + response = api_client.post(reverse("profiles:question-end-poll")) api_client.credentials() + neg_result = results_test_result.get(topic=NEG) + pos_result = results_test_result.get(topic=POS) - # Note 20100 is both a Home and Optional postal code assert PostalCode.objects.count() == 3 - # 2 * 5 number of different results * absolute(home and optional) number of postal codes - assert PostalCodeResult.objects.count() == 10 - # A count should be added for every user that answers and ends the poll + assert PostalCodeType.objects.count() == 2 + assert PostalCodeResult.objects.count() == 10 # 6 +4 + assert PostalCodeResult.objects.filter(result=neg_result).count() == 4 + assert PostalCodeResult.objects.get( + result=neg_result, + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + ), + ).count == len(postal_codes) assert ( - PostalCodeResult.objects.aggregate(total_count=(Sum("count")))["total_count"] - == num_users - ) - num_positive_results = PostalCodeResult.objects.filter( - result=positive_result - ).aggregate(total_count=(Sum("count")))["total_count"] - num_negative_results = PostalCodeResult.objects.filter( - result=negative_result - ).aggregate(total_count=(Sum("count")))["total_count"] - # 1/3 of the results are negative - assert num_negative_results == pytest.approx(num_users * (1 / 3), 1) - # 2/3 are positive - assert num_positive_results == pytest.approx(num_users * (2 / 3), 1) - - url = reverse("profiles:postalcoderesult-list") - response = api_client.get(url) - assert response.status_code == 200 - assert response.json()["count"] == 10 - postal_code_20100 = PostalCode.objects.get(postal_code=20100) - url = ( - reverse("profiles:postalcoderesult-list") - + f"?postal_code={postal_code_20100.id}" - ) - response = api_client.get(url) - assert response.json()["count"] == 4 - postal_code_type_home = PostalCodeType.objects.get( - type_name=PostalCodeType.HOME_POSTAL_CODE - ) - url = ( - reverse("profiles:postalcoderesult-list") - + f"?postal_code_type={postal_code_type_home.id}" + PostalCodeResult.objects.get( + result=neg_result, + postal_code_type=PostalCodeType.objects.get( + type_name=PostalCodeType.OPTIONAL_POSTAL_CODE + ), + ).result.topic + == NEG ) - response = api_client.get(url) - assert response.json()["count"] == 4 - url = ( - reverse("profiles:postalcoderesult-list") - + f"?postal_code_type={postal_code_type_home.id}&postal_code={postal_code_20100.id}" - ) - response = api_client.get(url) - assert response.json()["count"] == 2 - url = ( - reverse("profiles:postalcoderesult-list") - + f"?postal_code_string={postal_code_20100.postal_code}" + assert ( + PostalCodeResult.objects.filter(result=pos_result).aggregate( + total_count=(Sum("count")) + )["total_count"] + == len(postal_codes) * 2 ) - response = api_client.get(url) - assert response.json()["count"] == 4 - url = ( - reverse("profiles:postalcoderesult-list") - + f"?postal_code_type_string={PostalCodeType.HOME_POSTAL_CODE}" + assert ( + PostalCodeResult.objects.filter(result=neg_result).aggregate( + total_count=(Sum("count")) + )["total_count"] + == len(postal_codes) * 2 ) - response = api_client.get(url) - assert response.json()["count"] == 4 @pytest.mark.django_db diff --git a/profiles/tests/api/test_question.py b/profiles/tests/api/test_question.py index d73f9e6..a6f78f4 100644 --- a/profiles/tests/api/test_question.py +++ b/profiles/tests/api/test_question.py @@ -7,7 +7,6 @@ from rest_framework.reverse import reverse from profiles.models import Answer, PostalCodeResult -from profiles.tests.utils import delete_memoized_functions_cache @pytest.mark.django_db @@ -44,7 +43,6 @@ def test_questions_condition_states_not_authenticated( @pytest.mark.django_db -@delete_memoized_functions_cache def test_questions_condition_states_after_post_answer( api_client, users, questions, question_conditions, options ): @@ -77,7 +75,6 @@ def test_questions_condition_states_after_post_answer( @pytest.mark.django_db -@delete_memoized_functions_cache def test_questions_condition_states( api_client, users, answers, questions, question_conditions ): @@ -119,7 +116,6 @@ def test_questions_condition_states( @pytest.mark.django_db -@delete_memoized_functions_cache def test_get_questions_with_conditions( api_client, users, answers, questions, question_conditions ): @@ -132,7 +128,6 @@ def test_get_questions_with_conditions( @pytest.mark.django_db -@delete_memoized_functions_cache def test_question_condition_is_met( api_client, users, answers, questions, question_conditions ): @@ -148,7 +143,6 @@ def test_question_condition_is_met( @pytest.mark.django_db -@delete_memoized_functions_cache def test_question_condition_not_met( api_client, users, answers, questions, question_conditions ): @@ -186,7 +180,6 @@ def test_question_with_sub_question_condition_is_met( @pytest.mark.django_db -@delete_memoized_functions_cache def test_question_with_sub_question_condition_not_met( api_client, users, @@ -364,7 +357,7 @@ def test_result_count_is_filled_for_fun_is_false( url = reverse("profiles:question-end-poll") response = api_client_authenticated.post(url) assert response.status_code == 200 - assert PostalCodeResult.objects.count() == 1 + assert PostalCodeResult.objects.count() == 2 @pytest.mark.django_db @@ -379,7 +372,6 @@ def test_result_count_is_filled_for_fun_is_true(api_client_authenticated, users) @pytest.mark.django_db -@delete_memoized_functions_cache def test_result_count_result_can_be_used_is_true( api_client_authenticated, users, answers ): @@ -388,11 +380,10 @@ def test_result_count_result_can_be_used_is_true( url = reverse("profiles:question-end-poll") response = api_client_authenticated.post(url) assert response.status_code == 200 - assert PostalCodeResult.objects.count() == 1 + assert PostalCodeResult.objects.count() == 2 @pytest.mark.django_db -@delete_memoized_functions_cache def test_result_count_result_can_be_used_is_false( api_client_authenticated, users, answers ): @@ -406,7 +397,6 @@ def test_result_count_result_can_be_used_is_false( @pytest.mark.django_db -@delete_memoized_functions_cache def test_sub_question_condition( api_client_authenticated, questions, sub_question_conditions, options, sub_questions ): diff --git a/profiles/tests/api/test_result.py b/profiles/tests/api/test_result.py index e71ea18..2ffb7de 100644 --- a/profiles/tests/api/test_result.py +++ b/profiles/tests/api/test_result.py @@ -2,7 +2,6 @@ from rest_framework.reverse import reverse from profiles.tests.conftest import NEG, OK, POS, YES_BIKE -from profiles.tests.utils import delete_memoized_functions_cache ANSWER_URL = reverse("profiles:answer-list") RESULT_URL = reverse("profiles:answer-get-result") @@ -15,7 +14,6 @@ def test_result_no_answers(api_client_auth_no_answers): @pytest.mark.django_db -@delete_memoized_functions_cache def test_relative_result( api_client_auth_no_answers, questions_test_result, @@ -40,7 +38,6 @@ def test_relative_result( @pytest.mark.django_db -@delete_memoized_functions_cache def test_neg_result( api_client_auth_no_answers, questions_test_result, @@ -69,7 +66,6 @@ def test_neg_result( @pytest.mark.django_db -@delete_memoized_functions_cache def test_ok_result( api_client_auth_no_answers, questions_test_result, @@ -100,7 +96,6 @@ def test_ok_result( @pytest.mark.django_db -@delete_memoized_functions_cache def test_pos_result( api_client_auth_no_answers, questions_test_result, @@ -117,7 +112,6 @@ def test_pos_result( @pytest.mark.django_db -@delete_memoized_functions_cache def test_options_with_multiple_results( api_client_auth_no_answers, questions_test_result, diff --git a/profiles/tests/conftest.py b/profiles/tests/conftest.py index ef76e52..2fb5735 100644 --- a/profiles/tests/conftest.py +++ b/profiles/tests/conftest.py @@ -102,7 +102,9 @@ def options_test_result(questions_test_result, results_test_result): """ if q_c <= v_c: option.results.add(result) - + for result in results_test_result: + result.num_options = Option.objects.filter(results=result).count() + result.save() return Option.objects.all() @@ -115,6 +117,9 @@ def options_with_multiple_results(questions_test_result, results_test_result): option = Option.objects.create(question=q3, value=YES_BIKE) option.results.add(pos_res) option.results.add(ok_res) + for result in results_test_result: + result.num_options = Option.objects.filter(results=result).count() + result.save() return Option.objects.all() @@ -178,8 +183,12 @@ def options(questions, sub_questions, results): @pytest.mark.django_db @pytest.fixture def results(): - Result.objects.create(topic="negative", description="negative result") - Result.objects.create(topic="positive", description="positive result") + Result.objects.create( + topic="negative", description="negative result", num_options=1 + ) + Result.objects.create( + topic="positive", description="positive result", num_options=1 + ) return Result.objects.all() diff --git a/profiles/tests/test_import_questions.py b/profiles/tests/test_import_questions.py index 380c769..564bafa 100644 --- a/profiles/tests/test_import_questions.py +++ b/profiles/tests/test_import_questions.py @@ -42,6 +42,9 @@ def test_import_questions(): assert "Kauris" in results_qs[4].value_fi assert "Hjort" in results_qs[4].value_sv assert "Deer" in results_qs[4].value_en + assert results_qs[0].num_options == 38 + assert results_qs[4].num_options == 47 + # Test questions assert Question.objects.count() == 17 # Test question without sub questions diff --git a/profiles/tests/utils.py b/profiles/tests/utils.py deleted file mode 100644 index a9f0307..0000000 --- a/profiles/tests/utils.py +++ /dev/null @@ -1,10 +0,0 @@ -from memoize import delete_memoized - -from profiles.utils import get_num_results_per_option - - -def delete_memoized_functions_cache(func): - def wrapper(): - delete_memoized(get_num_results_per_option) - - return wrapper diff --git a/profiles/utils.py b/profiles/utils.py index aaf2146..67d227e 100644 --- a/profiles/utils.py +++ b/profiles/utils.py @@ -1,22 +1,11 @@ import secrets import string -from memoize import memoize - from account.models import User -from profiles.models import Answer, Option, Result - - -@memoize(timeout=60 * 60) -def get_num_results_per_option() -> dict: - results = {} - for result in Result.objects.all(): - results[result] = Option.objects.filter(results=result).count() - return results +from profiles.models import Answer, Result def get_user_result(user: User) -> Result: - num_results_in_option = get_num_results_per_option() answer_qs = Answer.objects.filter(user=user) if answer_qs.count() == 0: return None @@ -34,7 +23,7 @@ def get_user_result(user: User) -> Result: results = {} for result in Result.objects.all(): - results[result] = cum_results[result] / num_results_in_option[result] + results[result] = cum_results[result] / result.num_options # The result is the highest relative result result = max(results, key=results.get) diff --git a/requirements.in b/requirements.in index 916e704..146d860 100644 --- a/requirements.in +++ b/requirements.in @@ -17,4 +17,3 @@ django-cors-headers freezegun django-filter pymemcache -django-memoize diff --git a/requirements.txt b/requirements.txt index 94156aa..f20da26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,6 @@ django==4.2 # django-cors-headers # django-extensions # django-filter - # django-memoize # django-modeltranslation # djangorestframework # drf-spectacular @@ -36,8 +35,6 @@ django-extensions==3.2.1 # via -r requirements.in django-filter==23.5 # via -r requirements.in -django-memoize==2.3.1 - # via -r requirements.in django-modeltranslation==0.18.9 # via -r requirements.in djangorestframework==3.14.0