From 3e7c8c5319c84551e7f5b491322ab3f22d00e6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fab=C3=ADola=20Fleury?= Date: Mon, 22 Feb 2021 23:21:28 +0000 Subject: [PATCH 1/2] =?UTF-8?q?Visualizar=20coment=C3=A1rios,=20enviar=20v?= =?UTF-8?q?otos=20e=20novos=20coment=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++ bot/actions/__init__.py | 2 +- bot/actions/actions.py | 62 ++++++++++++++++++++++----------- bot/actions/ej_connector/api.py | 32 +++++++++++++++++ bot/data/nlu.yml | 21 +++++++---- bot/data/rules.yml | 10 +++++- bot/data/stories.yml | 17 +++++---- bot/domain.yml | 26 +++++++++++--- bot/tests/test_actions.py | 44 +++++++++++++++-------- bot/tests/test_ej_connector.py | 32 +++++++++++++++++ bot/tests/test_stories.yml | 13 +++++-- docker-compose.yml | 2 ++ 12 files changed, 204 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 505b0cf..62b537c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ models results/ train_test_split +.env/ +env +.env \ No newline at end of file diff --git a/bot/actions/__init__.py b/bot/actions/__init__.py index a29afc7..0414035 100644 --- a/bot/actions/__init__.py +++ b/bot/actions/__init__.py @@ -1 +1 @@ -from .actions import ActionSetupConversation, ActionAskVote, FormValidationAction +from .actions import ActionSetupConversation, ActionAskVote, ValidateVoteForm diff --git a/bot/actions/actions.py b/bot/actions/actions.py index 927671e..65b2048 100644 --- a/bot/actions/actions.py +++ b/bot/actions/actions.py @@ -26,18 +26,20 @@ def name(self): def run(self, dispatcher, tracker, domain): conversation_id = 1 user_email = tracker.get_slot("email") + if user_email: user = API.create_user(tracker.sender_id, user_email, user_email) else: user = API.create_user(tracker.sender_id) - # TODO: get values from EJ server - number_comments = 2 - number_voted_comments = 1 + + statistics = API.get_user_conversation_statistics(conversation_id, user.token) first_comment = API.get_next_comment(conversation_id, user.token) return [ - SlotSet("number_voted_comments", number_voted_comments), + SlotSet("number_voted_comments", statistics["votes"]), SlotSet("conversation_id", conversation_id), - SlotSet("number_comments", number_comments), + SlotSet( + "number_comments", statistics["missing_votes"] + statistics["votes"] + ), SlotSet("comment_text", first_comment["content"]), SlotSet("current_comment_id", first_comment["id"]), SlotSet("change_comment", False), @@ -58,24 +60,32 @@ def run( {"title": "Discordar", "payload": "Discordar"}, {"title": "Pular", "payload": "Pular"}, ] + conversation_id = tracker.get_slot("conversation_id") + token = tracker.get_slot("ej_user_token") + statistics = API.get_user_conversation_statistics(conversation_id, token) + total_comments = statistics["missing_votes"] + statistics["votes"] + number_voted_comments = statistics["votes"] if tracker.get_slot("change_comment"): - # TODO: get values from EJ server - # next_comment = get_random_comment() + new_comment = API.get_next_comment(conversation_id, token) + comment_content = new_comment["content"] + message = f"{comment_content} \n O que você acha disso ({number_voted_comments}/{total_comments})?" + + dispatcher.utter_message(text=message, buttons=buttons) + conversation_id = tracker.get_slot("conversation_id") token = tracker.get_slot("ej_user_token") new_comment = API.get_next_comment(conversation_id, token) - dispatcher.utter_message(text=new_comment, buttons=buttons) - number_voted_comments = tracker.get_slot("number_comments") + 1 return [ SlotSet("number_voted_comments", number_voted_comments), SlotSet("comment_text", new_comment["content"]), + SlotSet("number_comments", total_comments), SlotSet("current_comment_id", new_comment["id"]), ] else: - dispatcher.utter_message( - text=tracker.get_slot("comment_text"), buttons=buttons - ) + comment_content = tracker.get_slot("comment_text") + message = f"{comment_content} \n O que você acha disso ({number_voted_comments}/{total_comments})?" + dispatcher.utter_message(text=message, buttons=buttons) return [SlotSet("change_comment", True)] @@ -91,20 +101,30 @@ def validate_vote( domain: DomainDict, ) -> Dict[Text, Any]: """Validate vote value.""" + token = tracker.get_slot("ej_user_token") + conversation_id = tracker.get_slot("conversation_id") if str(slot_value) in ["Concordar", "Discordar", "Pular"]: - # TODO: Send vote value to EJ server - voted_comments = tracker.get_slot("number_voted_comments") - total_comments = tracker.get_slot("number_comments") - dispatcher.utter_message(template="utter_vote_received") - # TODO: Get next random comment - # was_sent = send_vote(vote_value) - if voted_comments < total_comments: + comment_id = tracker.get_slot("current_comment_id") + sent_vote = API.send_comment_vote(comment_id, slot_value, token) + + if sent_vote["created"]: + dispatcher.utter_message(template="utter_vote_received") + statistics = API.get_user_conversation_statistics(conversation_id, token) + if statistics["missing_votes"] > 0: # user still has comments to vote, remain in loop return {"vote": None} else: # user voted in all comments, can exit loop + dispatcher.utter_message(template="utter_voted_all_comments") return [{"vote": slot_value}] + elif str(slot_value) == "PARAR": + dispatcher.utter_message(template="utter_stopped") + return {"vote": "None"} else: - dispatcher.utter_message(template="utter_out_of_context") - # did not send expected vote value + # register a new comment instead of a vote + response = API.send_new_comment(conversation_id, slot_value, token) + if response["created"]: + dispatcher.utter_message(template="utter_sent_comment") + else: + dispatcher.utter_message(template="utter_send_comment_error") return {"vote": None} diff --git a/bot/actions/ej_connector/api.py b/bot/actions/ej_connector/api.py index f25db5a..12c0876 100644 --- a/bot/actions/ej_connector/api.py +++ b/bot/actions/ej_connector/api.py @@ -1,5 +1,6 @@ import os import requests +import json from .user import User HEADERS = { @@ -10,6 +11,7 @@ API_URL = f"{HOST}/api/v1" REGISTRATION_URL = f"{HOST}/rest-auth/registration/" VOTES_URL = f"{API_URL}/votes/" +COMMENTS_URL = f"{API_URL}/comments/" def conversation_url(conversation_id): @@ -56,3 +58,33 @@ def get_next_comment(conversation_id, token): comment_url_as_list = comment["links"]["self"].split("/") comment["id"] = comment_url_as_list[len(comment_url_as_list) - 2] return comment + + def get_user_conversation_statistics(conversation_id, token): + url = user_statistics_url(conversation_id) + response = requests.get(url, headers=auth_headers(token)) + return response.json() + + def send_comment_vote(comment_id, choice, token): + body = json.dumps( + { + "comment": comment_id, + "choice": VOTE_CHOICES[choice], + } + ) + response = requests.post( + VOTES_URL, + data=body, + headers=auth_headers(token), + ) + return response.json() + + def send_new_comment(conversation_id, content, token): + body = json.dumps( + {"content": content, "conversation": conversation_id, "status": "pending"} + ) + response = requests.post( + COMMENTS_URL, + data=body, + headers=auth_headers(token), + ) + return response.json() diff --git a/bot/data/nlu.yml b/bot/data/nlu.yml index fa6c243..75c309c 100644 --- a/bot/data/nlu.yml +++ b/bot/data/nlu.yml @@ -55,6 +55,18 @@ nlu: - email: esteemail@outroservidor.com - meu email: email@email.com.br +- intent: stop + examples: | + - para + - PARAR + - PAUSAR + - parar + - pausa + - pausar + - chega + - sair + - stop + - intent: invalid_email examples: | - @wlor @@ -79,11 +91,6 @@ nlu: - você é um robô? - Fala alguma coisa - asdfgasd - - chega - - para - - parar - - pausa - - tempo - - stop - - sair + - oque + - que diff --git a/bot/data/rules.yml b/bot/data/rules.yml index be42792..28bc071 100644 --- a/bot/data/rules.yml +++ b/bot/data/rules.yml @@ -4,4 +4,12 @@ rules: - rule: User ask a out of context question steps: - intent: out_of_context - - action: utter_out_of_context \ No newline at end of file + - action: utter_out_of_context + +- rule: User doesn't want to participate anymore + steps: + # This unhappy path handles the case of an intent `stop`. + - intent: stop + - action: utter_stopped + - action: utter_finish + - active_loop: null \ No newline at end of file diff --git a/bot/data/stories.yml b/bot/data/stories.yml index c75efa4..fc98716 100644 --- a/bot/data/stories.yml +++ b/bot/data/stories.yml @@ -12,18 +12,20 @@ stories: - intent: start - action: utter_start - intent: disagree - - action: utter_goodbye + - action: utter_finish - story: User wants to participate steps: - intent: start - action: utter_start - intent: agree + - action: utter_agreed_participation - action: utter_ask_email - story: Activate vote form with user email steps: - intent: agree + - action: utter_agreed_participation - action: utter_ask_email - intent: email entities: @@ -32,24 +34,25 @@ stories: - action: vote_form - active_loop: vote_form - active_loop: null - - slot_was_set: - - vote: null + - action: utter_finish + - story: Activate vote form without user email steps: - intent: agree + - action: utter_agreed_participation - action: utter_ask_email - intent: disagree - action: action_setup_conversation - action: vote_form - active_loop: vote_form - active_loop: null - - slot_was_set: - - vote: null + - action: utter_finish - story: User provides invalid email steps: - intent: agree + - action: utter_agreed_participation - action: utter_ask_email - intent: invalid_email - action: utter_invalid_email @@ -60,5 +63,5 @@ stories: - action: vote_form - active_loop: vote_form - active_loop: null - - slot_was_set: - - vote: null \ No newline at end of file + - action: utter_finish + diff --git a/bot/domain.yml b/bot/domain.yml index 391d202..72712b5 100644 --- a/bot/domain.yml +++ b/bot/domain.yml @@ -19,7 +19,9 @@ intents: use_entities: true - invalid_email: use_entities: true -entities: +- stop: + use_entities: true +entities: - email slots: change_comment: @@ -41,6 +43,7 @@ slots: type: rasa.shared.core.slots.TextSlot initial_value: null auto_fill: true + influence_conversation: true conversation_id: type: rasa.shared.core.slots.FloatSlot initial_value: null @@ -80,17 +83,30 @@ slots: - pular responses: utter_start: - - text: Olá, estou coletando opiniões, gostaria de participar? + - text: Você topa me dizer o que acha do que o pessoal anda falando? Vai me ajudar bastante a entender o assunto + utter_agreed_participation: + - text: Oba, muito obrigada!! :) A qualquer momento você pode parar a conversa, é só escrever PARAR. utter_ask_email: - text: Legal, você gostaria de se identificar pelo seu email? Se sim, já pode enviar que anoto aqui. utter_invalid_email: - text: Não entendi, esse email não parece válido, pode tentar enviar de novo? - utter_goodbye: - - text: Agradeço pela participação! Até logo utter_vote_received: - - text: Seus votos foram computados, agradeço! + - text: Registrei sua opinião! + - text: Beleza, anotei aqui. + - text: Entendo, registrado. + - text: Obrigada por participar. utter_out_of_context: - text: Desculpe, não sei como posso te ajudar com isso. + utter_sent_comment: + - text: Seu comentário foi adicionado à conversa. + utter_send_comment_error: + - text: Não foi possível salvar seu comentário, tente novamente mais tarde. + utter_voted_all_comments: + - text: Obrigada! Você opinou em todos os comentários que existem atualmente sobre esse assunto. + utter_stopped: + - text: Você já me ajudou bastante, muito obrigada! Vou ficar esperando pra gente conversar mais um pouquinho + utter_finish: + - text: Volte quando quiser, é só me dar um "Oi". Mas pode deixar que te mantenho por dentro sobre as novidades desse assunto :) actions: - action_setup_conversation - action_ask_vote diff --git a/bot/tests/test_actions.py b/bot/tests/test_actions.py index 66ca0ce..de70a76 100644 --- a/bot/tests/test_actions.py +++ b/bot/tests/test_actions.py @@ -5,8 +5,14 @@ from rasa_sdk.executor import CollectingDispatcher from rasa_sdk.events import SlotSet, FollowupAction +from rasa.shared.core.domain import Domain from rasa.shared.core.trackers import DialogueStateTracker, AnySlotDict -from actions import ActionSetupConversation, ActionAskVote +from actions import ActionSetupConversation, ActionAskVote, ValidateVoteForm + +STATISTICS = { + "votes": 0, + "missing_votes": 6, +} @pytest.fixture @@ -24,40 +30,48 @@ def tracker(): return DialogueStateTracker(sender_id="1", slots=AnySlotDict()) -@patch("actions.ej_connector.api.requests.get") -@patch("actions.ej_connector.api.requests.post") def test_action_setup_conversation(dispatcher, domain, tracker): with patch("actions.ej_connector.api.requests.get") as mock_get: with patch("actions.ej_connector.api.requests.post") as mock_post: - user_response = {"key": "key_value"} + user_response_mock = {"key": "key_value"} mock_post.return_value = Mock(ok=True) - mock_post.return_value.json.return_value = user_response + mock_post.return_value.json.return_value = user_response_mock - comment_response = { + comment_statistics_mock = { "content": "This is the comment text", "links": {"self": "http://localhost:8000/api/v1/comments/1/"}, + "votes": STATISTICS["votes"], + "missing_votes": STATISTICS["missing_votes"], } mock_get.return_value = Mock(ok=True) - mock_get.return_value.json.return_value = comment_response + mock_get.return_value.json.return_value = comment_statistics_mock action = ActionSetupConversation() events = action.run(dispatcher, tracker, domain) expected_events = [ - SlotSet("number_voted_comments", 1), + SlotSet("number_voted_comments", comment_statistics_mock["votes"]), SlotSet("conversation_id", 1), - SlotSet("number_comments", 2), - SlotSet("comment_text", comment_response["content"]), + SlotSet( + "number_comments", + comment_statistics_mock["missing_votes"] + + comment_statistics_mock["votes"], + ), + SlotSet("comment_text", comment_statistics_mock["content"]), SlotSet("current_comment_id", "1"), SlotSet("change_comment", False), - SlotSet("ej_user_token", user_response["key"]), + SlotSet("ej_user_token", user_response_mock["key"]), FollowupAction("vote_form"), ] assert events == expected_events def test_action_ask_vote(dispatcher, domain, tracker): - action = ActionAskVote() - events = action.run(dispatcher, tracker, domain) - expected_events = [SlotSet("change_comment", True)] + with patch("actions.ej_connector.api.requests.get") as mock_get: + statistics_mock = STATISTICS + mock_get.return_value = Mock(ok=True) + mock_get.return_value.json.return_value = statistics_mock + action = ActionAskVote() + events = action.run(dispatcher, tracker, domain) + expected_events = [SlotSet("change_comment", True)] - assert events == expected_events + assert events == expected_events diff --git a/bot/tests/test_ej_connector.py b/bot/tests/test_ej_connector.py index e88a98e..7709cba 100644 --- a/bot/tests/test_ej_connector.py +++ b/bot/tests/test_ej_connector.py @@ -50,6 +50,38 @@ def test_get_random_comment_in_ej(self, mock_get): assert response["content"] == response_value["content"] assert response["id"] == "1" + @patch("actions.ej_connector.api.requests.get") + def test_get_user_conversation_statistics(self, mock_get): + statistics_mock = { + "votes": 3, + "missing_votes": 6, + } + mock_get.return_value = Mock(ok=True) + mock_get.return_value.json.return_value = statistics_mock + response = API.get_user_conversation_statistics(CONVERSATION_ID, TOKEN) + + assert response["votes"] == statistics_mock["votes"] + assert response["missing_votes"] == statistics_mock["missing_votes"] + + @patch("actions.ej_connector.api.requests.post") + def test_send_user_vote(self, mock_post): + vote_response_mock = {"created": True} + mock_post.return_value = Mock(ok=True) + mock_post.return_value.json.return_value = vote_response_mock + + response = API.send_comment_vote(CONVERSATION_ID, "Pular", TOKEN) + assert response["created"] + + @patch("actions.ej_connector.api.requests.post") + def test_send_user_comment(self, mock_post): + vote_response_mock = {"created": True, "content": "content"} + mock_post.return_value = Mock(ok=True) + mock_post.return_value.json.return_value = vote_response_mock + + response = API.send_new_comment(CONVERSATION_ID, "content", TOKEN) + assert response["created"] + assert response["content"] == "content" + class UserClassTest(unittest.TestCase): """tests actions.ej_connector.user file""" diff --git a/bot/tests/test_stories.yml b/bot/tests/test_stories.yml index 6ba1620..f19cfd3 100644 --- a/bot/tests/test_stories.yml +++ b/bot/tests/test_stories.yml @@ -12,7 +12,7 @@ stories: - story: User utters out of context steps: - user: | - chega + asdfgasd intent: out_of_context - action: utter_out_of_context @@ -25,7 +25,7 @@ stories: - user: | não intent: disagree - - action: utter_goodbye + - action: utter_finish - story: User agree to use email steps: @@ -36,6 +36,7 @@ stories: - user: | sim intent: agree + - action: utter_agreed_participation - action: utter_ask_email - user: | meu email: [email@user.com](email) @@ -48,9 +49,14 @@ stories: - story: User want to participate without email steps: + - user: | + oi + intent: start + - action: utter_start - user: | sim intent: agree + - action: utter_agreed_participation - action: utter_ask_email - user: | não @@ -68,8 +74,9 @@ stories: - user: | sim intent: agree + - action: utter_agreed_participation - action: utter_ask_email - user: | @mario intent: invalid_email - - action: utter_invalid_email \ No newline at end of file + - action: utter_invalid_email diff --git a/docker-compose.yml b/docker-compose.yml index 044ddc8..13de771 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,8 @@ services: context: . dockerfile: ./docker/bot.Dockerfile restart: unless-stopped + networks: + - bot-network volumes: - ./bot/:/bot/ - /bot/tests/:/bot/tests/ From badfa3ba1f608318a8b0396b9ee56dceccf652ea Mon Sep 17 00:00:00 2001 From: David Carlos Date: Tue, 23 Feb 2021 22:04:49 +0000 Subject: [PATCH 2/2] Adds env variable to disable tls --- .gitlab-ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d636540..d290376 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,9 @@ image: docker:latest +variables: + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: "" + services: - docker:dind @@ -33,4 +37,4 @@ test: script: - make run-test-nlu - make run-test-core - - make test-actions \ No newline at end of file + - make test-actions