From a28044ad1df983299ce40dce48b3e784c9cb93b3 Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Tue, 6 Aug 2024 10:46:35 +0200 Subject: [PATCH 1/9] aaq urgency check endpoint --- aaq/serializers.py | 4 +++ aaq/tests/helpers.py | 16 ++++++++++ aaq/tests/test_views.py | 70 ++++++++++++++++++++++++++++++++++++++++- aaq/urls.py | 5 +++ aaq/views.py | 27 ++++++++++++++++ 5 files changed, 121 insertions(+), 1 deletion(-) diff --git a/aaq/serializers.py b/aaq/serializers.py index 2406188f..ce9c9d7c 100644 --- a/aaq/serializers.py +++ b/aaq/serializers.py @@ -53,3 +53,7 @@ class ContentFeedbackSerializer(serializers.Serializer): feedback_text = serializers.CharField(required=False) query_id = serializers.IntegerField(required=True) content_id = serializers.IntegerField(required=True) + + +class UrgencyCheckV2Serializer(serializers.Serializer): + message_text = serializers.CharField(required=True) diff --git a/aaq/tests/helpers.py b/aaq/tests/helpers.py index 2a0ca636..56345d71 100644 --- a/aaq/tests/helpers.py +++ b/aaq/tests/helpers.py @@ -118,3 +118,19 @@ def post_search_return_empty(self, request): } return (200, {}, json.dumps(resp_body)) + + +class FakeAaqUdV2Api: + def post_urgency_detect_return_one(self, request): + resp_body = { + "is_urgent": True, + } + + return (200, {}, json.dumps(resp_body)) + + def post_urgency_detect_return_zero(self, request): + resp_body = { + "is_urgent": False, + } + + return (200, {}, json.dumps(resp_body)) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index d61f58f1..cc7534ed 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -6,7 +6,7 @@ from rest_framework import status from rest_framework.test import APITestCase -from .helpers import FakeAaqApi, FakeAaqCoreApi, FakeAaqUdApi, FakeTask +from .helpers import FakeAaqApi, FakeAaqCoreApi, FakeAaqUdApi, FakeAaqUdV2Api, FakeTask class GetFirstPageViewTests(APITestCase): @@ -527,3 +527,71 @@ def test_response_feedback_invalid_feedback_text_view(self): assert response.json() == { "feedback_sentiment": ['"test" is not a valid choice.'] } + + +class CheckUrgencyV2ViewTests(APITestCase): + url = reverse("aaq-check-urgency-v2") + + @responses.activate + def test_urgency_check_urgent(self): + """ + Test that we can get an urgency score of 1.0 + """ + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + fakeAaqUdV2Api = FakeAaqUdV2Api() + responses.add_callback( + responses.POST, + "http://aaq_v2/urgency-check-v2", + callback=fakeAaqUdV2Api.post_urgency_detect_return_one, + content_type="application/json", + ) + + payload = json.dumps({"message_text": "I am pregnant and out of breath"}) + + response = self.client.post( + self.url, data=payload, content_type="application/json" + ) + + assert response.status_code == 200 + assert response.json() == {"urgency_score": 1.0} + + @responses.activate + def test_urgency_check_not_urgent(self): + """ + Test that we can get an urgency score of 0.0 + """ + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + fakeAaqUdV2Api = FakeAaqUdV2Api() + responses.add_callback( + responses.POST, + "http://aaq_v2/urgency-check-v2", + callback=fakeAaqUdV2Api.post_urgency_detect_return_zero, + content_type="application/json", + ) + + payload = json.dumps({"message_text": "I am fine"}) + + response = self.client.post( + self.url, data=payload, content_type="application/json" + ) + + assert response.status_code == 200 + assert response.json() == {"urgency_score": 0.0} + + @responses.activate + def test_urgency_check_invalid(self): + """ + Test that we can get a field required message + """ + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + payload = json.dumps({}) + + response = self.client.post( + self.url, data=payload, content_type="application/json" + ) + + assert response.status_code == 400 + assert response.json() == {"message_text": ["This field is required."]} diff --git a/aaq/urls.py b/aaq/urls.py index ece9abd2..8dac10b6 100644 --- a/aaq/urls.py +++ b/aaq/urls.py @@ -38,4 +38,9 @@ views.search, name="aaq-search", ), + re_path( + r"^api/v2/check-urgency", + views.check_urgency_v2, + name="aaq-check-urgency-v2", + ), ] diff --git a/aaq/views.py b/aaq/views.py index f7e1196b..742846db 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -15,6 +15,7 @@ ResponseFeedbackSerializer, SearchSerializer, UrgencyCheckSerializer, + UrgencyCheckV2Serializer, ) from .tasks import send_feedback_task, send_feedback_task_v2 @@ -222,3 +223,29 @@ def search(request, *args, **kwargs): } return Response(json_msg, status=status.HTTP_200_OK) + + +@api_view(("POST",)) +@renderer_classes((JSONRenderer,)) +def check_urgency_v2(request, *args, **kwargs): + serializer = UrgencyCheckV2Serializer(data=request.data) + serializer.is_valid(raise_exception=True) + message_text = serializer.validated_data["message_text"] + url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "/urgency-check-v2") + headers = { + "Authorization": settings.AAQ_V2_API_URL, + "Content-Type": "application/json", + } + + response = requests.request("POST", url, json=message_text, headers=headers) + is_urgent = response.json()["is_urgent"] + urgency_score = 0.0 + if is_urgent: + urgency_score = 1.0 + + json_msg = { + "urgency_score": urgency_score, + } + + return_data = json_msg + return Response(return_data, status=status.HTTP_200_OK) From f30a4072220f132357dce210880280972b99bcad Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Tue, 6 Aug 2024 11:20:51 +0200 Subject: [PATCH 2/9] aaq urgency check endpoint --- aaq/tests/helpers.py | 4 ++-- aaq/tests/test_views.py | 8 ++++---- aaq/views.py | 12 ++---------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/aaq/tests/helpers.py b/aaq/tests/helpers.py index 56345d71..8d43abc8 100644 --- a/aaq/tests/helpers.py +++ b/aaq/tests/helpers.py @@ -121,14 +121,14 @@ def post_search_return_empty(self, request): class FakeAaqUdV2Api: - def post_urgency_detect_return_one(self, request): + def post_urgency_detect_return_true(self, request): resp_body = { "is_urgent": True, } return (200, {}, json.dumps(resp_body)) - def post_urgency_detect_return_zero(self, request): + def post_urgency_detect_return_false(self, request): resp_body = { "is_urgent": False, } diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index cc7534ed..a3bc0256 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -543,7 +543,7 @@ def test_urgency_check_urgent(self): responses.add_callback( responses.POST, "http://aaq_v2/urgency-check-v2", - callback=fakeAaqUdV2Api.post_urgency_detect_return_one, + callback=fakeAaqUdV2Api.post_urgency_detect_return_true, content_type="application/json", ) @@ -554,7 +554,7 @@ def test_urgency_check_urgent(self): ) assert response.status_code == 200 - assert response.json() == {"urgency_score": 1.0} + assert response.json() == {"is_urgent": True} @responses.activate def test_urgency_check_not_urgent(self): @@ -567,7 +567,7 @@ def test_urgency_check_not_urgent(self): responses.add_callback( responses.POST, "http://aaq_v2/urgency-check-v2", - callback=fakeAaqUdV2Api.post_urgency_detect_return_zero, + callback=fakeAaqUdV2Api.post_urgency_detect_return_false, content_type="application/json", ) @@ -578,7 +578,7 @@ def test_urgency_check_not_urgent(self): ) assert response.status_code == 200 - assert response.json() == {"urgency_score": 0.0} + assert response.json() == {"is_urgent": False} @responses.activate def test_urgency_check_invalid(self): diff --git a/aaq/views.py b/aaq/views.py index 742846db..3f430f56 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -238,14 +238,6 @@ def check_urgency_v2(request, *args, **kwargs): } response = requests.request("POST", url, json=message_text, headers=headers) - is_urgent = response.json()["is_urgent"] - urgency_score = 0.0 - if is_urgent: - urgency_score = 1.0 + is_urgent = {"is_urgent": response.json()["is_urgent"]} - json_msg = { - "urgency_score": urgency_score, - } - - return_data = json_msg - return Response(return_data, status=status.HTTP_200_OK) + return Response(is_urgent, status=status.HTTP_200_OK) From 1464ec4f91b1d94774144d9bd975339d4920e0cf Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Tue, 6 Aug 2024 11:36:57 +0200 Subject: [PATCH 3/9] aaq urgency check endpoint --- aaq/tests/helpers.py | 13 +++++++++++++ aaq/tests/test_views.py | 21 +++++++++++++++++++-- aaq/views.py | 3 +-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/aaq/tests/helpers.py b/aaq/tests/helpers.py index 8d43abc8..4bdb9256 100644 --- a/aaq/tests/helpers.py +++ b/aaq/tests/helpers.py @@ -123,14 +123,27 @@ def post_search_return_empty(self, request): class FakeAaqUdV2Api: def post_urgency_detect_return_true(self, request): resp_body = { + "details": { + "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, + "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, + }, "is_urgent": True, + "matched_rules": [ + "Blurry vision and dizziness", + "Nausea that lasts for 3 days", + ], } return (200, {}, json.dumps(resp_body)) def post_urgency_detect_return_false(self, request): resp_body = { + "details": { + "0": {"distance": 0.1, "urgency_rule": "Baby okay"}, + "1": {"distance": 0.2, "urgency_rule": "Baby healthy"}, + }, "is_urgent": False, + "matched_rules": ["Baby okay", "Baby healthy"], } return (200, {}, json.dumps(resp_body)) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index a3bc0256..1564e996 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -554,7 +554,17 @@ def test_urgency_check_urgent(self): ) assert response.status_code == 200 - assert response.json() == {"is_urgent": True} + assert response.json() == { + "details": { + "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, + "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, + }, + "is_urgent": True, + "matched_rules": [ + "Blurry vision and dizziness", + "Nausea that lasts for 3 days", + ], + } @responses.activate def test_urgency_check_not_urgent(self): @@ -578,7 +588,14 @@ def test_urgency_check_not_urgent(self): ) assert response.status_code == 200 - assert response.json() == {"is_urgent": False} + assert response.json() == { + "details": { + "0": {"distance": 0.1, "urgency_rule": "Baby okay"}, + "1": {"distance": 0.2, "urgency_rule": "Baby healthy"}, + }, + "is_urgent": False, + "matched_rules": ["Baby okay", "Baby healthy"], + } @responses.activate def test_urgency_check_invalid(self): diff --git a/aaq/views.py b/aaq/views.py index 3f430f56..0c19b125 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -238,6 +238,5 @@ def check_urgency_v2(request, *args, **kwargs): } response = requests.request("POST", url, json=message_text, headers=headers) - is_urgent = {"is_urgent": response.json()["is_urgent"]} - return Response(is_urgent, status=status.HTTP_200_OK) + return Response(response.json(), status=status.HTTP_200_OK) From 02fbae658a148f766610d367b5d75e9c5f1be86e Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Tue, 6 Aug 2024 11:49:08 +0200 Subject: [PATCH 4/9] aaq urgency check endpoint --- aaq/tests/test_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index 1564e996..3b09a95d 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -535,7 +535,7 @@ class CheckUrgencyV2ViewTests(APITestCase): @responses.activate def test_urgency_check_urgent(self): """ - Test that we can get an urgency score of 1.0 + Test that we can get is urgent True """ user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) @@ -569,7 +569,7 @@ def test_urgency_check_urgent(self): @responses.activate def test_urgency_check_not_urgent(self): """ - Test that we can get an urgency score of 0.0 + Test that we can get is urgent False """ user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) From 3048457ca3cc606b86f23b8f6b237aed29310cc8 Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Tue, 6 Aug 2024 13:42:19 +0200 Subject: [PATCH 5/9] aaq urgency check endpoint --- aaq/tests/test_views.py | 6 +++++- aaq/views.py | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index 3b09a95d..90dfa14d 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -376,12 +376,14 @@ def test_search(self): response = self.client.post( self.url, data=payload, content_type="application/json" ) + [request] = responses.calls self.assertEqual(response.status_code, 200) self.assertIn("message", response.data) self.assertIn("body", response.data) self.assertIn("query_id", response.data) self.assertIn("feedback_secret_key", response.data) + self.assertEqual(json.loads(request.request.body), json.loads(payload)) assert response.json() == { "message": "*0* - Example content title\n*1* -" @@ -552,6 +554,7 @@ def test_urgency_check_urgent(self): response = self.client.post( self.url, data=payload, content_type="application/json" ) + [request] = responses.calls assert response.status_code == 200 assert response.json() == { @@ -565,11 +568,12 @@ def test_urgency_check_urgent(self): "Nausea that lasts for 3 days", ], } + self.assertEqual(json.loads(request.request.body), json.loads(payload)) @responses.activate def test_urgency_check_not_urgent(self): """ - Test that we can get is urgent False + Test that we can get is False """ user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) diff --git a/aaq/views.py b/aaq/views.py index 0c19b125..a617c84b 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -230,7 +230,8 @@ def search(request, *args, **kwargs): def check_urgency_v2(request, *args, **kwargs): serializer = UrgencyCheckV2Serializer(data=request.data) serializer.is_valid(raise_exception=True) - message_text = serializer.validated_data["message_text"] + message_text = {} + message_text["message_text"] = serializer.validated_data["message_text"] url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "/urgency-check-v2") headers = { "Authorization": settings.AAQ_V2_API_URL, From ee5474edb11cc04d328548e50635c5b3db45a7fd Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Tue, 6 Aug 2024 13:45:41 +0200 Subject: [PATCH 6/9] aaq urgency check endpoint --- aaq/tests/test_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index 90dfa14d..fba6d094 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -537,7 +537,7 @@ class CheckUrgencyV2ViewTests(APITestCase): @responses.activate def test_urgency_check_urgent(self): """ - Test that we can get is urgent True + Test that we can get is_urgent True """ user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) @@ -573,7 +573,7 @@ def test_urgency_check_urgent(self): @responses.activate def test_urgency_check_not_urgent(self): """ - Test that we can get is False + Test that we can get is_urgent False """ user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) From a68a93d36a395bbb1084d0cd686826eedc38d78a Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Wed, 7 Aug 2024 13:53:20 +0200 Subject: [PATCH 7/9] aaq urgency check endpoint --- aaq/serializers.py | 4 -- aaq/tests/test_views.py | 143 +++++++++++++--------------------------- aaq/urls.py | 2 +- aaq/views.py | 54 +++++++-------- 4 files changed, 73 insertions(+), 130 deletions(-) diff --git a/aaq/serializers.py b/aaq/serializers.py index ce9c9d7c..2406188f 100644 --- a/aaq/serializers.py +++ b/aaq/serializers.py @@ -53,7 +53,3 @@ class ContentFeedbackSerializer(serializers.Serializer): feedback_text = serializers.CharField(required=False) query_id = serializers.IntegerField(required=True) content_id = serializers.IntegerField(required=True) - - -class UrgencyCheckV2Serializer(serializers.Serializer): - message_text = serializers.CharField(required=True) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index fba6d094..cdb8eb9b 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -358,6 +358,12 @@ def test_search(self): user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) fakeAaqApi = FakeAaqApi() + + search_payload = { + "generate_llm_response": False, + "query_metadata": {"some_key": "query_metadata"}, + "query_text": "Breastfeeding", + } responses.add_callback( responses.POST, "http://aaq_v2/search", @@ -365,35 +371,61 @@ def test_search(self): content_type="application/json", ) - payload = json.dumps( - { - "generate_llm_response": False, - "query_metadata": {"some_key": "query_metadata"}, - "query_text": "Breastfeeding", - } + fakeAaqUdV2Api = FakeAaqUdV2Api() + responses.add_callback( + responses.POST, + "http://aaq_v2/urgency-check-v2", + callback=fakeAaqUdV2Api.post_urgency_detect_return_true, + content_type="application/json", ) + search_payload = { + "query_text": "Test query", + "generate_llm_response": True, + "query_metadata": {"key": "value"}, + } + + urgency_check_payload = { + "message_text": "Test query", + } + response = self.client.post( - self.url, data=payload, content_type="application/json" + self.url, data=json.dumps(search_payload), content_type="application/json" ) - [request] = responses.calls + + search_request = responses.calls[0] + urgency_check_request = responses.calls[1] self.assertEqual(response.status_code, 200) self.assertIn("message", response.data) self.assertIn("body", response.data) self.assertIn("query_id", response.data) self.assertIn("feedback_secret_key", response.data) - self.assertEqual(json.loads(request.request.body), json.loads(payload)) - + self.assertIn("details", response.data) + self.assertIn("is_urgent", response.data) + self.assertIn("matched_rules", response.data) + self.assertEqual(json.loads(search_request.request.body), search_payload) + self.assertEqual( + json.loads(urgency_check_request.request.body), urgency_check_payload + ) assert response.json() == { - "message": "*0* - Example content title\n*1* -" - " Another example content title", + "message": "*0* - Example content title\n" + "*1* - Another example content title", "body": { "0": {"text": "Example content text", "id": 23}, "1": {"text": "Another example content text", "id": 12}, }, "feedback_secret_key": "secret-key-12345-abcde", "query_id": 1, + "details": { + "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, + "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, + }, + "is_urgent": True, + "matched_rules": [ + "Blurry vision and dizziness", + "Nausea that lasts for 3 days", + ], } @responses.activate @@ -529,90 +561,3 @@ def test_response_feedback_invalid_feedback_text_view(self): assert response.json() == { "feedback_sentiment": ['"test" is not a valid choice.'] } - - -class CheckUrgencyV2ViewTests(APITestCase): - url = reverse("aaq-check-urgency-v2") - - @responses.activate - def test_urgency_check_urgent(self): - """ - Test that we can get is_urgent True - """ - user = get_user_model().objects.create_user("test") - self.client.force_authenticate(user) - fakeAaqUdV2Api = FakeAaqUdV2Api() - responses.add_callback( - responses.POST, - "http://aaq_v2/urgency-check-v2", - callback=fakeAaqUdV2Api.post_urgency_detect_return_true, - content_type="application/json", - ) - - payload = json.dumps({"message_text": "I am pregnant and out of breath"}) - - response = self.client.post( - self.url, data=payload, content_type="application/json" - ) - [request] = responses.calls - - assert response.status_code == 200 - assert response.json() == { - "details": { - "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, - "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, - }, - "is_urgent": True, - "matched_rules": [ - "Blurry vision and dizziness", - "Nausea that lasts for 3 days", - ], - } - self.assertEqual(json.loads(request.request.body), json.loads(payload)) - - @responses.activate - def test_urgency_check_not_urgent(self): - """ - Test that we can get is_urgent False - """ - user = get_user_model().objects.create_user("test") - self.client.force_authenticate(user) - fakeAaqUdV2Api = FakeAaqUdV2Api() - responses.add_callback( - responses.POST, - "http://aaq_v2/urgency-check-v2", - callback=fakeAaqUdV2Api.post_urgency_detect_return_false, - content_type="application/json", - ) - - payload = json.dumps({"message_text": "I am fine"}) - - response = self.client.post( - self.url, data=payload, content_type="application/json" - ) - - assert response.status_code == 200 - assert response.json() == { - "details": { - "0": {"distance": 0.1, "urgency_rule": "Baby okay"}, - "1": {"distance": 0.2, "urgency_rule": "Baby healthy"}, - }, - "is_urgent": False, - "matched_rules": ["Baby okay", "Baby healthy"], - } - - @responses.activate - def test_urgency_check_invalid(self): - """ - Test that we can get a field required message - """ - user = get_user_model().objects.create_user("test") - self.client.force_authenticate(user) - payload = json.dumps({}) - - response = self.client.post( - self.url, data=payload, content_type="application/json" - ) - - assert response.status_code == 400 - assert response.json() == {"message_text": ["This field is required."]} diff --git a/aaq/urls.py b/aaq/urls.py index 8dac10b6..5aeb2af7 100644 --- a/aaq/urls.py +++ b/aaq/urls.py @@ -40,7 +40,7 @@ ), re_path( r"^api/v2/check-urgency", - views.check_urgency_v2, + views.search, name="aaq-check-urgency-v2", ), ] diff --git a/aaq/views.py b/aaq/views.py index a617c84b..379acf3d 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -15,7 +15,6 @@ ResponseFeedbackSerializer, SearchSerializer, UrgencyCheckSerializer, - UrgencyCheckV2Serializer, ) from .tasks import send_feedback_task, send_feedback_task_v2 @@ -174,21 +173,27 @@ def response_feedback(request, *args, **kwargs): def search(request, *args, **kwargs): serializer = SearchSerializer(data=request.data) serializer.is_valid(raise_exception=True) - query_text = serializer.validated_data["query_text"] - generate_llm_response = serializer.validated_data["generate_llm_response"] - query_metadata = serializer.validated_data["query_metadata"] - url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "search") - payload = { - "query_text": query_text, - "generate_llm_response": generate_llm_response, - "query_metadata": query_metadata, + + search_payload = { + "query_text": serializer.validated_data["query_text"], } + search_url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "/search") + + if "generate_llm_response" in serializer.validated_data: + search_payload["generate_llm_response"] = serializer.validated_data[ + "generate_llm_response" + ] + if "query_metadata" in serializer.validated_data: + search_payload["query_metadata"] = serializer.validated_data["query_metadata"] + headers = { "Authorization": settings.AAQ_V2_AUTH, "Content-Type": "application/json", } - response = requests.request("POST", url, json=payload, headers=headers) + response = requests.request( + "POST", search_url, json=search_payload, headers=headers + ) query_id = response.json()["query_id"] feedback_secret_key = response.json()["feedback_secret_key"] @@ -215,29 +220,26 @@ def search(request, *args, **kwargs): body_content[key] = {"text": text, "id": id} message_titles.append(f"*{key}* - {title}") - json_msg = { + search_results = { "message": "\n".join(message_titles), "body": body_content, "feedback_secret_key": feedback_secret_key, "query_id": query_id, } - return Response(json_msg, status=status.HTTP_200_OK) + urgency_check_payload = { + "message_text": serializer.validated_data["query_text"], + } + urgency_check_url = urllib.parse.urljoin( + settings.AAQ_V2_API_URL, "/urgency-check-v2" + ) -@api_view(("POST",)) -@renderer_classes((JSONRenderer,)) -def check_urgency_v2(request, *args, **kwargs): - serializer = UrgencyCheckV2Serializer(data=request.data) - serializer.is_valid(raise_exception=True) - message_text = {} - message_text["message_text"] = serializer.validated_data["message_text"] - url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "/urgency-check-v2") - headers = { - "Authorization": settings.AAQ_V2_API_URL, - "Content-Type": "application/json", - } + response = requests.request( + "POST", urgency_check_url, json=urgency_check_payload, headers=headers + ) + check_urgency_results = response.json() - response = requests.request("POST", url, json=message_text, headers=headers) + search_results.update(check_urgency_results) - return Response(response.json(), status=status.HTTP_200_OK) + return Response(search_results, status=status.HTTP_200_OK) From 48bc4d8500eca1d3afd68eab93278238d7b125b8 Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Thu, 8 Aug 2024 14:10:01 +0200 Subject: [PATCH 8/9] aaq search endpoint --- aaq/serializers.py | 4 +- aaq/tests/test_utils.py | 140 ++++++++++++++++++++++++++++++++++++++++ aaq/tests/test_views.py | 72 +++------------------ aaq/urls.py | 7 +- aaq/utils.py | 80 +++++++++++++++++++++++ aaq/views.py | 77 +++------------------- 6 files changed, 241 insertions(+), 139 deletions(-) create mode 100644 aaq/tests/test_utils.py create mode 100644 aaq/utils.py diff --git a/aaq/serializers.py b/aaq/serializers.py index 2406188f..abac13e1 100644 --- a/aaq/serializers.py +++ b/aaq/serializers.py @@ -41,8 +41,8 @@ class ResponseFeedbackSerializer(serializers.Serializer): class SearchSerializer(serializers.Serializer): query_text = serializers.CharField(required=True) - generate_llm_response = serializers.BooleanField(required=False) - query_metadata = serializers.JSONField(required=False) + generate_llm_response = serializers.BooleanField(required=False, default=False) + query_metadata = serializers.JSONField(required=False, default=dict) class ContentFeedbackSerializer(serializers.Serializer): diff --git a/aaq/tests/test_utils.py b/aaq/tests/test_utils.py new file mode 100644 index 00000000..88a400e8 --- /dev/null +++ b/aaq/tests/test_utils.py @@ -0,0 +1,140 @@ +import json + +import responses +from django.contrib.auth import get_user_model +from rest_framework.test import APITestCase + +from ..utils import check_urgency_v2, search +from .helpers import FakeAaqApi, FakeAaqUdV2Api + + +class SearchFunctionTest(APITestCase): + + @responses.activate + def test_search_function(self): + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + + fakeAaqApi = FakeAaqApi() + responses.add_callback( + responses.POST, + "http://aaq_v2/search", + callback=fakeAaqApi.post_search, + content_type="application/json", + ) + + fakeAaqUdV2Api = FakeAaqUdV2Api() + responses.add_callback( + responses.POST, + "http://aaq_v2/check-urgency", + callback=fakeAaqUdV2Api.post_urgency_detect_return_true, + content_type="application/json", + ) + + query_text = "test query" + generate_llm_response = False + query_metadata = {} + + payload = { + "generate_llm_response": generate_llm_response, + "query_metadata": query_metadata, + "query_text": query_text, + } + + response = search(query_text, generate_llm_response, query_metadata) + + search_request = responses.calls[0] + + self.assertEqual(response.status_code, 200) + self.assertIn("message", response.data) + self.assertIn("body", response.data) + self.assertIn("feedback_secret_key", response.data) + self.assertIn("query_id", response.data) + self.assertEqual(response.data["query_id"], 1) + self.assertEqual(json.loads(search_request.request.body), payload) + assert response.data == { + "message": "*0* - Example content title\n" + "*1* - Another example content title", + "body": { + "0": {"text": "Example content text", "id": 23}, + "1": {"text": "Another example content text", "id": 12}, + }, + "feedback_secret_key": "secret-key-12345-abcde", + "query_id": 1, + "details": { + "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, + "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, + }, + "is_urgent": True, + "matched_rules": [ + "Blurry vision and dizziness", + "Nausea that lasts for 3 days", + ], + } + + @responses.activate + def test_urgency_check(self): + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + + fakeAaqUdV2Api = FakeAaqUdV2Api() + responses.add_callback( + responses.POST, + "http://aaq_v2/check-urgency", + callback=fakeAaqUdV2Api.post_urgency_detect_return_true, + content_type="application/json", + ) + + message_text = "Test message" + payload = { + "message_text": message_text, + } + + response = check_urgency_v2(message_text) + + [request] = responses.calls + + self.assertEqual(response.status_code, 200) + self.assertIn("details", response.data) + self.assertIn("is_urgent", response.data) + self.assertIn("matched_rules", response.data) + self.assertEqual(json.loads(request.request.body), payload) + + assert response.data == { + "details": { + "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, + "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, + }, + "is_urgent": True, + "matched_rules": [ + "Blurry vision and dizziness", + "Nausea that lasts for 3 days", + ], + } + + @responses.activate + def test_search_gibberish(self): + """ + Check that we get a response with an empty list in the search results part + """ + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + fakeAaqApi = FakeAaqApi() + responses.add_callback( + responses.POST, + "http://aaq_v2/search", + callback=fakeAaqApi.post_search_return_empty, + content_type="application/json", + ) + + query_text = "jgghkjfhtfftf" + generate_llm_response = False + query_metadata = {} + response = search(query_text, generate_llm_response, query_metadata) + + assert response.data == { + "message": "Gibberish Detected", + "body": {}, + "feedback_secret_key": "secret-key-12345-abcde", + "query_id": 1, + } diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index cdb8eb9b..a5f0df26 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -354,16 +354,10 @@ def test_search(self): """ Test that search returns data. """ - user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) - fakeAaqApi = FakeAaqApi() - search_payload = { - "generate_llm_response": False, - "query_metadata": {"some_key": "query_metadata"}, - "query_text": "Breastfeeding", - } + fakeAaqApi = FakeAaqApi() responses.add_callback( responses.POST, "http://aaq_v2/search", @@ -374,28 +368,21 @@ def test_search(self): fakeAaqUdV2Api = FakeAaqUdV2Api() responses.add_callback( responses.POST, - "http://aaq_v2/urgency-check-v2", + "http://aaq_v2/check-urgency", callback=fakeAaqUdV2Api.post_urgency_detect_return_true, content_type="application/json", ) - search_payload = { - "query_text": "Test query", - "generate_llm_response": True, - "query_metadata": {"key": "value"}, - } - - urgency_check_payload = { - "message_text": "Test query", + payload = { + "generate_llm_response": False, + "query_metadata": {}, + "query_text": "query_text", } response = self.client.post( - self.url, data=json.dumps(search_payload), content_type="application/json" + self.url, data=json.dumps(payload), content_type="application/json" ) - search_request = responses.calls[0] - urgency_check_request = responses.calls[1] - self.assertEqual(response.status_code, 200) self.assertIn("message", response.data) self.assertIn("body", response.data) @@ -404,11 +391,8 @@ def test_search(self): self.assertIn("details", response.data) self.assertIn("is_urgent", response.data) self.assertIn("matched_rules", response.data) - self.assertEqual(json.loads(search_request.request.body), search_payload) - self.assertEqual( - json.loads(urgency_check_request.request.body), urgency_check_payload - ) - assert response.json() == { + + assert response.data == { "message": "*0* - Example content title\n" "*1* - Another example content title", "body": { @@ -428,40 +412,6 @@ def test_search(self): ], } - @responses.activate - def test_search_gibberish(self): - """ - Check that we get a response with an empty list in the search results part - """ - user = get_user_model().objects.create_user("test") - self.client.force_authenticate(user) - fakeAaqApi = FakeAaqApi() - responses.add_callback( - responses.POST, - "http://aaq_v2/search", - callback=fakeAaqApi.post_search_return_empty, - content_type="application/json", - ) - - payload = json.dumps( - { - "generate_llm_response": False, - "query_metadata": {"some_key": "query_metadata"}, - "query_text": "yjyvcgrfeuyikbjmfb", - } - ) - - response = self.client.post( - self.url, data=payload, content_type="application/json" - ) - - assert response.json() == { - "message": "Gibberish Detected", - "body": {}, - "feedback_secret_key": "secret-key-12345-abcde", - "query_id": 1, - } - @responses.activate def test_search_invalid_request_body(self): """ @@ -470,10 +420,8 @@ def test_search_invalid_request_body(self): user = get_user_model().objects.create_user("test") self.client.force_authenticate(user) - payload = json.dumps({}) - response = self.client.post( - self.url, data=payload, content_type="application/json" + self.url, data=json.dumps({}), content_type="application/json" ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/aaq/urls.py b/aaq/urls.py index 5aeb2af7..960eef45 100644 --- a/aaq/urls.py +++ b/aaq/urls.py @@ -35,12 +35,7 @@ ), re_path( r"^api/v2/search", - views.search, + views.aaq_search, name="aaq-search", ), - re_path( - r"^api/v2/check-urgency", - views.search, - name="aaq-check-urgency-v2", - ), ] diff --git a/aaq/utils.py b/aaq/utils.py new file mode 100644 index 00000000..3e059023 --- /dev/null +++ b/aaq/utils.py @@ -0,0 +1,80 @@ +import urllib + +import requests +from django.conf import settings +from rest_framework import status +from rest_framework.response import Response + + +def check_urgency_v2(message_text): + urgency_check_payload = { + "message_text": message_text, + } + url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "check-urgency") + headers = { + "Authorization": settings.AAQ_V2_AUTH, + "Content-Type": "application/json", + } + + response = requests.request( + "POST", url, json=urgency_check_payload, headers=headers + ) + + return Response(response.json(), status=status.HTTP_200_OK) + + +def search(query_text, generate_llm_response, query_metadata): + url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "search") + payload = { + "query_text": query_text, + "generate_llm_response": generate_llm_response, + "query_metadata": query_metadata, + } + headers = { + "Authorization": settings.AAQ_V2_AUTH, + "Content-Type": "application/json", + } + + response = requests.request("POST", url, json=payload, headers=headers) + + query_id = response.json()["query_id"] + feedback_secret_key = response.json()["feedback_secret_key"] + search_results = response.json()["search_results"] + + if search_results == {}: + json_msg = { + "message": "Gibberish Detected", + "body": {}, + "feedback_secret_key": feedback_secret_key, + "query_id": query_id, + } + return Response(json_msg, status=status.HTTP_200_OK) + + json_msg = {} + body_content = {} + message_titles = [] + + for key, value in search_results.items(): + text = value["text"] + id = value["id"] + title = value["title"] + + body_content[key] = {"text": text, "id": id} + message_titles.append(f"*{key}* - {title}") + + json_msg = { + "message": "\n".join(message_titles), + "body": body_content, + "feedback_secret_key": feedback_secret_key, + "query_id": query_id, + } + + urgency_check_payload = { + "message_text": query_text, + } + + check_urgency_response = check_urgency_v2(urgency_check_payload) + + json_msg.update(check_urgency_response.data) + + return Response(json_msg, status=status.HTTP_200_OK) diff --git a/aaq/views.py b/aaq/views.py index 379acf3d..7e2d3f29 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -18,6 +18,7 @@ ) from .tasks import send_feedback_task, send_feedback_task_v2 +from .utils import search logger = logging.getLogger(__name__) @@ -170,76 +171,14 @@ def response_feedback(request, *args, **kwargs): @api_view(("POST",)) @renderer_classes((JSONRenderer,)) -def search(request, *args, **kwargs): +def aaq_search(request): + serializer = SearchSerializer(data=request.data) serializer.is_valid(raise_exception=True) + query_text = serializer.validated_data["query_text"] + generate_llm_response = serializer.validated_data["generate_llm_response"] + query_metadata = serializer.validated_data["query_metadata"] - search_payload = { - "query_text": serializer.validated_data["query_text"], - } - search_url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "/search") - - if "generate_llm_response" in serializer.validated_data: - search_payload["generate_llm_response"] = serializer.validated_data[ - "generate_llm_response" - ] - if "query_metadata" in serializer.validated_data: - search_payload["query_metadata"] = serializer.validated_data["query_metadata"] - - headers = { - "Authorization": settings.AAQ_V2_AUTH, - "Content-Type": "application/json", - } - - response = requests.request( - "POST", search_url, json=search_payload, headers=headers - ) - - query_id = response.json()["query_id"] - feedback_secret_key = response.json()["feedback_secret_key"] - search_results = response.json()["search_results"] - - if search_results == {}: - json_msg = { - "message": "Gibberish Detected", - "body": {}, - "feedback_secret_key": feedback_secret_key, - "query_id": query_id, - } - return Response(json_msg, status=status.HTTP_200_OK) - - json_msg = {} - body_content = {} - message_titles = [] - - for key, value in search_results.items(): - text = value["text"] - id = value["id"] - title = value["title"] - - body_content[key] = {"text": text, "id": id} - message_titles.append(f"*{key}* - {title}") - - search_results = { - "message": "\n".join(message_titles), - "body": body_content, - "feedback_secret_key": feedback_secret_key, - "query_id": query_id, - } - - urgency_check_payload = { - "message_text": serializer.validated_data["query_text"], - } - - urgency_check_url = urllib.parse.urljoin( - settings.AAQ_V2_API_URL, "/urgency-check-v2" - ) - - response = requests.request( - "POST", urgency_check_url, json=urgency_check_payload, headers=headers - ) - check_urgency_results = response.json() - - search_results.update(check_urgency_results) + response = search(query_text, generate_llm_response, query_metadata) - return Response(search_results, status=status.HTTP_200_OK) + return Response(response.data, status=status.HTTP_200_OK) From 40aa6c8b0d99d3ad6a67c90eda9242b4726af9a2 Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Thu, 8 Aug 2024 17:16:28 +0200 Subject: [PATCH 9/9] aaq search endpoint --- aaq/tests/test_utils.py | 27 +++++++++------------ aaq/tests/test_views.py | 53 ++++++++++++++++++++++++++++++++++------- aaq/utils.py | 19 ++++----------- aaq/views.py | 2 +- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/aaq/tests/test_utils.py b/aaq/tests/test_utils.py index 88a400e8..617fa93d 100644 --- a/aaq/tests/test_utils.py +++ b/aaq/tests/test_utils.py @@ -45,14 +45,13 @@ def test_search_function(self): search_request = responses.calls[0] - self.assertEqual(response.status_code, 200) - self.assertIn("message", response.data) - self.assertIn("body", response.data) - self.assertIn("feedback_secret_key", response.data) - self.assertIn("query_id", response.data) - self.assertEqual(response.data["query_id"], 1) + self.assertIn("message", response) + self.assertIn("body", response) + self.assertIn("feedback_secret_key", response) + self.assertIn("query_id", response) + self.assertEqual(response["query_id"], 1) self.assertEqual(json.loads(search_request.request.body), payload) - assert response.data == { + assert response == { "message": "*0* - Example content title\n" "*1* - Another example content title", "body": { @@ -86,21 +85,17 @@ def test_urgency_check(self): ) message_text = "Test message" - payload = { - "message_text": message_text, - } response = check_urgency_v2(message_text) [request] = responses.calls - self.assertEqual(response.status_code, 200) - self.assertIn("details", response.data) - self.assertIn("is_urgent", response.data) - self.assertIn("matched_rules", response.data) - self.assertEqual(json.loads(request.request.body), payload) + self.assertIn("details", response) + self.assertIn("is_urgent", response) + self.assertIn("matched_rules", response) + self.assertEqual(json.loads(request.request.body), message_text) - assert response.data == { + assert response == { "details": { "0": {"distance": 0.1, "urgency_rule": "Blurry vision and dizziness"}, "1": {"distance": 0.2, "urgency_rule": "Nausea that lasts for 3 days"}, diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index a5f0df26..fe32fd43 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -384,15 +384,15 @@ def test_search(self): ) self.assertEqual(response.status_code, 200) - self.assertIn("message", response.data) - self.assertIn("body", response.data) - self.assertIn("query_id", response.data) - self.assertIn("feedback_secret_key", response.data) - self.assertIn("details", response.data) - self.assertIn("is_urgent", response.data) - self.assertIn("matched_rules", response.data) - - assert response.data == { + self.assertIn("message", response.json()) + self.assertIn("body", response.json()) + self.assertIn("query_id", response.json()) + self.assertIn("feedback_secret_key", response.json()) + self.assertIn("details", response.json()) + self.assertIn("is_urgent", response.json()) + self.assertIn("matched_rules", response.json()) + + assert response.json() == { "message": "*0* - Example content title\n" "*1* - Another example content title", "body": { @@ -427,6 +427,41 @@ def test_search_invalid_request_body(self): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.json(), {"query_text": ["This field is required."]}) + @responses.activate + def test_request(self): + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + + fakeAaqApi = FakeAaqApi() + responses.add_callback( + responses.POST, + "http://aaq_v2/search", + callback=fakeAaqApi.post_search, + content_type="application/json", + ) + + fakeAaqUdV2Api = FakeAaqUdV2Api() + responses.add_callback( + responses.POST, + "http://aaq_v2/check-urgency", + callback=fakeAaqUdV2Api.post_urgency_detect_return_true, + content_type="application/json", + ) + + payload = { + "generate_llm_response": "testing", + "query_metadata": {}, + "query_text": "query_text", + } + + response = self.client.post( + self.url, data=json.dumps(payload), content_type="application/json" + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.json(), {"generate_llm_response": ["Must be a valid boolean."]} + ) + class ContentFeedbackViewTests(APITestCase): url = reverse("aaq-content-feedback") diff --git a/aaq/utils.py b/aaq/utils.py index 3e059023..7a0b9cbe 100644 --- a/aaq/utils.py +++ b/aaq/utils.py @@ -7,20 +7,15 @@ def check_urgency_v2(message_text): - urgency_check_payload = { - "message_text": message_text, - } url = urllib.parse.urljoin(settings.AAQ_V2_API_URL, "check-urgency") headers = { "Authorization": settings.AAQ_V2_AUTH, "Content-Type": "application/json", } - response = requests.request( - "POST", url, json=urgency_check_payload, headers=headers - ) + response = requests.request("POST", url, json=message_text, headers=headers) - return Response(response.json(), status=status.HTTP_200_OK) + return response.json() def search(query_text, generate_llm_response, query_metadata): @@ -69,12 +64,8 @@ def search(query_text, generate_llm_response, query_metadata): "query_id": query_id, } - urgency_check_payload = { - "message_text": query_text, - } - - check_urgency_response = check_urgency_v2(urgency_check_payload) + check_urgency_response = check_urgency_v2(query_text) - json_msg.update(check_urgency_response.data) + json_msg.update(check_urgency_response) - return Response(json_msg, status=status.HTTP_200_OK) + return json_msg diff --git a/aaq/views.py b/aaq/views.py index 7e2d3f29..81742e63 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -181,4 +181,4 @@ def aaq_search(request): response = search(query_text, generate_llm_response, query_metadata) - return Response(response.data, status=status.HTTP_200_OK) + return Response(response, status=status.HTTP_200_OK)