From b2ae680c725b0af851c9bf026b71ebe44c4d04c1 Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Mon, 5 Aug 2024 11:24:43 +0200 Subject: [PATCH 1/3] aaq response feedback endpoint --- aaq/serializers.py | 9 +++++ aaq/tasks.py | 28 ++++++++++++++ aaq/tests/helpers.py | 7 ++++ aaq/tests/test_tasks.py | 34 ++++++++++++++++- aaq/tests/test_views.py | 85 +++++++++++++++++++++++++++++++++++++++++ aaq/urls.py | 5 +++ aaq/views.py | 24 +++++++++++- 7 files changed, 190 insertions(+), 2 deletions(-) diff --git a/aaq/serializers.py b/aaq/serializers.py index a21b31cf..61983226 100644 --- a/aaq/serializers.py +++ b/aaq/serializers.py @@ -28,3 +28,12 @@ def validate(self, data): ) return data + + +class ResponseFeedbackSerializer(serializers.Serializer): + feedback_secret_key = serializers.CharField(required=True) + feedback_sentiment = serializers.ChoiceField( + required=False, choices=["negative", "positive"] + ) + feedback_text = serializers.CharField(required=False) + query_id = serializers.IntegerField(required=True) diff --git a/aaq/tasks.py b/aaq/tasks.py index de70aacd..79edcc5c 100644 --- a/aaq/tasks.py +++ b/aaq/tasks.py @@ -34,3 +34,31 @@ def send_feedback_task(secret_key, inbound_id, feedback_type, **kwargs): } response = requests.request("PUT", url, json=data, headers=headers) response.raise_for_status() + + +@app.task( + autoretry_for=(RequestException, SoftTimeLimitExceeded), + retry_backoff=True, + max_retries=15, + acks_late=True, + soft_time_limit=10, + time_limit=15, +) +def send_feedback_task_v2(feedback_secret_key, query_id, **kwargs): + data = { + "feedback_secret_key": feedback_secret_key, + "query_id": query_id, + } + + if "feedback_sentiment" in kwargs: + data["feedback_sentiment"] = kwargs["feedback_sentiment"] + if "feedback_text" in kwargs: + data["feedback_text"] = kwargs["feedback_text"] + + url = urljoin(settings.AAQ_V2_API_URL, "/response-feedback") + headers = { + "Authorization": settings.AAQ_V2_AUTH, + "Content-Type": "application/json", + } + response = requests.post(url, json=data, headers=headers) + response.raise_for_status() diff --git a/aaq/tests/helpers.py b/aaq/tests/helpers.py index b2df9c90..63d8c106 100644 --- a/aaq/tests/helpers.py +++ b/aaq/tests/helpers.py @@ -72,3 +72,10 @@ def call_add_feedback_task(self, request): "task_added": "True", } return (202, {}, json.dumps(resp_body)) + + def call_response_feedback_task_v2(self, request): + resp_body = { + "task_added": "True", + } + + return (200, {}, json.dumps(resp_body)) diff --git a/aaq/tests/test_tasks.py b/aaq/tests/test_tasks.py index ae0d3748..719715c0 100644 --- a/aaq/tests/test_tasks.py +++ b/aaq/tests/test_tasks.py @@ -3,7 +3,7 @@ import responses from django.test import TestCase -from aaq.tasks import send_feedback_task +from aaq.tasks import send_feedback_task, send_feedback_task_v2 class AddFeedbackTaskTest(TestCase): @@ -62,3 +62,35 @@ def test_add_feedback_page_task(self): self.assertEqual(request.request.url, "http://aaqcore/inbound/feedback") self.assertEqual(json.loads(request.request.body), data) + + +class ResponseFeedbackTaskTest(TestCase): + @responses.activate + def test_response_feedback_task(self): + data = { + "feedback_secret_key": "secret 12345", + "query_id": 1, + "feedback_sentiment": "negative", + "feedback_text": "Not helpful", + } + + responses.add( + responses.POST, + "http://aaq_v2/response-feedback", + json={}, + status=200, + ) + + kwargs = {} + kwargs["feedback_sentiment"] = "negative" + kwargs["feedback_text"] = "Not helpful" + send_feedback_task_v2.delay( + "secret 12345", + 1, + **kwargs, + ) + + [request] = responses.calls + + self.assertEqual(request.request.url, "http://aaq_v2/response-feedback") + self.assertEqual(json.loads(request.request.body), data) diff --git a/aaq/tests/test_views.py b/aaq/tests/test_views.py index 02b0bba2..b608fb07 100644 --- a/aaq/tests/test_views.py +++ b/aaq/tests/test_views.py @@ -264,3 +264,88 @@ def test_not_urgent(self): ) assert response.json() == {"urgency_score": 0.0} + + +class ResponseFeedbackViewTests(APITestCase): + url = reverse("aaq-response-feedback") + + @responses.activate + def test_response_feedback_view(self): + """Test that we can submit response feedback on an FAQ""" + data = json.dumps( + { + "feedback_secret_key": "dummy_secret", + "query_id": 1, + "feedback_sentiment": "negative", + "feedback_text": "Not helpful", + } + ) + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + fakeTask = FakeTask() + responses.add_callback( + responses.POST, + "http://aaq_v2/response-feedback", + callback=fakeTask.call_response_feedback_task_v2, + content_type="application/json", + ) + + response = self.client.post( + self.url, data=data, content_type="application/json" + ) + # need to check response here + + assert response.status_code == 200 + + def test_response_feedback_invalid_view(self): + """Test that we can submit response feedback""" + data = json.dumps( + { + "feedback_secret_key": "dummy_secret", + } + ) + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + fakeTask = FakeTask() + responses.add_callback( + responses.POST, + "http://aaq_v2/response-feedback", + callback=fakeTask.call_response_feedback_task_v2, + content_type="application/json", + ) + + response = self.client.post( + self.url, data=data, content_type="application/json" + ) + + assert response.status_code == 400 + assert response.json() == {"query_id": ["This field is required."]} + + def test_response_feedback_invalid_sentiment_view(self): + """Test that we can submit response feedback""" + data = json.dumps( + { + "feedback_secret_key": "dummy_secret", + "query_id": 1, + "feedback_sentiment": "sentiment", + "feedback_text": "Not helpful", + } + ) + user = get_user_model().objects.create_user("test") + self.client.force_authenticate(user) + fakeTask = FakeTask() + responses.add_callback( + responses.POST, + "http://aaq_v2/response-feedback", + callback=fakeTask.call_response_feedback_task_v2, + content_type="application/json", + ) + + response = self.client.post( + self.url, data=data, content_type="application/json" + ) + + assert response.status_code == 400 + assert response.json() == { + "feedback_sentiment": ['"sentiment" is not a valid choice.'] + } diff --git a/aaq/urls.py b/aaq/urls.py index bba440e2..2ac74c0d 100644 --- a/aaq/urls.py +++ b/aaq/urls.py @@ -23,4 +23,9 @@ views.check_urgency, name="aaq-check-urgency", ), + re_path( + r"^api/v2/response-feedback", + views.response_feedback, + name="aaq-response-feedback", + ), ] diff --git a/aaq/views.py b/aaq/views.py index 448bbdd6..1e4c7839 100644 --- a/aaq/views.py +++ b/aaq/views.py @@ -11,10 +11,11 @@ from aaq.serializers import ( AddFeedbackSerializer, InboundCheckSerializer, + ResponseFeedbackSerializer, UrgencyCheckSerializer, ) -from .tasks import send_feedback_task +from .tasks import send_feedback_task, send_feedback_task_v2 logger = logging.getLogger(__name__) @@ -116,3 +117,24 @@ def check_urgency(request, *args, **kwargs): return_data = json_msg return Response(return_data, status=status.HTTP_202_ACCEPTED) + + +@api_view(("POST",)) +@renderer_classes((JSONRenderer,)) +def response_feedback(request, *args, **kwargs): + serializer = ResponseFeedbackSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + feedback_secret_key = serializer.validated_data["feedback_secret_key"] + query_id = serializer.validated_data["query_id"] + + task_kwargs = {} + if "feedback_sentiment" in serializer.validated_data: + task_kwargs["feedback_sentiment"] = serializer.validated_data[ + "feedback_sentiment" + ] + if "feedback_text" in serializer.validated_data: + task_kwargs["feedback_text"] = serializer.validated_data["feedback_text"] + + send_feedback_task_v2.delay(feedback_secret_key, query_id, **task_kwargs) + + return Response(status=status.HTTP_200_OK) From e51c32d23baa2fb2c2777fadfc83e363a2e206a2 Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Mon, 5 Aug 2024 15:43:15 +0200 Subject: [PATCH 2/3] aaq response feedback v2 endpoint --- aaq/tests/helpers.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/aaq/tests/helpers.py b/aaq/tests/helpers.py index 867c1435..2a0ca636 100644 --- a/aaq/tests/helpers.py +++ b/aaq/tests/helpers.py @@ -77,10 +77,8 @@ def call_add_feedback_task_v2(self, request): resp_body = { "task_added": "True", } - headers = { - "Content-Type": "application/json", - } - return 200, headers, json.dumps(resp_body) + + return 200, {}, json.dumps(resp_body) class FakeAaqApi: From c1fdf7ca8f48326a62c832423af895d4a5f28a4a Mon Sep 17 00:00:00 2001 From: Hlamalani Date: Mon, 5 Aug 2024 15:44:28 +0200 Subject: [PATCH 3/3] aaq response feedback v2 endpoint --- ndoh_hub/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ndoh_hub/settings.py b/ndoh_hub/settings.py index 0b74ef3d..84908fe7 100644 --- a/ndoh_hub/settings.py +++ b/ndoh_hub/settings.py @@ -430,6 +430,10 @@ AAQ_UD_API_URL = env.str("AAQ_UD_API_URL", None) AAQ_UD_INBOUND_CHECK_AUTH = env.str("AAQ_UD_INBOUND_CHECK_AUTH", None) +# AAQ V2 +AAQ_V2_API_URL = env.str("AAQ_V2_API_URL", None) +AAQ_V2_AUTH = env.str("AAQ_V2_AUTH", None) + ALERT_OPTOUT_PHRASE = env.str("ALERT_OPTOUT_PHRASE", "Opt out of alerts") WHATSAPP_TEMPLATE_SEND_TIMEOUT_HOURS = env.int(