diff --git a/core/names/field.py b/core/names/field.py index fc806168..2e2c4e3b 100644 --- a/core/names/field.py +++ b/core/names/field.py @@ -22,6 +22,7 @@ SYSTEM_NAME = "systemName" USER_CHANNEL = "userChannel" USER_ID = "userId" +MESSAGE = "message" DEBUG_INFO = "debug_info" DEBUG_INFO_APP_KEY = "debug_info_key" diff --git a/smart_kit/action/smart_geo_action.py b/smart_kit/action/smart_geo_action.py new file mode 100644 index 00000000..4ef32feb --- /dev/null +++ b/smart_kit/action/smart_geo_action.py @@ -0,0 +1,14 @@ +from typing import Dict, Any, Optional, Union, List + +from core.basic_models.actions.command import Command +from core.basic_models.actions.string_actions import StringAction +from core.model.base_user import BaseUser +from core.text_preprocessing.base import BaseTextPreprocessingResult +from smart_kit.names.message_names import GET_PROFILE_DATA + + +class SmartGeoAction(StringAction): + def run(self, user: BaseUser, text_preprocessing_result: BaseTextPreprocessingResult, + params: Optional[Dict[str, Union[str, float, int]]] = None) -> List[Command]: + commands = [Command(GET_PROFILE_DATA)] + return commands diff --git a/smart_kit/handlers/handler_take_profile_data.py b/smart_kit/handlers/handler_take_profile_data.py new file mode 100644 index 00000000..500bf966 --- /dev/null +++ b/smart_kit/handlers/handler_take_profile_data.py @@ -0,0 +1,22 @@ +from core.logging.logger_utils import log +from core.names.field import MESSAGE +from core.text_preprocessing.preprocessing_result import TextPreprocessingResult +from scenarios.user.user_model import User + +from smart_kit.handlers.handler_base import HandlerBase +from smart_kit.names.field import PROFILE_DATA, STATUS_CODE, CODE, GEO + + +class HandlerTakeProfileData(HandlerBase): + SUCCESS_CODE = 1 + + def run(self, payload, user: User): + super().run(payload, user) + log(f"{self.__class__.__name__} started", user) + + text_preprocessing_result = TextPreprocessingResult(payload.get(MESSAGE, {})) + action = user.descriptions["external_actions"]["smart_geo_fail"] + if payload.get(STATUS_CODE, {}).get(CODE) == self.SUCCESS_CODE: + action = user.descriptions["external_actions"]["smart_geo_success"] + user.variables.set("smart_geo", payload.get(PROFILE_DATA, {}).get(GEO)) + return action.run(user, text_preprocessing_result) diff --git a/smart_kit/models/smartapp_model.py b/smart_kit/models/smartapp_model.py index bf5a4855..bde1b755 100644 --- a/smart_kit/models/smartapp_model.py +++ b/smart_kit/models/smartapp_model.py @@ -9,7 +9,9 @@ import scenarios.logging.logger_constants as log_const from smart_kit.handlers.handle_close_app import HandlerCloseApp -from smart_kit.names.message_names import MESSAGE_TO_SKILL, LOCAL_TIMEOUT, RUN_APP, SERVER_ACTION, CLOSE_APP +from smart_kit.handlers.handler_take_profile_data import HandlerTakeProfileData +from smart_kit.names.message_names import MESSAGE_TO_SKILL, LOCAL_TIMEOUT, RUN_APP, SERVER_ACTION, CLOSE_APP, \ + TAKE_PROFILE_DATA from smart_kit.handlers.handle_respond import HandlerRespond from smart_kit.handlers.handler_text import HandlerText from smart_kit.handlers.handler_timeout import HandlerTimeout @@ -39,7 +41,8 @@ def __init__(self, resources: SmartAppResources, dialogue_manager_cls, custom_se RUN_APP: handler_text, LOCAL_TIMEOUT: HandlerTimeout(self.app_name), SERVER_ACTION: HandlerServerAction(self.app_name), - CLOSE_APP: HandlerCloseApp(self.app_name) + CLOSE_APP: HandlerCloseApp(self.app_name), + TAKE_PROFILE_DATA: HandlerTakeProfileData(self.app_name) } self._handlers.update({ message_name: HandlerRespond(self.app_name, action_name=action_name) diff --git a/smart_kit/names/field.py b/smart_kit/names/field.py new file mode 100644 index 00000000..c8a51e45 --- /dev/null +++ b/smart_kit/names/field.py @@ -0,0 +1,4 @@ +PROFILE_DATA = "profile_data" +STATUS_CODE = "status_code" +CODE = "code" +GEO = "geo" diff --git a/smart_kit/names/message_names.py b/smart_kit/names/message_names.py index 6a344a49..c93ce789 100644 --- a/smart_kit/names/message_names.py +++ b/smart_kit/names/message_names.py @@ -6,3 +6,5 @@ RUN_APP = "RUN_APP" CLOSE_APP = "CLOSE_APP" SERVER_ACTION = "SERVER_ACTION" +GET_PROFILE_DATA = "GET_PROFILE_DATA" +TAKE_PROFILE_DATA = "TAKE_PROFILE_DATA" diff --git a/smart_kit/resources/__init__.py b/smart_kit/resources/__init__.py index ba834ec8..ba9ca858 100644 --- a/smart_kit/resources/__init__.py +++ b/smart_kit/resources/__init__.py @@ -90,6 +90,7 @@ from scenarios.user.preprocessing_messages.preprocessing_messages_description import \ PreprocessingMessagesDescription from smart_kit.action.http import HTTPRequestAction +from smart_kit.action.smart_geo_action import SmartGeoAction from smart_kit.message.get_to_message import to_messages from smart_kit.message.smart_app_push_message import SmartAppPushToMessage from smart_kit.request.kafka_request import SmartKitKafkaRequest @@ -315,6 +316,7 @@ def init_actions(self): actions["push"] = PushAction actions["give_me_memory"] = GiveMeMemoryAction actions["remember_this"] = RememberThisAction + actions["smart_geo"] = SmartGeoAction def init_requirements(self): requirements[None] = Requirement diff --git a/smart_kit/template/static/references/actions/actions.json b/smart_kit/template/static/references/actions/actions.json index 45c82b18..aecc281d 100644 --- a/smart_kit/template/static/references/actions/actions.json +++ b/smart_kit/template/static/references/actions/actions.json @@ -21,5 +21,33 @@ "type": "template", "template": "{{ payload.new_session and settings['template_settings'].get('reset_context_on_new_session', False)}}" } + }, + "smart_geo_success": { + "type": "string", + "command": "ANSWER_TO_USER", + "nodes": { + "pronounceText": "Геоданные пользователя", + "items": [ + { + "bubble": { + "text": "Геоданные пользователя: {{ user.variables.smart_geo }}" + } + } + ] + } + }, + "smart_geo_fail": { + "type": "string", + "command": "ANSWER_TO_USER", + "nodes": { + "pronounceText": "Ошибка при получении геоданных пользователя", + "items": [ + { + "bubble": { + "text": "Ошибка при получении геоданных пользователя" + } + } + ] + } } } \ No newline at end of file diff --git a/tests/scenarios_tests/actions_test/test_action.py b/tests/scenarios_tests/actions_test/test_action.py index 6fe2bdd8..e43eae0b 100644 --- a/tests/scenarios_tests/actions_test/test_action.py +++ b/tests/scenarios_tests/actions_test/test_action.py @@ -24,6 +24,8 @@ from scenarios.actions.action import ClearFormAction, ClearInnerFormAction, BreakScenarioAction, AskAgainAction, \ RemoveFormFieldAction, RemoveCompositeFormFieldAction from scenarios.scenario_models.history import Event +from smart_kit.action.smart_geo_action import SmartGeoAction +from smart_kit.message.smartapp_to_message import SmartAppToMessage from smart_kit.utils.picklable_mock import PicklableMock, PicklableMagicMock @@ -238,7 +240,8 @@ def test_action_3(self): self.assertEqual(result[0].name, "cmd_id") self.assertEqual(result[0].raw, {'messageName': 'cmd_id', 'payload': {}}) behavior.add.assert_called_once_with( - self.user.message.generate_new_callback_id(), "test", scenarios_names[-1], text_preprocessing_result_raw, ANY + self.user.message.generate_new_callback_id(), "test", scenarios_names[-1], text_preprocessing_result_raw, + ANY ) @@ -711,3 +714,35 @@ def test_action_with_jinja(self): self.user.history.add_event.assert_called_once() self.user.history.add_event.assert_called_once_with(expected) + + +class SmartGeoActionTest(unittest.TestCase): + + def setUp(self): + items = {"type": "smart_geo"} + self.smart_geo_action = SmartGeoAction(items) + + def test_action_send_request(self): + incoming_message = Mock(incremental_id="1605196199186625000", + session_id="0062530b-5521-42cc-90b0-a9d65dea4e98", + uuid={"userChannel": "B2C", "userId": "ec8a9097-1508-4bec-8d97-67f2329c03e0", + "sub": "385342565001000018390f1f"}, + payload={}) + user = Mock() + text_preprocessing_result = Mock() + params = Mock() + command = self.smart_geo_action.run(user, text_preprocessing_result, params)[0] + answer = SmartAppToMessage(command, incoming_message, None) + expected = { + "messageId": "1605196199186625000", + "sessionId": "0062530b-5521-42cc-90b0-a9d65dea4e98", + "messageName": "GET_PROFILE_DATA", + "uuid": { + "userId": "ec8a9097-1508-4bec-8d97-67f2329c03e0", + "userChannel": "B2C", + "sub": "385342565001000018390f1f" + }, + "payload": { + } + } + self.assertEqual(answer.as_dict, expected) diff --git a/tests/smart_kit_tests/handlers/test_handle_take_profile_data.py b/tests/smart_kit_tests/handlers/test_handle_take_profile_data.py new file mode 100644 index 00000000..17d53bae --- /dev/null +++ b/tests/smart_kit_tests/handlers/test_handle_take_profile_data.py @@ -0,0 +1,48 @@ +# coding: utf-8 +import unittest +from unittest.mock import Mock, MagicMock + +from smart_kit.handlers import handler_take_profile_data + + +class MockVariables(Mock): + _storage = {} + + def set(self, x, y): + self._storage[x] = y + + def __getitem__(self, item): + return self._storage[item] + + def get(self, item, default=None): + if item in self._storage: + return self._storage[item] + return default + + +class HandleTakeProfileDataTest(unittest.TestCase): + def setUp(self): + self.app_name = "TestAppName" + self.test_payload_1 = {"server_action": {}} + self.test_payload_2 = {"server_action": {"action_id": 1, "parameters": 1}} + self.test_user = MagicMock('user', message=MagicMock(message_name="some_name"), variables=MockVariables()) + self.test_user.descriptions = {"external_actions": {"smart_geo_fail": MagicMock(run=lambda x, y: "fail"), + "smart_geo_success": MagicMock(run=lambda x, y: "success")}} + + def test_handle_take_profile_data_init(self): + obj = handler_take_profile_data.HandlerTakeProfileData(self.app_name) + self.assertIsNotNone(obj.SUCCESS_CODE) + self.assertIsNotNone(handler_take_profile_data.GEO) + + def test_handle_take_profile_data_run_fail(self): + obj = handler_take_profile_data.HandlerTakeProfileData(self.app_name) + payload = {"status_code": {"code": 102}} + self.assertEqual(obj.run(payload, self.test_user), "fail") + + def test_handle_take_profile_data_run_success(self): + obj = handler_take_profile_data.HandlerTakeProfileData(self.app_name) + payload = {"profile_data": {"geo": {"reverseGeocoding": {"country": "Российская Федерация"}, + "location": {"lat": 10.125, "lon": 10.0124}}}, + "status_code": {"code": 1}} + self.assertEqual(obj.run(payload, self.test_user), "success") + self.assertEqual(self.test_user.variables.get("smart_geo"), payload["profile_data"]["geo"])