├── .gitattributes ├── .github └── workflows │ ├── python-package.yml │ └── python-publish-to-pip.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── __init__.py ├── core ├── __init__.py ├── basic_models │ ├── __init__.py │ ├── actions │ │ ├── __init__.py │ │ ├── basic_actions.py │ │ ├── client_profile_actions.py │ │ ├── command.py │ │ ├── counter_actions.py │ │ ├── external_actions.py │ │ ├── push_action.py │ │ ├── string_actions.py │ │ └── variable_actions.py │ ├── answer_items │ │ ├── __init__.py │ │ └── answer_items.py │ ├── classifiers │ │ ├── __init__.py │ │ ├── basic_classifiers.py │ │ ├── classifiers_constants.py │ │ ├── external_classifiers.py │ │ └── vectorizer_models.py │ ├── counter │ │ ├── __init__.py │ │ ├── counter.py │ │ └── counters.py │ ├── operators │ │ ├── __init__.py │ │ ├── comparators.py │ │ └── operators.py │ ├── parametrizers │ │ ├── __init__.py │ │ ├── filter.py │ │ └── parametrizer.py │ ├── requirement │ │ ├── __init__.py │ │ ├── basic_requirements.py │ │ ├── counter_requirements.py │ │ ├── device_requirements.py │ │ ├── external_requirements.py │ │ ├── project_requirements.py │ │ └── user_text_requirements.py │ ├── scenarios │ │ ├── __init__.py │ │ └── base_scenario.py │ └── variables │ │ ├── __init__.py │ │ └── variables.py ├── configs │ ├── __init__.py │ ├── base_config.py │ └── global_constants.py ├── db_adapter │ ├── __init__.py │ ├── aioredis_adapter.py │ ├── aioredis_sentinel_adapter.py │ ├── ceph │ │ ├── __init__.py │ │ ├── ceph_adapter.py │ │ ├── ceph_exception.py │ │ └── ceph_io.py │ ├── db_adapter.py │ ├── error.py │ ├── ignite_adapter.py │ ├── ignite_thread_adapter.py │ ├── memory_adapter.py │ ├── os_adapter.py │ └── redis_adapter.py ├── descriptions │ ├── __init__.py │ ├── descriptions.py │ ├── descriptions_items.py │ └── smart_updatable_descriptions_items.py ├── logging │ ├── __init__.py │ ├── logger_constants.py │ ├── logger_formatter.py │ ├── logger_handlers.py │ └── logger_utils.py ├── message │ ├── __init__.py │ ├── app_info.py │ ├── device.py │ ├── from_message.py │ ├── message_constants.py │ └── msg_validator.py ├── model │ ├── __init__.py │ ├── base_user.py │ ├── factory.py │ ├── field.py │ ├── heapq │ │ ├── __init__.py │ │ └── heapq_storage.py │ ├── lazy_items.py │ ├── model.py │ ├── queued_objects │ │ ├── __init__.py │ │ ├── limited_queued_hashable_objects.py │ │ └── limited_queued_hashable_objects_description.py │ └── registered.py ├── monitoring │ ├── __init__.py │ ├── healthcheck_handler.py │ ├── monitoring.py │ └── twisted_server.py ├── mq │ ├── __init__.py │ └── kafka │ │ ├── __init__.py │ │ ├── base_kafka_consumer.py │ │ ├── base_kafka_publisher.py │ │ ├── kafka_consumer.py │ │ └── kafka_publisher.py ├── names │ ├── __init__.py │ └── field.py ├── repositories │ ├── __init__.py │ ├── base_repository.py │ ├── classifier_repository.py │ ├── csv_repository.py │ ├── dill_repository.py │ ├── file_repository.py │ ├── folder_repository.py │ ├── items_repository.py │ └── shard_repository.py ├── request │ ├── __init__.py │ ├── base_request.py │ ├── external_requests.py │ ├── kafka_request.py │ └── rest_request.py ├── text_preprocessing │ ├── __init__.py │ ├── base.py │ ├── constants.py │ ├── grammem │ │ ├── __init__.py │ │ └── grammem_constants.py │ ├── helpers.py │ └── preprocessing_result.py ├── unified_template │ ├── __init__.py │ ├── currency2text.py │ ├── jinja_filters.py │ ├── num2ordinal.py │ ├── num2text.py │ ├── pan2service.py │ └── unified_template.py └── utils │ ├── __init__.py │ ├── delay_runner.py │ ├── exception_handlers.py │ ├── fallback.py │ ├── loader.py │ ├── masking_message.py │ ├── memstats.py │ ├── period_determiner.py │ ├── pickle_copy.py │ ├── rerunable.py │ ├── singleton.py │ ├── stats_timer.py │ └── utils.py ├── scenarios ├── __init__.py ├── actions │ ├── __init__.py │ ├── action.py │ └── action_params_names.py ├── behaviors │ ├── __init__.py │ ├── behavior_description.py │ ├── behavior_descriptions.py │ └── behaviors.py ├── logging │ ├── __init__.py │ └── logger_constants.py ├── requirements │ ├── __init__.py │ └── requirements.py ├── scenario_descriptions │ ├── __init__.py │ ├── form_filling_scenario.py │ ├── scenarios_description.py │ └── tree_scenario │ │ ├── __init__.py │ │ ├── tree_scenario.py │ │ └── tree_scenario_node.py ├── scenario_models │ ├── __init__.py │ ├── field │ │ ├── __init__.py │ │ ├── composite_field.py │ │ ├── composite_fillers.py │ │ ├── external_field_filler_descriptions.py │ │ ├── field.py │ │ ├── field_descriptions │ │ │ ├── __init__.py │ │ │ ├── basic_field_description.py │ │ │ ├── composite_field_description.py │ │ │ ├── integration_field_description.py │ │ │ └── question_field_description.py │ │ ├── field_filler_description.py │ │ └── fields.py │ ├── field_requirements │ │ ├── __init__.py │ │ └── field_requirements.py │ ├── field_validator │ │ ├── __init__.py │ │ └── field_validator.py │ ├── forms │ │ ├── __init__.py │ │ ├── composite_forms.py │ │ ├── form.py │ │ ├── form_description.py │ │ ├── forms.py │ │ └── forms_description.py │ ├── history │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── formatters.py │ │ ├── history.py │ │ └── history_description.py │ └── scenario_models.py ├── user │ ├── __init__.py │ ├── last_fields │ │ ├── __init__.py │ │ ├── last_field.py │ │ └── last_fields.py │ ├── last_scenarios │ │ ├── __init__.py │ │ ├── last_scenarios.py │ │ ├── last_scenarios_description.py │ │ └── last_scenarios_descriptions.py │ ├── message_history │ │ ├── __init__.py │ │ └── message_history.py │ ├── parametrizer.py │ ├── preprocessing_messages │ │ ├── __init__.py │ │ ├── prepricessing_messages_for_scenarios.py │ │ └── preprocessing_messages_description.py │ ├── reply_selector │ │ ├── __init__.py │ │ └── reply_selector.py │ └── user_model.py └── utils │ └── __init__.py ├── setup.cfg ├── setup.py ├── smart_kit ├── __init__.py ├── __main__.py ├── _version.py ├── action │ ├── __init__.py │ └── http.py ├── compatibility │ ├── __init__.py │ └── commands.py ├── configs │ ├── __init__.py │ ├── logger_config.py │ └── settings.py ├── handlers │ ├── __init__.py │ ├── handle_close_app.py │ ├── handle_respond.py │ ├── handle_server_action.py │ ├── handler_base.py │ ├── handler_run_app.py │ ├── handler_text.py │ └── handler_timeout.py ├── management │ ├── __init__.py │ ├── app_manager.py │ ├── base.py │ ├── cache.py │ ├── get_bundles_from_pps.py │ ├── plugins.py │ ├── smart_kit_manager.py │ └── tests.py ├── message │ ├── __init__.py │ ├── app_info.py │ ├── get_to_message.py │ ├── smart_app_push_message.py │ └── smartapp_to_message.py ├── models │ ├── __init__.py │ ├── dialogue_manager.py │ └── smartapp_model.py ├── names │ ├── __init__.py │ ├── action_params_names.py │ └── message_names.py ├── request │ ├── __init__.py │ └── kafka_request.py ├── resources │ └── __init__.py ├── start_points │ ├── __init__.py │ ├── app.py │ ├── base_main_loop.py │ ├── main_loop_async_http.py │ ├── main_loop_http.py │ ├── main_loop_kafka.py │ ├── main_loop_parallel_kafka.py │ └── postprocess.py ├── system_answers │ ├── __init__.py │ └── nothing_found_action.py ├── template │ ├── app │ │ ├── __init__.py-tpl │ │ ├── adapters │ │ │ ├── __init__.py-tpl │ │ │ └── db_adapters.py-tpl │ │ ├── basic_entities │ │ │ ├── __init__.py-tpl │ │ │ ├── actions.py-tpl │ │ │ ├── fillers.py-tpl │ │ │ └── requirements.py-tpl │ │ ├── handlers │ │ │ ├── __init__.py-tpl │ │ │ └── handlers.py-tpl │ │ ├── local_testing │ │ │ ├── __init__.py-tpl │ │ │ └── custom_local_testing.py-tpl │ │ ├── models │ │ │ ├── __init__.py-tpl │ │ │ ├── dialogue_manager.py-tpl │ │ │ └── model.py-tpl │ │ ├── resources │ │ │ ├── __init__.py-tpl │ │ │ └── custom_app_resourses.py-tpl │ │ └── user │ │ │ ├── __init__.py-tpl │ │ │ ├── parametrizer.py-tpl │ │ │ └── user.py-tpl │ ├── app_config.py-tpl │ ├── manage.py-tpl │ ├── monitoring │ │ ├── __init__.py-tpl │ │ └── monitoring_wsgi_app.py-tpl │ ├── static │ │ ├── .text_normalizer_resources │ │ │ ├── dict_synonyms.json │ │ │ ├── static_workdata.json │ │ │ └── text2num_dict.json │ │ ├── configs │ │ │ ├── logging_config.yml │ │ │ └── template_config.yml │ │ └── references │ │ │ ├── actions │ │ │ └── actions.json │ │ │ ├── behaviors │ │ │ └── behaviors.json │ │ │ ├── bundles │ │ │ └── bundles.json │ │ │ ├── classifiers │ │ │ └── classifiers.json │ │ │ ├── classifiers_data │ │ │ └── ReadBookOrNotClassifier.pkl │ │ │ ├── field_fillers │ │ │ └── field_fillers.json │ │ │ ├── forms │ │ │ └── hello_form.json │ │ │ ├── history.json │ │ │ ├── last_scenarios_descriptions.json │ │ │ ├── predefined_fields_storage.json │ │ │ ├── preprocessing_messages_for_scenarios_settings.json │ │ │ ├── responses.json │ │ │ ├── scenarios │ │ │ ├── hello_scenario.json │ │ │ └── run_app.json │ │ │ ├── test_template.json │ │ │ └── tests │ │ │ └── hello_scenario_tests.json │ ├── wsgi.py-tpl │ └── wsgi_config.py-tpl ├── testing │ ├── __init__.py │ ├── local.py │ ├── suite.py │ └── utils.py ├── text_preprocessing │ ├── __init__.py │ ├── base_text_normalizer.py │ ├── http_text_normalizer.py │ ├── local_text_normalizer.py │ ├── nltk_tokenizer_binding.py │ ├── pymorphy2_morph_wrapper.py │ ├── remote.py │ ├── text2num.py │ └── utils.py └── utils │ ├── SmartAppToMessage_pb2.py │ ├── __init__.py │ ├── cache.py │ ├── diff.py │ ├── logger_writer │ ├── __init__.py │ └── logger_formatter.py │ ├── monitoring.py │ └── picklable_mock.py ├── tests ├── __init__.py ├── core_tests │ ├── __init__.py │ ├── basic_scenario_models_test │ │ ├── __init__.py │ │ ├── action_test │ │ │ ├── __init__.py │ │ │ ├── test_action.py │ │ │ ├── test_command.py │ │ │ └── test_random_action.py │ │ ├── test_answer_items.py │ │ ├── test_counter.py │ │ ├── test_parametrizer.py │ │ └── test_variables.py │ ├── configs │ │ ├── __init__.py │ │ └── test_global_constants.py │ ├── descriptions │ │ ├── __init__.py │ │ ├── test_descriptions.py │ │ ├── test_lazy_descriptions.py │ │ └── test_smart_updatable_lazy_descriptions.py │ ├── jinja_tests │ │ ├── __init__.py │ │ └── test_jinja.py │ ├── masking_test │ │ ├── __init__.py │ │ └── masking_test.py │ ├── model_test │ │ ├── __init__.py │ │ ├── test_lazy_items.py │ │ ├── test_limited_queued_hashable_objects.py │ │ └── test_model.py │ ├── monitoring_test │ │ ├── __init__.py │ │ └── test_monitoring.py │ ├── mq_test │ │ ├── __init__.py │ │ └── test_from_message.py │ ├── operators_test │ │ ├── __init__.py │ │ ├── test_comparators.py │ │ └── test_operators.py │ ├── repositories_test │ │ ├── __init__.py │ │ └── test_repo.py │ ├── request_test │ │ ├── __init__.py │ │ └── test_rest_request.py │ ├── requirements_test │ │ ├── __init__.py │ │ └── test_requirements.py │ ├── test_utils │ │ ├── __init__.py │ │ ├── test_date_determining.py │ │ ├── test_delay_runner.py │ │ ├── test_logger.py │ │ ├── test_number_converter.py │ │ ├── test_rerunable.py │ │ ├── test_time_check.py │ │ └── test_timer.py │ └── unified_template_test │ │ ├── __init__.py │ │ ├── test_money2text.py │ │ └── test_unified_template.py ├── scenarios_tests │ ├── __init__.py │ ├── actions_test │ │ ├── __init__.py │ │ └── test_action.py │ ├── behaviors_test │ │ ├── __init__.py │ │ ├── test_behavior_description.py │ │ ├── test_behavior_model.py │ │ └── test_behaviors.py │ ├── fields_test │ │ ├── __init__.py │ │ ├── test_composite_field.py │ │ └── test_fields.py │ ├── fillers │ │ ├── __init__.py │ │ ├── _trial_temp │ │ │ └── _trial_marker │ │ ├── test_approve.py │ │ ├── test_available_info_filler.py │ │ ├── test_classifier_filler.py │ │ ├── test_composite_filler.py │ │ ├── test_external_filler.py │ │ ├── test_first_meeting.py │ │ ├── test_geo_token_filler.py │ │ ├── test_intersection.py │ │ ├── test_org_token_filler.py │ │ ├── test_person_filler.py │ │ ├── test_previous_messages_filler.py │ │ ├── test_regexp_and_string_operations_filler.py │ │ ├── test_regexp_filler.py │ │ └── test_regexps_filler.py │ ├── forms_test │ │ ├── __init__.py │ │ └── test_forms.py │ ├── preprocessing_messages_test │ │ ├── __init__.py │ │ ├── test_preprocessing_messages_description.py │ │ └── test_preprocessing_scenarios_messages.py │ ├── requirements_test │ │ ├── __init__.py │ │ └── test_requirements.py │ ├── scenario_models_test │ │ ├── __init__.py │ │ └── test_history.py │ ├── scenarios_test │ │ ├── __init__.py │ │ ├── test_last_scenarios.py │ │ └── test_tree_scenario.py │ └── user_models │ │ ├── __init__.py │ │ ├── test_field.py │ │ ├── test_is_int_value.py │ │ ├── test_last_fields.py │ │ └── test_token_part_in_set_requirement.py └── smart_kit_tests │ ├── __init__.py │ ├── action │ ├── __init__.py │ ├── test_http.py │ └── test_run_scenario_by_project_name.py │ ├── adapters │ ├── __init__.py │ └── test_memory_adapter.py │ ├── compatibility │ └── test_commands.py │ ├── configs │ ├── __init__.py │ ├── test_logger_config.py │ └── test_settings.py │ ├── handlers │ ├── __init__.py │ ├── test_handle_close_app.py │ ├── test_handle_respond.py │ ├── test_handle_server_action.py │ ├── test_handler_base.py │ ├── test_handler_text.py │ └── test_handler_timeout.py │ ├── management │ ├── __init__.py │ ├── test_base.py │ └── test_smart_kit_manager.py │ ├── message │ ├── __init__.py │ ├── test_app_info.py │ ├── test_device.py │ ├── test_smart_app_push_message.py │ └── test_smartapp_to_message.py │ ├── models │ ├── __init__.py │ ├── test_dialogue_manager.py │ └── test_heapq_storage.py │ ├── names │ ├── __init__.py │ ├── test_field.py │ └── test_manage_names.py │ ├── request │ ├── __init__.py │ └── test_kafka_request.py │ ├── requirement │ ├── __init__.py │ └── test_device_requirements.py │ ├── system_answers │ ├── __init__.py │ └── test_nothing_found_action.py │ ├── text_preprocessing │ ├── __init__.py │ ├── test_normalization.py │ └── test_remote.py │ └── user │ ├── __init__.py │ ├── test_parametrizer.py │ └── test_user_model.py └── versioneer.py /.gitattributes: -------------------------------------------------------------------------------- 1 | smart_kit/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: [3.8, 3.9] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | python -m pip install flake8 30 | python -m pip install . 31 | - name: Lint with flake8 32 | run: | 33 | # stop the build if there are Python syntax errors or undefined names 34 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 35 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 36 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 37 | - name: Test with unittest 38 | run: | 39 | python -m unittest discover -s tests 40 | -------------------------------------------------------------------------------- /.github/workflows/python-publish-to-pip.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | push: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | deploy: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | - name: Set up Python 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: '3.x' 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install setuptools wheel twine 30 | - name: Build and publish 31 | env: 32 | TWINE_USERNAME: __token__ 33 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 34 | run: | 35 | python setup.py sdist bdist_wheel 36 | twine upload dist/* 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | __pycache__ 4 | *.orig 5 | *.pyc 6 | *.pyo 7 | *.ipynb 8 | venv 9 | 10 | *.egg-info/ 11 | build/ 12 | 13 | *.ipynb* 14 | 15 | 16 | *.log 17 | ai/backlog.md 18 | ai/log 19 | 20 | app.db 21 | search.db 22 | flask 23 | coverage_html_report 24 | htmlcov 25 | .coverage 26 | .cache 27 | .pytest_cache 28 | 29 | !.gitignore 30 | !README.md 31 | 32 | /coverage.xml 33 | /xunit.xml 34 | 35 | /.scannerwork/ 36 | 37 | tests/TestPass.csv 38 | tests/parsed_log.csv 39 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include smart_kit/template * 2 | include versioneer.py 3 | include smart_kit/_version.py 4 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/__init__.py -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | import core.unified_template.jinja_filters # don't delete - it's used to load filters before first jinja usage 2 | -------------------------------------------------------------------------------- /core/basic_models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/__init__.py -------------------------------------------------------------------------------- /core/basic_models/actions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/actions/__init__.py -------------------------------------------------------------------------------- /core/basic_models/actions/command.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class Command: 5 | def __init__(self, name, params=None, action_id=None, request_type=None, request_data=None, loader=None): 6 | """ 7 | Initialize Command instance with params 8 | 9 | :param name: str, command name 10 | :param params: 11 | :param action_id: 12 | :param request_type: 13 | :param request_data: 14 | :param loader: loader name for data before send. Possible loader values: json.dumps / protobuf 15 | """ 16 | 17 | self.name = name 18 | self.payload = params or {} 19 | self.action_id = action_id 20 | self.request_type = request_type 21 | self.request_data = request_data or {} 22 | self.loader = loader or "json.dumps" 23 | 24 | @property 25 | def raw(self): 26 | message = {"messageName": self.name, "payload": self.payload} 27 | if self.action_id is not None: 28 | message["action_id"] = self.action_id 29 | return message 30 | -------------------------------------------------------------------------------- /core/basic_models/actions/external_actions.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Any, Union, List 2 | 3 | from core.model.base_user import BaseUser 4 | 5 | from core.basic_models.actions.command import Command 6 | from core.text_preprocessing.base import BaseTextPreprocessingResult 7 | 8 | from core.basic_models.actions.basic_actions import action_factory 9 | from core.basic_models.actions.basic_actions import CommandAction 10 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 11 | 12 | 13 | class ExternalActions(SmartUpdatableDescriptionsItems): 14 | def __init__(self, items): 15 | super(ExternalActions, self).__init__(action_factory, items) 16 | 17 | 18 | class ExternalAction(CommandAction): 19 | version: Optional[int] 20 | command: str 21 | action: str 22 | 23 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None): 24 | super(ExternalAction, self).__init__(items, id) 25 | self._action_key = items["action"] 26 | 27 | def run(self, user: BaseUser, text_preprocessing_result: BaseTextPreprocessingResult, 28 | params: Optional[Dict[str, Union[str, float, int]]] = None) -> List[Command]: 29 | action = user.descriptions["external_actions"][self._action_key] 30 | commands = action.run(user, text_preprocessing_result, params) 31 | return commands 32 | -------------------------------------------------------------------------------- /core/basic_models/answer_items/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/answer_items/__init__.py -------------------------------------------------------------------------------- /core/basic_models/classifiers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/classifiers/__init__.py -------------------------------------------------------------------------------- /core/basic_models/classifiers/classifiers_constants.py: -------------------------------------------------------------------------------- 1 | """Константы классификаторов.""" 2 | 3 | 4 | # Константы любого классификатора, формирующие шаблон ответа 5 | SCORE_KEY = "score" 6 | ANSWER_KEY = "answer" 7 | OTHER_KEY = "other" 8 | 9 | # Дополнительные константы для внешних "external" классификаторов 10 | EXTERNAL_CLASSIFIER_BLOCKING_TIMEOUT = 0.2 # Время (в секундах) за которое классификатор должен успеть ответить 11 | 12 | # Константы классификатора Intent Recognizer 13 | INTENT_RECOGNIZER_ANSWER_DISTANCE_KEY = "answer_distance" 14 | INTENT_RECOGNIZER_ANSWER_KEY = "answer" 15 | INTENT_RECOGNIZER_OTHER_KEY = "other" 16 | 17 | # Параметры, наличие которых обязательно в конфиге любого классификатора 18 | REQUIRED_CONFIG_PARAMS = frozenset(["type"]) 19 | -------------------------------------------------------------------------------- /core/basic_models/classifiers/external_classifiers.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any 2 | 3 | from core.basic_models.classifiers.basic_classifiers import classifier_factory 4 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 5 | 6 | 7 | class ExternalClassifiers(SmartUpdatableDescriptionsItems): 8 | 9 | def __init__(self, settings: Dict[str, Any]) -> None: 10 | super(ExternalClassifiers, self).__init__(classifier_factory, settings, ordered=True) 11 | -------------------------------------------------------------------------------- /core/basic_models/classifiers/vectorizer_models.py: -------------------------------------------------------------------------------- 1 | from core.model.factory import build_factory 2 | from core.model.registered import Registered 3 | 4 | vectorizers = Registered() 5 | 6 | vectorizer_factory = build_factory(vectorizers) 7 | -------------------------------------------------------------------------------- /core/basic_models/counter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/counter/__init__.py -------------------------------------------------------------------------------- /core/basic_models/counter/counters.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from typing import Dict 3 | 4 | from core.basic_models.counter.counter import Counter 5 | 6 | 7 | class Counters: 8 | 9 | def __init__(self, items: Dict[str, Dict], user): 10 | self._raw_items = items or {} 11 | self._items: Dict[str, Counter] = {} 12 | self._item_type = Counter 13 | 14 | def __getitem__(self, key): 15 | return self.get(key) 16 | 17 | def get(self, key): 18 | counter = self._items.get(key) 19 | if counter is None: 20 | raw_data = self._raw_items.get(key) 21 | counter = self._item_type(raw_data) 22 | self._items[key] = counter 23 | return counter 24 | 25 | def clear(self, key): 26 | if key in self._items: 27 | del self._items[key] 28 | if key in self._raw_items: 29 | self._raw_items.pop(key) 30 | 31 | def expire(self): 32 | items = list(self.raw.keys()) 33 | for key in items: 34 | if self[key].check_expire(): 35 | self.clear(key) 36 | 37 | @property 38 | def raw(self): 39 | for name in self._items: 40 | counter = self._items[name] 41 | value = counter.raw 42 | if value: 43 | self._raw_items[name] = value 44 | else: 45 | if name in self._raw_items: 46 | self._raw_items.pop(name) 47 | return self._raw_items 48 | -------------------------------------------------------------------------------- /core/basic_models/operators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/operators/__init__.py -------------------------------------------------------------------------------- /core/basic_models/operators/comparators.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from typing import Iterable, Dict, Any, Optional 3 | 4 | from core.model.factory import build_factory 5 | from core.model.registered import Registered 6 | 7 | comparators = Registered() 8 | 9 | comparator_factory = build_factory(comparators) 10 | 11 | 12 | class Comparator: 13 | def __init__(self, items: Optional[Dict[str, Any]]) -> None: 14 | pass 15 | 16 | def compare(self, left: Any, right: Any) -> bool: 17 | raise NotImplementedError 18 | 19 | 20 | class MoreComparator(Comparator): 21 | def compare(self, left: int, right: int) -> bool: 22 | return left > right 23 | 24 | 25 | class LessComparator(Comparator): 26 | def compare(self, left: int, right: int) -> bool: 27 | return left < right 28 | 29 | 30 | class MoreOrEqualComparator(Comparator): 31 | def compare(self, left: int, right: int) -> bool: 32 | return left >= right 33 | 34 | 35 | class LessOrEqualComparator(Comparator): 36 | def compare(self, left: int, right: int) -> bool: 37 | return left <= right 38 | 39 | 40 | class EqualComparator(Comparator): 41 | def compare(self, left: int, right: int) -> bool: 42 | return left == right 43 | 44 | 45 | class NotEqualComparator(Comparator): 46 | def compare(self, left: int, right: int) -> bool: 47 | return left != right 48 | 49 | 50 | class InComparator(Comparator): 51 | def compare(self, left: Any, right: Iterable[Any]) -> bool: 52 | return left in right 53 | -------------------------------------------------------------------------------- /core/basic_models/parametrizers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/parametrizers/__init__.py -------------------------------------------------------------------------------- /core/basic_models/parametrizers/filter.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Filter: 4 | def __init__(self, items): 5 | pass 6 | 7 | def filter_out(self, data, user, params=None): 8 | return data 9 | -------------------------------------------------------------------------------- /core/basic_models/parametrizers/parametrizer.py: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | from core.basic_models.parametrizers.filter import Filter 4 | 5 | 6 | class BasicParametrizer: 7 | def __init__(self, user, items): 8 | self._user = user 9 | self._filter = items.get("filter") or {} 10 | 11 | @lazy 12 | def filter(self): 13 | return Filter(self._filter) 14 | 15 | def filter_out(self, data, filter_params=None): 16 | return self.filter.filter_out(data, self._user, filter_params) 17 | 18 | def _get_user_data(self, text_preprocessing_result=None): 19 | return {"message": self._user.message} 20 | 21 | def collect(self, text_preprocessing_result=None, filter_params=None): 22 | data = self._get_user_data(text_preprocessing_result) 23 | return self.filter_out(data, filter_params) 24 | -------------------------------------------------------------------------------- /core/basic_models/requirement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/requirement/__init__.py -------------------------------------------------------------------------------- /core/basic_models/requirement/counter_requirements.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | from core.model.base_user import BaseUser 4 | 5 | from core.text_preprocessing.base import BaseTextPreprocessingResult 6 | 7 | from core.basic_models.operators.operators import Operator 8 | from core.basic_models.requirement.basic_requirements import ComparisonRequirement 9 | from time import time 10 | 11 | 12 | class CounterValueRequirement(ComparisonRequirement): 13 | operator: Operator 14 | key: str 15 | 16 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None) -> None: 17 | super(CounterValueRequirement, self).__init__(items, id) 18 | items = items or {} 19 | self.key = items["key"] 20 | 21 | def check(self, text_preprocessing_result: BaseTextPreprocessingResult, user: BaseUser, 22 | params: Dict[str, Any] = None) -> bool: 23 | counter = user.counters[self.key] 24 | return self.operator.compare(counter) 25 | 26 | 27 | class CounterUpdateTimeRequirement(ComparisonRequirement): 28 | operator: Operator 29 | key: str 30 | 31 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None) -> None: 32 | super(CounterUpdateTimeRequirement, self).__init__(items, id) 33 | items = items or {} 34 | self.key = items["key"] 35 | self.fallback_value = items.get("fallback_value") or False 36 | 37 | def check(self, text_preprocessing_result: BaseTextPreprocessingResult, user: BaseUser, 38 | params: Dict[str, Any] = None) -> bool: 39 | _time = user.counters[self.key].update_time 40 | return self.operator.compare(time() - _time) if _time else self.fallback_value 41 | -------------------------------------------------------------------------------- /core/basic_models/requirement/external_requirements.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | from core.basic_models.requirement.basic_requirements import Requirement, requirement_factory 4 | from core.model.base_user import BaseUser 5 | 6 | from core.text_preprocessing.base import BaseTextPreprocessingResult 7 | 8 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 9 | 10 | 11 | class ExternalRequirements(SmartUpdatableDescriptionsItems): 12 | def __init__(self, items): 13 | super(ExternalRequirements, self).__init__(requirement_factory, items, ordered=True) 14 | 15 | 16 | class ExternalRequirement(Requirement): 17 | requirement: str 18 | 19 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None) -> None: 20 | super(ExternalRequirement, self).__init__(items, id) 21 | self.requirement = items["requirement"] 22 | 23 | def check(self, text_preprocessing_result: BaseTextPreprocessingResult, user: BaseUser, 24 | params: Dict[str, Any] = None) -> bool: 25 | requirement = user.descriptions["external_requirements"][self.requirement] 26 | return requirement.check(text_preprocessing_result, user, params) 27 | -------------------------------------------------------------------------------- /core/basic_models/requirement/project_requirements.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | from core.model.base_user import BaseUser 4 | 5 | from core.text_preprocessing.base import BaseTextPreprocessingResult 6 | 7 | from core.basic_models.requirement.basic_requirements import Requirement 8 | 9 | 10 | class SettingsRequirement(Requirement): 11 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None) -> None: 12 | super().__init__(items, id) 13 | self._config = items.get("config", "template_settings") 14 | self._key = items["key"] 15 | self._value = items["value"] 16 | 17 | def check(self, text_preprocessing_result: BaseTextPreprocessingResult, user: BaseUser, 18 | params: Dict[str, Any] = None) -> bool: 19 | return user.settings[self._config][self._key] == self._value 20 | -------------------------------------------------------------------------------- /core/basic_models/scenarios/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/scenarios/__init__.py -------------------------------------------------------------------------------- /core/basic_models/variables/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/basic_models/variables/__init__.py -------------------------------------------------------------------------------- /core/basic_models/variables/variables.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Variables: 5 | DEFAULT_TTL = 86400 6 | 7 | def __init__(self, items, user, savable=True): 8 | self._savable = savable 9 | self._storage = items or {} 10 | 11 | @property 12 | def raw(self): 13 | if self._savable: 14 | return self._storage 15 | return None 16 | 17 | @property 18 | def values(self): 19 | self.expire() 20 | result = {} 21 | for key in self._storage: 22 | value, _ = self._storage[key] 23 | result[key] = value 24 | return result 25 | 26 | def set(self, key, value, ttl=None): 27 | ttl = ttl if ttl is not None else self.DEFAULT_TTL 28 | self._storage[key] = value, time.time() + ttl 29 | 30 | def update(self, key, value, ttl=None): 31 | _, old_ttl = self._storage[key] 32 | ttl = ttl or old_ttl 33 | self.set(key, value, ttl) 34 | 35 | def get(self, key, default=None): 36 | value, expire_time = self._storage.get(key, (default, time.time() + self.DEFAULT_TTL)) 37 | if expire_time <= time.time(): 38 | value = default 39 | return value 40 | 41 | def expire(self): 42 | for key in list(self._storage): 43 | _, expire_time = self._storage[key] 44 | if expire_time <= time.time(): 45 | self.delete(key) 46 | 47 | def delete(self, key): 48 | del self._storage[key] 49 | 50 | def clear(self): 51 | self._storage.clear() 52 | -------------------------------------------------------------------------------- /core/configs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/configs/__init__.py -------------------------------------------------------------------------------- /core/configs/global_constants.py: -------------------------------------------------------------------------------- 1 | CALLBACK_ID_HEADER = "app_callback_id" 2 | LINK_BEHAVIOR_FLAG = "link_previous_behavior" 3 | KAFKA = "kafka" 4 | -------------------------------------------------------------------------------- /core/db_adapter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/db_adapter/__init__.py -------------------------------------------------------------------------------- /core/db_adapter/ceph/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/db_adapter/ceph/__init__.py -------------------------------------------------------------------------------- /core/db_adapter/ceph/ceph_exception.py: -------------------------------------------------------------------------------- 1 | class CephIOMaxRetryReached(Exception): 2 | pass 3 | 4 | 5 | class CephIOFileNotFoundException(FileNotFoundError): 6 | pass 7 | -------------------------------------------------------------------------------- /core/db_adapter/ceph/ceph_io.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from core.db_adapter.ceph.ceph_exception import CephIOMaxRetryReached, CephIOFileNotFoundException 4 | from core.utils.rerunable import Rerunable 5 | 6 | 7 | class CephIO(Rerunable): 8 | def __init__(self, config): 9 | super(CephIO, self).__init__(config) 10 | self.bucket = config["bucket"] 11 | self.filename = config["filename"] 12 | self.mode = config["mode"] 13 | 14 | @property 15 | def _handled_exception(self): 16 | return ConnectionError 17 | 18 | def _on_prepare(self): 19 | pass 20 | 21 | def _on_all_tries_fail(self): 22 | raise CephIOMaxRetryReached(f"Can't get key from {self.bucket} in case of {str(self._handled_exception)}.") 23 | 24 | def _get_bucket_keys(self): 25 | return self.bucket.get_key(self.filename) 26 | 27 | def __enter__(self): 28 | key = self._run(self._get_bucket_keys) 29 | if key: 30 | data = self._run(key.get_contents_as_string) 31 | io_stream = None 32 | if self.mode == "r": 33 | io_stream = io.StringIO(data.decode("utf-8")) 34 | elif self.mode == "rb": 35 | io_stream = io.BytesIO(data) 36 | return io_stream 37 | else: 38 | raise CephIOFileNotFoundException(f"{self.filename} is not exist.") 39 | 40 | def __exit__(self, type, value, traceback): 41 | pass 42 | -------------------------------------------------------------------------------- /core/db_adapter/error.py: -------------------------------------------------------------------------------- 1 | from core.db_adapter.db_adapter import DBAdapterException 2 | 3 | 4 | class NotSupportedOperation(DBAdapterException): 5 | def __init__(self, msg=None): 6 | msg = msg or "Operation is not supported" 7 | super().__init__(msg) 8 | -------------------------------------------------------------------------------- /core/db_adapter/memory_adapter.py: -------------------------------------------------------------------------------- 1 | from core.db_adapter import error 2 | from core.db_adapter.db_adapter import DBAdapter 3 | 4 | 5 | class MemoryAdapter(DBAdapter): 6 | 7 | def __init__(self, config=None): 8 | super(DBAdapter, self).__init__(config) 9 | self.memory_storage = {} 10 | 11 | def _glob(self, path, pattern): 12 | raise error.NotSupportedOperation 13 | 14 | def _path_exists(self, path): 15 | raise error.NotSupportedOperation 16 | 17 | def _on_prepare(self): 18 | pass 19 | 20 | def connect(self): 21 | pass 22 | 23 | def _open(self, filename, *args, **kwargs): 24 | pass 25 | 26 | def _save(self, id, data): 27 | self.memory_storage[id] = data 28 | 29 | def _replace_if_equals(self, id, sample, data): 30 | stored_data = self.memory_storage.get(id) 31 | if stored_data == sample: 32 | self.memory_storage[id] = data 33 | return True 34 | return False 35 | 36 | def _get(self, id): 37 | data = self.memory_storage.get(id) 38 | return data 39 | 40 | def _list_dir(self, path): 41 | pass 42 | -------------------------------------------------------------------------------- /core/db_adapter/os_adapter.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import fnmatch 4 | 5 | from core.db_adapter.db_adapter import DBAdapter 6 | from core.db_adapter import error 7 | 8 | 9 | class OSAdapter(DBAdapter): 10 | def _save(self, id, data): 11 | raise error.NotSupportedOperation 12 | 13 | def _replace_if_equals(self, id, sample, data): 14 | raise error.NotSupportedOperation 15 | 16 | def _get(self, id): 17 | raise error.NotSupportedOperation 18 | 19 | def connect(self): 20 | pass 21 | 22 | def _on_prepare(self): 23 | pass 24 | 25 | @property 26 | def source(self): 27 | return self 28 | 29 | def _list_dir(self, path): 30 | result = [] 31 | for path, subdirs, files in os.walk(path): 32 | result.extend([os.path.join(path, name) for name in files if not name.startswith(".")]) 33 | return result 34 | 35 | def _open(self, filename, *args, **kwargs): 36 | return io.open(filename, *args, **kwargs) 37 | 38 | def _get_counter_name(self): 39 | return "os_adapter" 40 | 41 | def _glob(self, path, pattern): 42 | files_list = self._list_dir(path) 43 | filtered = fnmatch.filter(files_list, pattern) 44 | return filtered 45 | 46 | def _path_exists(self, path): 47 | return os.path.exists(path) 48 | 49 | def _mtime(self, path): 50 | return os.path.getmtime(path) 51 | -------------------------------------------------------------------------------- /core/db_adapter/redis_adapter.py: -------------------------------------------------------------------------------- 1 | import redis 2 | import typing 3 | from core.db_adapter.db_adapter import DBAdapter 4 | from core.db_adapter import error 5 | 6 | 7 | class RedisAdapter(DBAdapter): 8 | def __init__(self, config=None): 9 | super().__init__(config) 10 | self._redis: typing.Optional[redis.Redis] = None 11 | 12 | try: 13 | del self.config["type"] 14 | except KeyError: 15 | pass 16 | 17 | def connect(self): 18 | self._redis = redis.Redis(**self.config) 19 | 20 | def _open(self, filename, *args, **kwargs): 21 | pass 22 | 23 | def _save(self, id, data): 24 | return self._redis.set(id, data) 25 | 26 | def _replace_if_equals(self, id, sample, data): 27 | return self._redis.set(id, data) 28 | 29 | def _get(self, id): 30 | data = self._redis.get(id) 31 | return data.decode() if data else None 32 | 33 | def _list_dir(self, path): 34 | raise error.NotSupportedOperation 35 | 36 | def _glob(self, path, pattern): 37 | raise error.NotSupportedOperation 38 | 39 | def _path_exists(self, path): 40 | self._redis.exists(path) 41 | 42 | def _on_prepare(self): 43 | pass 44 | -------------------------------------------------------------------------------- /core/descriptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/descriptions/__init__.py -------------------------------------------------------------------------------- /core/descriptions/descriptions.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from typing import Dict, Callable 3 | 4 | from core.descriptions.descriptions_items import DescriptionsItems 5 | from core.model.registered import Registered 6 | from core.repositories.base_repository import BaseRepository 7 | 8 | registered_description_factories = Registered() 9 | 10 | 11 | def default_description_factory(x): 12 | return x 13 | 14 | 15 | class Descriptions: 16 | def __init__(self, registered_repositories: Dict[str, BaseRepository]) -> None: 17 | self.registered_repositories: Dict[str, BaseRepository] = registered_repositories 18 | self._descriptions: Dict[str, DescriptionsItems] = {} 19 | 20 | def __getitem__(self, key: str) -> DescriptionsItems: 21 | description_item: DescriptionsItems = self._descriptions.get(key) 22 | if description_item is None: 23 | repository: BaseRepository = self.registered_repositories[key] 24 | factory: Callable = registered_description_factories.get(key, default_description_factory) 25 | description_item = factory(repository.data) 26 | self._descriptions[key] = description_item 27 | return description_item 28 | 29 | def __setitem__(self, key: str, description_item: DescriptionsItems) -> None: 30 | self._descriptions[key] = description_item 31 | -------------------------------------------------------------------------------- /core/descriptions/descriptions_items.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | 4 | class DescriptionsItems: 5 | def __init__(self, factory, items, ordered=False): 6 | items = items or {} 7 | self._factory = factory 8 | self._raw_items = None 9 | self._items = None 10 | self._init(items) 11 | 12 | def __contains__(self, key): 13 | return key in self._raw_items 14 | 15 | def _init(self, raw_items): 16 | self._raw_items = raw_items 17 | self._items = dict() 18 | for id in self._raw_items.keys(): 19 | self._get_or_create_item(id) 20 | 21 | def __getitem__(self, id): 22 | return self._get_or_create_item(id) 23 | 24 | def _get_or_create_item(self, id): 25 | existed_item = self._items.get(id) 26 | if existed_item is None: 27 | existed_item = self._factory(id=id, items=self._raw_items[id]) 28 | self._items[id] = existed_item 29 | return existed_item 30 | 31 | # should implement python dictionary interface 32 | def get(self, key): 33 | if key in self: 34 | return self.__getitem__(key) 35 | 36 | def __len__(self): 37 | return len(self._raw_items) 38 | 39 | def __iter__(self): 40 | return iter(self._raw_items) 41 | 42 | def get_keys(self): 43 | return self._raw_items.keys() 44 | 45 | def keys(self): 46 | return self._raw_items.keys() 47 | 48 | def update_data(self, items): 49 | self._init(items) 50 | 51 | def update_item(self, key, item): 52 | if key in self._items: 53 | del self._items[key] 54 | self._raw_items[key] = item 55 | self._get_or_create_item(key) 56 | 57 | def remove_item(self, key): 58 | if key in self._items: 59 | del self._items[key] 60 | if key in self._raw_items: 61 | del self._raw_items[key] 62 | -------------------------------------------------------------------------------- /core/descriptions/smart_updatable_descriptions_items.py: -------------------------------------------------------------------------------- 1 | from core.descriptions.descriptions_items import DescriptionsItems 2 | 3 | 4 | class SmartUpdatableDescriptionsItems(DescriptionsItems): 5 | 6 | def update_data(self, items): 7 | for item_id in items.keys(): 8 | existed_item = self._items.get(item_id) 9 | if existed_item: 10 | if existed_item.version != items[item_id].get("version", -1) or \ 11 | items[item_id].get("force_update"): 12 | del self._items[item_id] 13 | redundant = set(self._items.keys()) - set(items.keys()) 14 | for key in redundant: 15 | del self._items[key] 16 | self._raw_items = items 17 | -------------------------------------------------------------------------------- /core/logging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/logging/__init__.py -------------------------------------------------------------------------------- /core/logging/logger_constants.py: -------------------------------------------------------------------------------- 1 | KEY_NAME = "key_name" 2 | SCENARIO_VALUE = "scenario" 3 | REQUIREMENT_CHECK_VALUE = "requirement_check" 4 | EXCEPTION_VALUE = "exception" 5 | HANDLED_EXCEPTION_VALUE = "handled_exception" 6 | IGNITE_VALUE = "ignite" 7 | MEMCACHED_VALUE = "memcached" 8 | TWISTED_SERVER = "twisted" 9 | REPOSITORY_LOAD_VALUE = "repository_load" 10 | REPOSITORY_CLEAR_VALUE = "repository_clear" 11 | KAFKA_ON_ASSIGN_VALUE = "kafka_on_assign" 12 | IR_STARTUP_VALUE = "intent_recognizer_startup" 13 | INTENT_ID_VALUE = "intent_id" 14 | REQUEST_VALUE = "request" 15 | -------------------------------------------------------------------------------- /core/logging/logger_formatter.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from datetime import datetime 3 | from pythonjsonlogger import jsonlogger 4 | 5 | from core.model.factory import build_factory 6 | from core.model.registered import Registered 7 | 8 | loggers_formatter = Registered() 9 | 10 | loggers_formatter_factory = build_factory(loggers_formatter) 11 | 12 | 13 | class BaseJsonFormatter(jsonlogger.JsonFormatter): 14 | VERSION = 0 15 | DEV_TEAM = "NA" 16 | APPLICATION_NAME = "NA" 17 | 18 | def add_fields(self, log_record, record, message_dict): 19 | super(BaseJsonFormatter, self).add_fields(log_record, record, message_dict) 20 | dt = datetime.fromtimestamp(record.created) 21 | st = dt.strftime("%Y-%m-%dT%H:%M:%S") 22 | log_record['timestamp'] = "%s.%06d" % (st, record.msecs * 1000) 23 | log_record['version'] = self.VERSION 24 | log_record['team'] = self.DEV_TEAM 25 | log_record['application'] = self.APPLICATION_NAME 26 | if isinstance(record.args, dict): 27 | log_record['args'] = record.args 28 | -------------------------------------------------------------------------------- /core/logging/logger_handlers.py: -------------------------------------------------------------------------------- 1 | import os 2 | from logging.handlers import RotatingFileHandler 3 | 4 | 5 | class RotatingFilePidHandler(RotatingFileHandler): 6 | 7 | def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False): 8 | pid = os.getpid() 9 | filename = f"{filename}.{pid}" 10 | super(RotatingFilePidHandler, self).__init__(filename, mode, maxBytes, backupCount, encoding, delay) 11 | -------------------------------------------------------------------------------- /core/message/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/message/__init__.py -------------------------------------------------------------------------------- /core/message/app_info.py: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | from core.names import field 4 | 5 | 6 | class AppInfo: 7 | 8 | def __init__(self, value): 9 | self._value = value 10 | 11 | @lazy 12 | def project_id(self): 13 | return self._value.get(field.PROJECT_ID) 14 | 15 | @lazy 16 | def system_name(self): 17 | return self._value.get(field.SYSTEM_NAME) 18 | 19 | @lazy 20 | def application_id(self): 21 | return self._value.get(field.APPLICATION_ID) 22 | 23 | @lazy 24 | def app_version_id(self): 25 | return self._value.get(field.APP_VERSION_ID) 26 | 27 | @lazy 28 | def frontend_endpoint(self): 29 | return self._value.get(field.FRONTEND_ENDPOINT) 30 | 31 | @lazy 32 | def frontend_type(self): 33 | return self._value.get(field.FRONTEND_TYPE) 34 | -------------------------------------------------------------------------------- /core/message/device.py: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | 4 | class Device: 5 | 6 | def __init__(self, value): 7 | self._value = value 8 | 9 | @lazy 10 | def value(self): 11 | return self._value 12 | 13 | @lazy 14 | def platform_type(self): 15 | return self._value.get("platformType") or "" 16 | 17 | @lazy 18 | def platform_version(self): 19 | return self._value.get("platformVersion") or "" 20 | 21 | @lazy 22 | def surface(self): 23 | return self._value.get("surface") or "" 24 | 25 | @lazy 26 | def surface_version(self): 27 | return self._value.get("surfaceVersion") or "" 28 | 29 | @lazy 30 | def features(self): 31 | return self._value.get("features") or {} 32 | 33 | @lazy 34 | def capabilities(self): 35 | return self._value.get("capabilities") or {} 36 | 37 | @lazy 38 | def additional_info(self): 39 | return self._value.get("additionalInfo") or {} 40 | 41 | @lazy 42 | def tenant(self): 43 | return self._value.get("tenant") or "" 44 | 45 | @lazy 46 | def device_id(self): 47 | return self._value.get("deviceId") or "" 48 | -------------------------------------------------------------------------------- /core/message/message_constants.py: -------------------------------------------------------------------------------- 1 | MSG_USERID_KEY = "userId" 2 | MSG_CHATID_KEY = "chatId" 3 | MSG_MESSAGEID_KEY = "messageId" 4 | MSG_USERCHANNEL_KEY = "userChannel" 5 | -------------------------------------------------------------------------------- /core/message/msg_validator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class MessageValidator(ABC): 5 | @abstractmethod 6 | def validate(self, message_name: str, payload: dict): 7 | raise NotImplemented() 8 | -------------------------------------------------------------------------------- /core/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/model/__init__.py -------------------------------------------------------------------------------- /core/model/field.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class Field: 5 | def __init__(self, name, model, description=None, *args): 6 | self.name = name 7 | self.model = model 8 | self.description = description 9 | self.args = args 10 | -------------------------------------------------------------------------------- /core/model/heapq/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/model/heapq/__init__.py -------------------------------------------------------------------------------- /core/model/heapq/heapq_storage.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | 3 | 4 | class HeapqKV: 5 | def __init__(self, value_to_key_func): 6 | self._heapq = [] 7 | self._to_remove = set() 8 | self._value_to_key = value_to_key_func 9 | 10 | def value_to_key(self, value): 11 | return self._value_to_key(value) 12 | 13 | def push(self, key, value): 14 | heapq.heappush(self._heapq, (key, value)) 15 | 16 | def get_head_key(self): 17 | while self._heapq: 18 | top = self._heapq[0] 19 | if self._check_key_valid(top[1]): 20 | return top[0] 21 | else: 22 | heapq.heappop(self._heapq) 23 | 24 | def pop(self): 25 | while self._heapq: 26 | top = self._heapq[0] 27 | if self._check_key_valid(top[1]): 28 | return heapq.heappop(self._heapq) 29 | else: 30 | heapq.heappop(self._heapq) 31 | 32 | def remove(self, key): 33 | self._to_remove.add(key) 34 | 35 | def _check_key_valid(self, value): 36 | key = self.value_to_key(value) 37 | if key in self._to_remove: 38 | self._to_remove.remove(key) 39 | return False 40 | return True 41 | -------------------------------------------------------------------------------- /core/model/model.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class Model: 5 | @property 6 | def fields(self): 7 | return [] 8 | 9 | def __init__(self, values, user): 10 | values = values or {} 11 | 12 | for field in self.fields: 13 | value = values.get(field.name) 14 | description = field.description 15 | args = field.args 16 | if description is not None: 17 | obj = field.model(value, description, user, *args) 18 | else: 19 | obj = field.model(value, user, *args) 20 | setattr(self, field.name, obj) 21 | 22 | def get_field(self, name): 23 | return getattr(self, name) 24 | 25 | @property 26 | def raw(self): 27 | result = {} 28 | for field in self.fields: 29 | value = getattr(self, field.name) 30 | raw = value.raw 31 | if raw is not None: 32 | result[field.name] = raw 33 | return result 34 | -------------------------------------------------------------------------------- /core/model/queued_objects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/model/queued_objects/__init__.py -------------------------------------------------------------------------------- /core/model/queued_objects/limited_queued_hashable_objects.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from collections import deque 3 | 4 | from core.model.lazy_items import LazyItems 5 | 6 | 7 | class LimitedQueuedHashableObjects: 8 | def __init__(self, items, description, user=None): 9 | self.description = description 10 | self.queue = deque(items or [], maxlen=description.max_len) 11 | 12 | def get_list(self): 13 | return list(self.queue) 14 | 15 | def add(self, id): 16 | self.queue.append(id) 17 | 18 | def clear(self): 19 | self.queue.clear() 20 | 21 | def check(self, id): 22 | duplicated = id in self.queue 23 | if not duplicated: 24 | self.add(id) 25 | return duplicated 26 | 27 | @property 28 | def raw(self): 29 | return self.get_list() 30 | 31 | 32 | class LimitedQueuedHashableObjectsItems(LazyItems): 33 | def __init__(self, items, descriptions, user): 34 | super(LimitedQueuedHashableObjectsItems, self).__init__(items, descriptions, user, LimitedQueuedHashableObjects) 35 | -------------------------------------------------------------------------------- /core/model/queued_objects/limited_queued_hashable_objects_description.py: -------------------------------------------------------------------------------- 1 | from core.descriptions.descriptions_items import DescriptionsItems 2 | 3 | 4 | class LimitedQueuedHashableObjectsDescription: 5 | DEFAULT_MAX_LEN = 10 6 | 7 | def __init__(self, items, id=None): 8 | self.id = id 9 | self.items = items or {} 10 | self.max_len = self.items.get("max_len", self.DEFAULT_MAX_LEN) 11 | 12 | 13 | class LimitedQueuedHashableObjectsDescriptionsItems(DescriptionsItems): 14 | def __init__(self, items): 15 | super(LimitedQueuedHashableObjectsDescriptionsItems, self).__init__(LimitedQueuedHashableObjectsDescription, items) 16 | -------------------------------------------------------------------------------- /core/model/registered.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class Registered(dict): 5 | 6 | def __getitem__(self, key): 7 | value = self.get(key, key) 8 | assert not isinstance(value, str), "{} factory is not registered".format(key) 9 | return value 10 | 11 | 12 | registered_factories = Registered() 13 | -------------------------------------------------------------------------------- /core/monitoring/__init__.py: -------------------------------------------------------------------------------- 1 | from core.monitoring import monitoring 2 | 3 | 4 | def init_monitoring(monitoring_class): 5 | if not isinstance(monitoring.monitoring, monitoring_class): 6 | monitoring.monitoring = monitoring_class() 7 | -------------------------------------------------------------------------------- /core/monitoring/twisted_server.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import reactor 2 | from twisted.web import server 3 | from core.logging.logger_utils import log 4 | import core.logging.logger_constants as log_const 5 | 6 | 7 | class TwistedServer: 8 | def __init__(self, port, interface, handler, debug=False): 9 | log("TwistedServer.__init__ started.", params={log_const.KEY_NAME: log_const.TWISTED_SERVER}, 10 | level="WARNING") 11 | site = server.Site(handler(debug=debug)) 12 | reactor.listenTCP(port, site, interface=interface or "") 13 | reactor.startRunning(False) 14 | log("TwistedServer.__init__ finished.", params={log_const.KEY_NAME: log_const.TWISTED_SERVER}, 15 | level="WARNING") 16 | 17 | def iterate(self): 18 | reactor.iterate() 19 | -------------------------------------------------------------------------------- /core/mq/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/mq/__init__.py -------------------------------------------------------------------------------- /core/mq/kafka/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/mq/kafka/__init__.py -------------------------------------------------------------------------------- /core/mq/kafka/base_kafka_consumer.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class BaseKafkaConsumer: 5 | def subscribe(self, topics=None): 6 | raise NotImplementedError 7 | 8 | def poll(self): 9 | raise NotImplementedError 10 | 11 | def consume(self, num_messages=1): 12 | raise NotImplementedError 13 | 14 | def close(self): 15 | raise NotImplementedError 16 | -------------------------------------------------------------------------------- /core/mq/kafka/base_kafka_publisher.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class BaseKafkaPublisher: 5 | def send(self, message, uid, topic): 6 | raise NotImplementedError 7 | 8 | def close(self): 9 | raise NotImplementedError 10 | -------------------------------------------------------------------------------- /core/names/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/names/__init__.py -------------------------------------------------------------------------------- /core/names/field.py: -------------------------------------------------------------------------------- 1 | ANNOTATIONS = "annotations" 2 | APPLICATION_ID = "applicationId" 3 | APP_INFO = "app_info" 4 | APP_VERSION_ID = "appversionId" 5 | AUTO_LISTENING = "auto_listening" 6 | CLASSES = 'classes' 7 | DEVICE = "device" 8 | FINISHED = "finished" 9 | FRONTEND_ENDPOINT = "frontendEndpoint" 10 | FRONTEND_TYPE = "frontendType" 11 | INTENT = "intent" 12 | INTENT_META = "intent_meta" 13 | ITEMS = "items" 14 | MESSAGE_NAME = "messageName" 15 | PROBAS = 'probas' 16 | PROJECT_ID = "projectId" 17 | PROJECT_NAME = "projectName" 18 | PRONOUNCE_TEXT = "pronounceText" 19 | SERVER_ACTION = "server_action" 20 | SMART_BIO = "smartBio" 21 | SUB = "sub" 22 | SYSTEM_NAME = "systemName" 23 | USER_CHANNEL = "userChannel" 24 | USER_ID = "userId" 25 | 26 | DEBUG_INFO = "debug_info" 27 | DEBUG_INFO_APP_KEY = "debug_info_key" 28 | EVENTS = "events" 29 | HISTORY_DATA = "historyData" 30 | -------------------------------------------------------------------------------- /core/repositories/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/repositories/__init__.py -------------------------------------------------------------------------------- /core/repositories/base_repository.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from core.db_adapter.os_adapter import OSAdapter 4 | from core.logging.logger_utils import log 5 | import core.logging.logger_constants as log_const 6 | 7 | 8 | class BaseRepository: 9 | def __init__(self, source=None, key=None): 10 | self.source = source or OSAdapter(None) 11 | self._data = None 12 | self.key = key 13 | 14 | @property 15 | def data(self): 16 | return self._data 17 | 18 | @data.setter 19 | def data(self, value): 20 | self._data = value 21 | 22 | def load(self): 23 | params = { 24 | "repository_class_name": self.__class__.__name__, 25 | "repository_key": self.key, 26 | log_const.KEY_NAME: log_const.REPOSITORY_LOAD_VALUE 27 | } 28 | log("%(repository_class_name)s.load %(repository_key)s repo loading completed.", params=params, 29 | level="WARNING") 30 | 31 | def fill(self, data): 32 | self.data = data 33 | 34 | def clear(self): 35 | self.data.clear() 36 | log("%(repository_class_name)s.clear %(repository_key)s cleared.", 37 | params={"repository_class_name": self.__class__.__name__, 38 | "repository_key": self.key, 39 | log_const.KEY_NAME: log_const.REPOSITORY_CLEAR_VALUE}, 40 | level="WARNING") 41 | 42 | def save(self, save_parameters): 43 | raise NotImplementedError 44 | 45 | def check_load_in_parts(self): 46 | return False 47 | -------------------------------------------------------------------------------- /core/repositories/csv_repository.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from core.repositories.base_repository import BaseRepository 3 | 4 | 5 | class CSVRepository(BaseRepository): 6 | def __init__(self, filename, source=None, *args, **kwargs): 7 | super(CSVRepository, self).__init__(source=source, *args, **kwargs) 8 | self.filename = filename 9 | 10 | def load(self): 11 | with self.source.open(self.filename, newline='') as stream: 12 | reader = csv.DictReader(stream) 13 | data = list(reader) 14 | self.fill(data) 15 | super(CSVRepository, self).load() 16 | -------------------------------------------------------------------------------- /core/repositories/dill_repository.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import dill 3 | 4 | import core.logging.logger_constants as log_const 5 | from core.logging.logger_utils import log 6 | from core.repositories.base_repository import BaseRepository 7 | 8 | 9 | class DillRepository(BaseRepository): 10 | """ 11 | load unpickleable file 12 | Args: 13 | filename: path to the file 14 | source: file adapter e.g OSAdapter, CephAdapter etc 15 | required: raise FileNotFoundError if file not found 16 | """ 17 | def __init__(self, filename, source=None, required=True, *args, **kwargs): 18 | super(DillRepository, self).__init__(source=source, *args, **kwargs) 19 | self.filename = filename 20 | self.required = required 21 | 22 | def load(self): 23 | dill._dill._reverse_typemap['ClassType'] = type 24 | try: 25 | 26 | with self.source.open(self.filename, 'rb') as stream: 27 | data = dill.load(stream) 28 | self.fill(data) 29 | except FileNotFoundError as error: 30 | params = { 31 | 'error': str(error), 32 | log_const.KEY_NAME: log_const.EXCEPTION_VALUE 33 | } 34 | log('DillRepository.load loading failed. Error %(error)s', params=params, level='WARNING') 35 | if self.required: 36 | raise 37 | super(DillRepository, self).load() 38 | -------------------------------------------------------------------------------- /core/repositories/items_repository.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from core.repositories.base_repository import BaseRepository 4 | 5 | 6 | class ItemsRepository(BaseRepository): 7 | def __init__(self, *args, **kwargs): 8 | super(ItemsRepository, self).__init__(*args, **kwargs) 9 | self.data = dict() 10 | 11 | @BaseRepository.data.setter 12 | def data(self, value): 13 | if value is None: 14 | self._data = dict() 15 | else: 16 | self._data = value 17 | 18 | def load(self): 19 | super(ItemsRepository, self).load() 20 | 21 | def __iter__(self): 22 | return iter(self.data) 23 | 24 | def __len__(self): 25 | return len(self.data) 26 | -------------------------------------------------------------------------------- /core/repositories/shard_repository.py: -------------------------------------------------------------------------------- 1 | import os 2 | from core.repositories.base_repository import BaseRepository 3 | 4 | 5 | class ShardRepository(BaseRepository): 6 | 7 | def __init__(self, path, loader, source=None, *args, **kwargs): 8 | super(ShardRepository, self).__init__(source=source, *args, **kwargs) 9 | self.path = path 10 | self.loader = loader 11 | 12 | @staticmethod 13 | def join_path(path, file_name): 14 | path = os.path.join(path, file_name) 15 | path = os.path.normpath(path) 16 | return path 17 | 18 | @staticmethod 19 | def _get_data_type(data): 20 | t = None 21 | for k, v in data.items(): 22 | if not t: 23 | if isinstance(v, dict): 24 | t = dict 25 | elif isinstance(v, list): 26 | t = list 27 | if t and not isinstance(v, t): 28 | raise TypeError( 29 | f"There are more than one data type in folder repository: values {v} incompatible with" 30 | f" data type {t}") 31 | return t 32 | 33 | def load(self): 34 | super(ShardRepository, self).load() 35 | 36 | def fill(self, data): 37 | res = None 38 | t = self._get_data_type(data) 39 | if t == dict: 40 | res = {} 41 | for k, v in data.items(): 42 | res.update(v) 43 | elif t == list: 44 | res = [] 45 | for k, v in data.items(): 46 | res.extend(v) 47 | self.data = res 48 | 49 | def fill_on_top(self, data): 50 | t = self._get_data_type(data) 51 | for k, v in data.items(): 52 | if t == dict: 53 | self.data.update(v) 54 | elif t == list: 55 | self.data.extend(v) 56 | 57 | def save(self, save_parameters): 58 | raise NotImplementedError 59 | -------------------------------------------------------------------------------- /core/request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/request/__init__.py -------------------------------------------------------------------------------- /core/request/base_request.py: -------------------------------------------------------------------------------- 1 | from core.model.factory import build_factory 2 | from core.model.registered import Registered 3 | from core.utils.exception_handlers import exc_handler 4 | import timeout_decorator 5 | 6 | 7 | requests_registered = Registered() 8 | request_factory = build_factory(requests_registered) 9 | 10 | 11 | class BaseRequest: 12 | BLOCKING_TIMEOUT = 0.2 13 | DEFAULT_ENABLED = True 14 | 15 | def __init__(self, items, id=None): 16 | self.values = items 17 | self.timeout = items.get("timeout") 18 | self._timeout_wrap = timeout_decorator.timeout(self.timeout or self.BLOCKING_TIMEOUT) 19 | self._enabled = items.get("enabled", self.DEFAULT_ENABLED) if items is not None else self.DEFAULT_ENABLED 20 | 21 | @property 22 | def group_key(self): 23 | return None 24 | 25 | def check_enabled(self): 26 | return self._enabled 27 | 28 | def update_empty_items(self, items): 29 | pass 30 | 31 | @exc_handler() 32 | def run(self, data, params=None): 33 | raise NotImplementedError 34 | -------------------------------------------------------------------------------- /core/request/external_requests.py: -------------------------------------------------------------------------------- 1 | from core.descriptions.descriptions_items import DescriptionsItems 2 | from core.request.base_request import request_factory 3 | 4 | 5 | class ExternalRequests(DescriptionsItems): 6 | def __init__(self, items): 7 | super(ExternalRequests, self).__init__(request_factory, items) 8 | -------------------------------------------------------------------------------- /core/request/kafka_request.py: -------------------------------------------------------------------------------- 1 | from core.request.base_request import BaseRequest 2 | 3 | 4 | class KafkaRequest(BaseRequest): 5 | TOPIC_KEY = "topic_key" 6 | KAFKA_KEY = "kafka_key" 7 | 8 | def __init__(self, items, id=None): 9 | super(KafkaRequest, self).__init__(items) 10 | items = items or {} 11 | self.topic_key = items.get(self.TOPIC_KEY) 12 | self.kafka_key = items.get(self.KAFKA_KEY) 13 | 14 | def update_empty_items(self, items): 15 | self.topic_key = self.topic_key or items["topic_key"] 16 | self.kafka_key = self.kafka_key or items["kafka_key"] 17 | 18 | @property 19 | def group_key(self): 20 | return "{}_{}".format(self.kafka_key, self.topic_key) if (self.topic_key and self.kafka_key) else None 21 | 22 | def _get_new_headers(self, source_mq_message): 23 | headers = source_mq_message.headers() or [] 24 | return headers 25 | 26 | def run(self, data, params=None): 27 | publishers = params["publishers"] 28 | publisher = publishers[self.kafka_key] 29 | source_mq_message = params["mq_message"] 30 | publisher.send(data, source_mq_message.key(), self.topic_key, headers=self._get_new_headers(source_mq_message)) 31 | 32 | def __str__(self): 33 | return f"KafkaRequest: topic_key={self.topic_key} kafka_key={self.kafka_key}" 34 | -------------------------------------------------------------------------------- /core/text_preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/text_preprocessing/__init__.py -------------------------------------------------------------------------------- /core/text_preprocessing/constants.py: -------------------------------------------------------------------------------- 1 | NUM_TOKEN = "NUM_TOKEN" 2 | TWO_NUM_TOKENS = "{} {}".format(NUM_TOKEN, NUM_TOKEN) 3 | CCY_TOKEN = "CCY_TOKEN" 4 | PHONE_NUMBER_TOKEN = "PHONE_NUMBER_TOKEN" 5 | TIME_DATE_TOKEN = "TIME_DATE_TOKEN" 6 | TIME_YEAR_TOKEN = "TIME_YEAR_TOKEN" 7 | TIME_MONTH_TOKEN = "TIME_MONTH_TOKEN" 8 | CARD_NUMBER_TOKEN = "CARD_NUMBER_TOKEN" 9 | ANIMACY_TOKEN = "ANIMACY_TOKEN" 10 | SENTENCE_ENDPOINT_TOKEN = "SENTENCE_ENDPOINT_TOKEN" 11 | TIME_DAY_TOKEN = "TIME_DAY_TOKEN" 12 | TIME_TIME_TOKEN = "TIME_TIME_TOKEN" 13 | TIME_TEMPORAL_TOKEN = "TIME_TEMPORAL_TOKEN" 14 | NAME_TOKEN = "NAME_TOKEN" 15 | SURNAME_TOKEN = "SURNAME_TOKEN" 16 | PATRONYMIC_TOKEN = "PATRONYMIC_TOKEN" 17 | EMAIL_ADDRESS_TOKEN = "EMAIL_ADDRESS_TOKEN" 18 | URL_TOKEN = "URL_TOKEN" 19 | EMOJI_TOKEN = "EMOJI_TOKEN" 20 | GEO_TOKEN = "GEO_TOKEN" 21 | CATALOG_GEO_TOKEN = "CATALOG_GEO_TOKEN" 22 | ORG_TOKEN = "ORG_TOKEN" 23 | CATALOG_ORG_TOKEN = "CATALOG_ORG_TOKEN" 24 | THUMB_UP_TOKEN = "THUMB_UP_TOKEN" 25 | THUMB_DOWN_TOKEN = "THUMB_DOWN_TOKEN" 26 | SMS_CARDNUM_TOKEN = "SMS_CARDNUM_TOKEN" 27 | SMS_OWNER_CARDNUM_TOKEN = "SMS_OWNER_CARDNUM_TOKEN" 28 | SMS_KEYWORD_TOKEN = "SMS_KEYWORD_TOKEN" 29 | SMS_ALIAS_TOKEN = "SMS_ALIAS_TOKEN" 30 | PERSON_TOKEN = "PERSON_TOKEN" 31 | TIME_DATE_INTERVAL_TOKEN = "TIME_DATE_INTERVAL_TOKEN" 32 | MONEY_TOKEN = "MONEY_TOKEN" 33 | PERIOD_TOKEN = "PERIOD_TOKEN" 34 | RELATIVE_TIME_TOKEN = "RELATIVE_TIME_TOKEN" 35 | -------------------------------------------------------------------------------- /core/text_preprocessing/grammem/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/text_preprocessing/grammem/__init__.py -------------------------------------------------------------------------------- /core/unified_template/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/unified_template/__init__.py -------------------------------------------------------------------------------- /core/unified_template/pan2service.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | def pan2service(pan: str) -> Optional[str]: 4 | 5 | if len(pan) < 4: 6 | return None 7 | 8 | first_digit = int(pan[0:2]) 9 | 10 | if first_digit == 50 or first_digit in range(56, 69): 11 | return "maestro" 12 | if first_digit in range(51, 55): 13 | return "mastercard" 14 | if first_digit == 37: 15 | return "amex" 16 | 17 | first_digit = int(pan[0]) 18 | if first_digit == 4: 19 | return "visa" 20 | if first_digit == 2: 21 | return "mir" 22 | 23 | return None 24 | -------------------------------------------------------------------------------- /core/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/core/utils/__init__.py -------------------------------------------------------------------------------- /core/utils/delay_runner.py: -------------------------------------------------------------------------------- 1 | import random 2 | from time import time 3 | 4 | 5 | class DelayRunner: 6 | def __init__(self, max_delay_minutes): 7 | self.max_update_delay = max_delay_minutes * 60 8 | self._ts = 0 9 | self._run_item = None 10 | self._run_args = None 11 | 12 | def schedule_run(self, run_item, run_args): 13 | self._ts = time() + random.randint(0, self.max_update_delay) 14 | self._run_item = run_item 15 | self._run_args = run_args 16 | 17 | def check_can_run(self): 18 | return self._ts != 0 and time() >= self._ts 19 | 20 | def run(self): 21 | self._run_item(*self._run_args) 22 | self._clean() 23 | 24 | def _clean(self): 25 | self._ts = 0 26 | self._run_item = None 27 | self._run_args = None 28 | 29 | -------------------------------------------------------------------------------- /core/utils/exception_handlers.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from functools import wraps 3 | 4 | 5 | def exc_handler(on_error_obj_method_name=None, handled_exceptions=None): 6 | handled_exceptions = tuple(handled_exceptions) if handled_exceptions else (Exception,) 7 | 8 | def exc_handler_decorator(funct): 9 | @wraps(funct) 10 | def _wrapper(obj, *args, **kwarg): 11 | result = None 12 | try: 13 | result = funct(obj, *args, **kwarg) 14 | except handled_exceptions: 15 | try: 16 | on_error = getattr(obj, on_error_obj_method_name) if \ 17 | on_error_obj_method_name else (lambda *x: None) 18 | result = on_error(*args, **kwarg) 19 | except: 20 | print(sys.exc_info()) 21 | return result 22 | 23 | return _wrapper 24 | 25 | return exc_handler_decorator 26 | -------------------------------------------------------------------------------- /core/utils/fallback.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from typing import Optional 3 | 4 | 5 | def fallback_if_none(fallback_result_value=None, fallback_result_attr: Optional[str] = None): 6 | """ Parametrized function decorator that returns value if original function has returned None. 7 | Fallback value could be specified explicitly or specified by attribute name (for class methods). 8 | If fallback value cannot be resolved - an exception is thrown. 9 | 10 | :param fallback_result_value: Explicit fallback value to return 11 | :param fallback_result_attr: Name of the attribute of the object that method assigned to 12 | :return: decorator 13 | """ 14 | def _decorator(fn): 15 | @functools.wraps(fn) 16 | def _wrapper(*args, **kwargs): 17 | result = fn(*args, **kwargs) 18 | if result is not None: 19 | return result 20 | if fallback_result_value is not None: 21 | return fallback_result_value 22 | if fallback_result_attr is not None: 23 | fallback_result = getattr(args[0], fallback_result_attr) 24 | if fallback_result is not None: 25 | return fallback_result 26 | raise ValueError(f'Function {fn.__module__}:{fn.__name__} has returned None, ' 27 | f'but fallback value or fallback attribute is not specified. Check decorator usage') 28 | return _wrapper 29 | return _decorator 30 | -------------------------------------------------------------------------------- /core/utils/loader.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from collections import OrderedDict 3 | 4 | import json 5 | 6 | 7 | def ordered_json(data): 8 | return json.loads(data, object_pairs_hook=OrderedDict) 9 | 10 | 11 | def reverse_json_dict(data): 12 | data = json.loads(data) 13 | result = dict() 14 | for key, values in data.items(): 15 | for value in values: 16 | result[value] = key 17 | return result 18 | -------------------------------------------------------------------------------- /core/utils/memstats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import objgraph 3 | import psutil 4 | import time 5 | 6 | 7 | def get_meminfo(): 8 | p = psutil.Process(os.getpid()) 9 | return str(p.memory_info()) 10 | 11 | 12 | def show_growth(file=None): 13 | objgraph.show_growth(file=file, shortnames=False) 14 | 15 | 16 | def show_most_common_types(file=None, limit=20): 17 | objgraph.show_most_common_types(limit=limit, shortnames=False, file=file) 18 | 19 | 20 | def get_leaking_objects(file=None, limit=5): 21 | roots = objgraph.get_leaking_objects() 22 | objgraph.show_refs(roots[:limit], refcounts=True, shortnames=False, output=file) 23 | 24 | 25 | if __name__ == "__main__": 26 | while 1 : 27 | print(show_most_common_types()) 28 | time.sleep(1) 29 | -------------------------------------------------------------------------------- /core/utils/pickle_copy.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | 4 | def pickle_deepcopy(obj): 5 | return pickle.loads(pickle.dumps(obj, -1)) 6 | -------------------------------------------------------------------------------- /core/utils/rerunable.py: -------------------------------------------------------------------------------- 1 | import core.logging.logger_constants as log_const 2 | from core.logging.logger_utils import log 3 | from core.monitoring.monitoring import monitoring 4 | 5 | 6 | class Rerunable(): 7 | DEFAULT_RERUNABLE_TRY_COUNT = 5 8 | 9 | def __init__(self, config=None): 10 | self.config = config or {} 11 | self.try_count = self.config.get("try_count") or self.DEFAULT_RERUNABLE_TRY_COUNT 12 | 13 | @property 14 | def _handled_exception(self): 15 | raise NotImplementedError 16 | 17 | def _on_prepare(self): 18 | raise NotImplementedError 19 | 20 | def _on_all_tries_fail(self): 21 | raise NotImplementedError 22 | 23 | def _run(self, action, *args, _try_count=None, **kwargs): 24 | if _try_count is None: 25 | _try_count = self.try_count 26 | if _try_count <= 0: 27 | self._on_all_tries_fail() 28 | _try_count = _try_count - 1 29 | try: 30 | result = action(*args, **kwargs) 31 | except self._handled_exception as e: 32 | params = { 33 | "class_name": str(self.__class__), 34 | "exception": str(e), 35 | "try_count": _try_count, 36 | log_const.KEY_NAME: log_const.HANDLED_EXCEPTION_VALUE 37 | } 38 | log("%(class_name)s run failed with %(exception)s.\n Got %(try_count)s tries left.", 39 | params=params, 40 | level="ERROR") 41 | self._on_prepare() 42 | result = self._run(action, *args, _try_count=_try_count, **kwargs) 43 | counter_name = self._get_counter_name() 44 | if counter_name: 45 | monitoring.got_counter(f"{counter_name}_exception") 46 | return result 47 | 48 | def _get_counter_name(self): 49 | return 50 | -------------------------------------------------------------------------------- /core/utils/singleton.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Singleton(type): 4 | _instances = {} 5 | def __call__(cls, *args, **kwargs): 6 | if cls not in cls._instances: 7 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 8 | return cls._instances[cls] 9 | -------------------------------------------------------------------------------- /core/utils/stats_timer.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import timeit 3 | 4 | 5 | class StatsTimer: 6 | def __enter__(self): 7 | self.start = timeit.default_timer() 8 | return self 9 | 10 | def __exit__(self, *args): 11 | self.end = timeit.default_timer() 12 | self.secs = self.end - self.start 13 | self.msecs = self.secs * 1000 14 | -------------------------------------------------------------------------------- /scenarios/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/__init__.py -------------------------------------------------------------------------------- /scenarios/actions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/actions/__init__.py -------------------------------------------------------------------------------- /scenarios/actions/action_params_names.py: -------------------------------------------------------------------------------- 1 | TO_MESSAGE_PARAMS = "to_message_params" 2 | TO_MESSAGE_NAME = "to_message_name" 3 | SAVED_MESSAGES = "saved_messages" 4 | REQUEST_FIELD = "request_field" 5 | LOCAL_VARS = "local_vars" 6 | -------------------------------------------------------------------------------- /scenarios/behaviors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/behaviors/__init__.py -------------------------------------------------------------------------------- /scenarios/behaviors/behavior_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import time 3 | 4 | from lazy import lazy 5 | 6 | from core.basic_models.actions.basic_actions import Action 7 | from core.model.factory import factory 8 | 9 | 10 | class BehaviorDescription: 11 | def __init__(self, items, id=None): 12 | self.id = id 13 | self._success_action = items["success_action"] 14 | self._fail_action = items.get("fail_action") 15 | self._misstate = items.get("misstate") 16 | self._timeout_action = items.get("timeout_action") 17 | self._timeout = items.get("timeout", 300) 18 | self.version = items.get("version", -1) 19 | self.loop_def = items.get("loop_def", True) 20 | 21 | def get_expire_time_from_now(self, user): 22 | return time.time() + self.timeout(user) 23 | 24 | def timeout(self, user): 25 | setting_timeout = user.settings["template_settings"].get("services_timeout", {}).get(self.id) 26 | return setting_timeout or self._timeout 27 | 28 | @lazy 29 | @factory(Action) 30 | def success_action(self): 31 | return self._success_action 32 | 33 | @lazy 34 | @factory(Action) 35 | def fail_action(self): 36 | return self._fail_action 37 | 38 | @lazy 39 | @factory(Action) 40 | def misstate(self): 41 | return self._misstate 42 | 43 | @lazy 44 | @factory(Action) 45 | def timeout_action(self): 46 | return self._timeout_action 47 | -------------------------------------------------------------------------------- /scenarios/behaviors/behavior_descriptions.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 3 | from scenarios.behaviors.behavior_description import BehaviorDescription 4 | 5 | 6 | class BehaviorDescriptions(SmartUpdatableDescriptionsItems): 7 | def __init__(self, items): 8 | super(BehaviorDescriptions, self).__init__(BehaviorDescription, items) 9 | -------------------------------------------------------------------------------- /scenarios/logging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/logging/__init__.py -------------------------------------------------------------------------------- /scenarios/requirements/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/requirements/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_descriptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_descriptions/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_descriptions/scenarios_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from core.basic_models.scenarios.base_scenario import scenario_factory 3 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 4 | 5 | 6 | class ScenariosDescriptions(SmartUpdatableDescriptionsItems): 7 | def __init__(self, items): 8 | super(ScenariosDescriptions, self).__init__(scenario_factory, items, ordered=True) 9 | -------------------------------------------------------------------------------- /scenarios/scenario_descriptions/tree_scenario/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_descriptions/tree_scenario/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_descriptions/tree_scenario/tree_scenario_node.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from lazy import lazy 3 | 4 | from core.basic_models.actions.basic_actions import Action 5 | from core.basic_models.requirement.basic_requirements import Requirement 6 | from core.model.factory import factory, list_factory 7 | 8 | 9 | class TreeScenarioNode: 10 | def __init__(self, items, id): 11 | items = items or {} 12 | self.id = id 13 | self.form_key = items["form_key"] 14 | self._requirement = items.get("requirement") 15 | self._actions = items.get("actions") 16 | self.available_nodes = items.get("available_nodes") 17 | 18 | @lazy 19 | @factory(Requirement) 20 | def requirement(self): 21 | return self._requirement 22 | 23 | @lazy 24 | @list_factory(Action) 25 | def actions(self): 26 | return self._actions 27 | -------------------------------------------------------------------------------- /scenarios/scenario_models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_models/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_models/field/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_models/field/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_models/field/external_field_filler_descriptions.py: -------------------------------------------------------------------------------- 1 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 2 | from scenarios.scenario_models.field.field_filler_description import field_filler_factory 3 | 4 | 5 | class ExternalFieldFillerDescriptions(SmartUpdatableDescriptionsItems): 6 | def __init__(self, items): 7 | super(ExternalFieldFillerDescriptions, self).__init__(field_filler_factory, items, ordered=True) 8 | -------------------------------------------------------------------------------- /scenarios/scenario_models/field/field_descriptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_models/field/field_descriptions/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_models/field/field_descriptions/composite_field_description.py: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | from core.model.factory import dict_factory, factory 4 | from scenarios.scenario_models.field.field_filler_description import FieldFillerDescription 5 | from scenarios.scenario_models.field.field_descriptions.basic_field_description import BasicFieldDescription 6 | 7 | 8 | class CompositeFieldDescription(BasicFieldDescription): 9 | 10 | def __init__(self, items, id): 11 | super(CompositeFieldDescription, self).__init__(items, id) 12 | self._fields = items["fields"] 13 | 14 | @lazy 15 | @factory(FieldFillerDescription) 16 | def filler(self): 17 | return self._filler 18 | 19 | @lazy 20 | @dict_factory(BasicFieldDescription) 21 | def fields(self): 22 | return self._fields 23 | -------------------------------------------------------------------------------- /scenarios/scenario_models/field/field_descriptions/integration_field_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from lazy import lazy 3 | 4 | from core.basic_models.requirement.basic_requirements import Requirement 5 | from core.model.factory import factory 6 | 7 | from scenarios.scenario_models.field.field_descriptions.basic_field_description import BasicFieldDescription 8 | 9 | 10 | class IntegrationFieldDescription(BasicFieldDescription): 11 | 12 | def __init__(self, items, id): 13 | super(IntegrationFieldDescription, self).__init__(items, id) 14 | self._retry_count = items.get("retry_count", 0) 15 | -------------------------------------------------------------------------------- /scenarios/scenario_models/field/field_descriptions/question_field_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from lazy import lazy 3 | 4 | from core.basic_models.actions.basic_actions import Action 5 | from core.model.factory import list_factory 6 | 7 | from scenarios.scenario_models.field.field_descriptions.basic_field_description import BasicFieldDescription 8 | 9 | 10 | class QuestionFieldDescription(BasicFieldDescription): 11 | DEFAULT_AVAILABLE_VALUE = True 12 | 13 | def __init__(self, items, id): 14 | super(QuestionFieldDescription, self).__init__(items, id) 15 | self._requests = items.get("questions", []) 16 | self._on_filled_actions = items.get("on_filled_actions", []) 17 | self._ask_again_requests = items.get("ask_again_questions", []) 18 | 19 | @lazy 20 | @list_factory(Action) 21 | def ask_again_requests(self): 22 | return self._ask_again_requests 23 | -------------------------------------------------------------------------------- /scenarios/scenario_models/field/fields.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from core.model.lazy_items import LazyItems 3 | 4 | 5 | class Fields(LazyItems): 6 | def __init__(self, items, descriptions, user, factory, lifetime): 7 | self._lifetime = lifetime 8 | super(Fields, self).__init__(items, descriptions, user, factory) 9 | 10 | def _build_factory(self, description, raw_data): 11 | return self._factory(description, raw_data or {}, self._user, self._lifetime) 12 | 13 | @property 14 | def values(self): 15 | result = dict() 16 | for key in self: 17 | item = self[key] 18 | result[item.description.id] = item.value 19 | return result 20 | -------------------------------------------------------------------------------- /scenarios/scenario_models/field_requirements/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_models/field_requirements/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_models/field_validator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_models/field_validator/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_models/field_validator/field_validator.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from core.basic_models.actions.basic_actions import Action 4 | from core.model.factory import factory, list_factory 5 | from scenarios.scenario_models.field_requirements.field_requirements import FieldRequirement 6 | 7 | 8 | class FieldValidator: 9 | def __init__(self, items): 10 | self._requirement = items.get("requirement") 11 | self._actions = items.get("actions") 12 | 13 | self.requirement = self.build_requirement() 14 | self.actions = self.build_actions() 15 | 16 | @factory(FieldRequirement) 17 | def build_requirement(self): 18 | return self._requirement 19 | 20 | @list_factory(Action) 21 | def build_actions(self): 22 | return self._actions 23 | -------------------------------------------------------------------------------- /scenarios/scenario_models/forms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/scenario_models/forms/__init__.py -------------------------------------------------------------------------------- /scenarios/scenario_models/forms/composite_forms.py: -------------------------------------------------------------------------------- 1 | from scenarios.scenario_models.forms.form import BaseForm 2 | from scenarios.scenario_models.forms.forms import Forms 3 | 4 | 5 | class CompositeForm(BaseForm): 6 | def __init__(self, items, description, user): 7 | super(CompositeForm, self).__init__(items, description, user) 8 | items = items or {} 9 | self.forms = Forms(items.get("forms"), description.forms, user) 10 | self._valid = items.get("valid") or self.description.valid 11 | 12 | def is_valid(self): 13 | return self._valid 14 | 15 | def set_valid(self): 16 | self._valid = True 17 | 18 | def get_fields_values(self): 19 | data = {} 20 | for form in self.description.forms.keys(): 21 | inner_form = self.forms[form] 22 | fields = inner_form.get_fields_values() if inner_form else {form: {}} 23 | data.update(fields) 24 | return {self.description.id: data} 25 | 26 | @property 27 | def raw(self): 28 | raw = {"valid": self._valid} 29 | if self.forms.raw: 30 | raw["forms"] = self.forms.raw 31 | if self.remove_time: 32 | raw["remove_time"] = self.remove_time 33 | return raw 34 | -------------------------------------------------------------------------------- /scenarios/scenario_models/forms/form_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from core.model.factory import ordered_dict_factory, build_factory 4 | from core.model.registered import Registered 5 | from scenarios.scenario_models.field.field_descriptions.basic_field_description import BasicFieldDescription 6 | 7 | form_descriptions = Registered() 8 | 9 | form_description_factory = build_factory(form_descriptions) 10 | 11 | 12 | class BaseFormDescription: 13 | DEFAULT_FORM_LIFETIME = 1 * 24 * 60 * 60 14 | 15 | def __init__(self, items, id): 16 | self.id = id 17 | self.valid = items.get("valid", False) 18 | self.lifetime = items.get("lifetime", self.DEFAULT_FORM_LIFETIME) 19 | self.version = items.get("version", -1) 20 | self.form_description = items.get("form_description", "") 21 | 22 | 23 | class FormDescription(BaseFormDescription): 24 | def __init__(self, items, id): 25 | super(FormDescription, self).__init__(items, id) 26 | self._fields = items["fields"] 27 | self.fields = self.build_fields() 28 | 29 | @ordered_dict_factory(BasicFieldDescription) 30 | def build_fields(self): 31 | return self._fields 32 | 33 | 34 | class CompositeFormDescription(BaseFormDescription): 35 | def __init__(self, items, id): 36 | super(CompositeFormDescription, self).__init__(items, id) 37 | self._forms = items["forms"] 38 | self.forms = self.build_forms() 39 | 40 | @ordered_dict_factory(BaseFormDescription) 41 | def build_forms(self): 42 | return self._forms 43 | -------------------------------------------------------------------------------- /scenarios/scenario_models/forms/forms_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 3 | from scenarios.scenario_models.forms.form_description import form_description_factory 4 | 5 | 6 | class FormsDescription(SmartUpdatableDescriptionsItems): 7 | def __init__(self, items): 8 | super(FormsDescription, self).__init__(form_description_factory, items) 9 | -------------------------------------------------------------------------------- /scenarios/scenario_models/history/__init__.py: -------------------------------------------------------------------------------- 1 | from .formatters import EventFormatter, HistoryEventFormatter, formatters, formatters_factory 2 | from .history import Event, History 3 | from .history_description import HistoryDescription 4 | from .constants import * 5 | -------------------------------------------------------------------------------- /scenarios/scenario_models/history/constants.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "HistoryConstants" 3 | ] 4 | 5 | 6 | class HistoryEventTypes: 7 | FIELD_EVENT = "field_event" 8 | END_SCENARIO = "end_scenario" 9 | 10 | class HistoryContentFields: 11 | FIELD = "field" 12 | 13 | 14 | class HistoryEventResults: 15 | FILLED = "filled" 16 | ASK_QUESTION = "ask_question" 17 | SUCCESS = "success" 18 | 19 | 20 | class HistoryConstants: 21 | types = HistoryEventTypes 22 | event_results = HistoryEventResults 23 | content_fields = HistoryContentFields 24 | -------------------------------------------------------------------------------- /scenarios/scenario_models/history/formatters.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, NamedTuple, Union, List, TYPE_CHECKING 2 | 3 | from core.model.factory import build_factory 4 | from core.model.registered import Registered 5 | 6 | if TYPE_CHECKING: 7 | from scenarios.scenario_models.history import Event 8 | 9 | formatters = Registered() 10 | formatters_factory = build_factory(formatters) 11 | 12 | 13 | class EventFormatter: 14 | 15 | def __init__(self, *args, **kwargs): 16 | pass 17 | 18 | def format(self, events: List['Event']) -> List[Union[NamedTuple, Dict[str, Any]]]: 19 | raise NotImplementedError 20 | 21 | 22 | class HistoryEventFormatter(EventFormatter): 23 | 24 | def format(self, events: List['Event']) -> List[Dict[str, Any]]: 25 | result = [] 26 | for no, event in enumerate(events): 27 | formatted_event = self._format_event(event) 28 | formatted_event["no"] = no + 1 29 | result.append(formatted_event) 30 | return result 31 | 32 | def _format_event(self, event: 'Event') -> Dict[str, Any]: 33 | return { 34 | "scenarioName": event.scenario, 35 | "scenarioVersion": event.scenario_version, 36 | "results": event.results, 37 | "eventType": event.type, 38 | "eventContent": event.content 39 | } 40 | -------------------------------------------------------------------------------- /scenarios/scenario_models/history/history_description.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from lazy import lazy 3 | 4 | from core.model.factory import factory 5 | from scenarios.scenario_models.history import EventFormatter 6 | 7 | 8 | class HistoryDescription: 9 | enabled: bool 10 | event_expiration_delay: Union[int, float] 11 | 12 | EVENT_EXPIRATION_DELAY = 60 13 | 14 | def __init__(self, items, id=None): 15 | self.id = id 16 | self.enabled = items.get("enabled", False) 17 | self.event_expiration_delay = items.get("event_expiration_delay", self.EVENT_EXPIRATION_DELAY) 18 | self._formatter = items.get("formatter") 19 | 20 | @lazy 21 | @factory(EventFormatter) 22 | def formatter(self) -> EventFormatter: 23 | return self._formatter 24 | -------------------------------------------------------------------------------- /scenarios/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/user/__init__.py -------------------------------------------------------------------------------- /scenarios/user/last_fields/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/user/last_fields/__init__.py -------------------------------------------------------------------------------- /scenarios/user/last_fields/last_field.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import time 3 | 4 | 5 | class LastField: 6 | def __init__(self, items): 7 | items = items or {} 8 | self.value = items.get("value") 9 | self.remove_time = items.get("remove_time") 10 | 11 | def set_remove_time(self, lifetime): 12 | if lifetime: 13 | self.remove_time = int(time.time()) + lifetime 14 | 15 | @property 16 | def raw(self): 17 | if self.value or self.remove_time: 18 | return {"value": self.value, 19 | "remove_time": self.remove_time} 20 | -------------------------------------------------------------------------------- /scenarios/user/last_fields/last_fields.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import time 3 | 4 | from scenarios.user.last_fields.last_field import LastField 5 | 6 | 7 | class LastFields: 8 | def __init__(self, items, user): 9 | self._raw_items = items or {} 10 | self._items = dict() 11 | self._factory = LastField 12 | 13 | def __getitem__(self, id): 14 | existed_item = self._items.get(id) 15 | if existed_item is None: 16 | raw_data = self._raw_items.get(id) 17 | existed_item = self._factory(raw_data) 18 | self._items[id] = existed_item 19 | return existed_item 20 | 21 | def __setitem__(self, id, value): 22 | self._items[id] = value 23 | 24 | def __iter__(self): 25 | return iter(self.raw) 26 | 27 | def expire(self): 28 | items = list(self.raw.keys()) 29 | for key in items: 30 | item = self.raw[key] 31 | remove_time = item.get("remove_time") 32 | if remove_time and remove_time <= int(time.time()): 33 | self.remove(key) 34 | 35 | def remove(self, key): 36 | if key in self._raw_items: 37 | self._raw_items.pop(key) 38 | if key in self._items: 39 | del self._items[key] 40 | 41 | def clear_all(self): 42 | self._raw_items.clear() 43 | self._items.clear() 44 | 45 | @property 46 | def raw(self): 47 | for i in self._items: 48 | item = self._items[i] 49 | value = getattr(item, "raw", item) 50 | if value: 51 | self._raw_items[i] = value 52 | else: 53 | if i in self._raw_items: 54 | self._raw_items.pop(i) 55 | return self._raw_items 56 | -------------------------------------------------------------------------------- /scenarios/user/last_scenarios/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/user/last_scenarios/__init__.py -------------------------------------------------------------------------------- /scenarios/user/last_scenarios/last_scenarios_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from lazy import lazy 3 | 4 | from core.basic_models.requirement.basic_requirements import Requirement 5 | from core.model.factory import factory 6 | 7 | 8 | class LastScenariosDescription: 9 | def __init__(self, items, id): 10 | self.id = id 11 | self._channels = items.get("channels") 12 | self._requirement = items.get("requirement") 13 | self.count = items.get("count", 1) 14 | 15 | @lazy 16 | @factory(Requirement) 17 | def requirement(self): 18 | return self._requirement 19 | 20 | def check(self, text_preprocessing_result, user): 21 | return user.message.channel in self._channels and self.requirement.check(text_preprocessing_result, user) if \ 22 | self._channels else self.requirement.check(text_preprocessing_result, user) 23 | -------------------------------------------------------------------------------- /scenarios/user/last_scenarios/last_scenarios_descriptions.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from core.descriptions.descriptions_items import DescriptionsItems 3 | from scenarios.user.last_scenarios.last_scenarios_description import LastScenariosDescription 4 | 5 | 6 | class LastScenariosDescriptionsItems(DescriptionsItems): 7 | DEFAULT_COUNT = 1 8 | 9 | def __init__(self, items): 10 | super(LastScenariosDescriptionsItems, self).__init__(LastScenariosDescription, items) 11 | 12 | def get_count(self, text_preprocessing_result, user): 13 | count = self.DEFAULT_COUNT 14 | for key in self: 15 | last_scenario_description = self[key] 16 | if last_scenario_description.check(text_preprocessing_result, user): 17 | count = last_scenario_description.count 18 | return count 19 | -------------------------------------------------------------------------------- /scenarios/user/message_history/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/user/message_history/__init__.py -------------------------------------------------------------------------------- /scenarios/user/message_history/message_history.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import time 3 | from collections import deque 4 | 5 | 6 | class MessageHistory: 7 | TS = "ts" 8 | MESSAGE = "message" 9 | DIRECTION = "direction" 10 | INCOMING = "incoming" 11 | OUTGOING = "outgoing" 12 | 13 | def __init__(self, items, description, user): 14 | items = items or [] 15 | self.items = deque(items) 16 | self.description = description 17 | 18 | def _push(self, message, direction): 19 | self._filter() 20 | stored_data = {self.TS: time.time(), 21 | self.MESSAGE: message, 22 | self.DIRECTION: direction} 23 | self.items.append(stored_data) 24 | 25 | def clear(self): 26 | self.items = deque() 27 | 28 | def _filter(self): 29 | now = time.time() 30 | while len(self.items) >= self.description.max_message_count: 31 | self.items.popleft() 32 | 33 | for _ in range(len(self.items)): 34 | threshold = self.description.lifetime + self.items[0].get(self.TS) 35 | if now > threshold: 36 | self.items.popleft() 37 | else: 38 | break 39 | 40 | @property 41 | def raw(self): 42 | return list(self.items) 43 | -------------------------------------------------------------------------------- /scenarios/user/parametrizer.py: -------------------------------------------------------------------------------- 1 | from core.basic_models.parametrizers.parametrizer import BasicParametrizer 2 | 3 | 4 | class Parametrizer(BasicParametrizer): 5 | 6 | def __init__(self, user, items): 7 | super(Parametrizer, self).__init__(user, items) 8 | 9 | def _get_scenario(self): 10 | scenario_id = self._user.last_scenarios.last_scenario_name 11 | return self._user.descriptions["scenarios"].get(scenario_id) if scenario_id else None 12 | 13 | def _get_main_form(self, forms): 14 | scenario = self._get_scenario() 15 | if scenario: 16 | return forms[scenario.form_type] 17 | return None 18 | 19 | def _get_user_data(self, text_preprocessing_result=None): 20 | tpr_data = text_preprocessing_result.raw if text_preprocessing_result else {} 21 | forms = self._user.forms.collect_form_fields() 22 | main_form = self._get_main_form(forms) 23 | data = { 24 | "counters": self._user.counters.raw, 25 | "forms": forms, 26 | "gender_sensitive_text": self._user.gender_selector.get_text_by_key, 27 | "local_vars": self._user.local_vars.values, 28 | "main_form": main_form, 29 | "message": self._user.message, 30 | "payload": self._user.message.payload, 31 | "scenario_id": self._user.last_scenarios.last_scenario_name, 32 | "text_preprocessing_result": tpr_data, 33 | "uuid": self._user.message.uuid, 34 | "variables": self._user.variables.values, 35 | "settings": self._user.settings 36 | } 37 | return data 38 | -------------------------------------------------------------------------------- /scenarios/user/preprocessing_messages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/user/preprocessing_messages/__init__.py -------------------------------------------------------------------------------- /scenarios/user/preprocessing_messages/prepricessing_messages_for_scenarios.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from scenarios.user.message_history.message_history import MessageHistory 3 | 4 | 5 | class PreprocessingScenariosMessages(MessageHistory): 6 | 7 | def add(self, text_preprocessing_result): 8 | if text_preprocessing_result is not None and text_preprocessing_result.original_text: 9 | self._push(text_preprocessing_result.raw, self.INCOMING) 10 | 11 | @property 12 | def processed_items(self): 13 | return [item['message'] for item in reversed(self.items)] 14 | -------------------------------------------------------------------------------- /scenarios/user/preprocessing_messages/preprocessing_messages_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | class PreprocessingMessagesDescription: 5 | def __init__(self, items): 6 | items = items or {} 7 | self.max_message_count = items["messages"] 8 | self.lifetime = items["lifetime"] 9 | -------------------------------------------------------------------------------- /scenarios/user/reply_selector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/user/reply_selector/__init__.py -------------------------------------------------------------------------------- /scenarios/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/scenarios/utils/__init__.py -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [versioneer] 2 | VCS = git 3 | style = pep440 4 | versionfile_source = smart_kit/_version.py 5 | versionfile_build = smart_kit/_version.py 6 | tag_prefix = v 7 | parentdir_prefix = v 8 | -------------------------------------------------------------------------------- /smart_kit/__init__.py: -------------------------------------------------------------------------------- 1 | from ._version import get_versions 2 | __version__ = get_versions()['version'] 3 | del get_versions 4 | -------------------------------------------------------------------------------- /smart_kit/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from smart_kit.management.base import HelpCommand 4 | 5 | from smart_kit.management.smart_kit_manager import SmartKitManager, CreateAppCommand, VersionCommand 6 | 7 | if __name__ == "__main__": 8 | Manager = SmartKitManager() 9 | Manager.register_command("create_app", CreateAppCommand) 10 | Manager.register_command("version", VersionCommand) 11 | Manager.register_command("help", HelpCommand, Manager.commands) 12 | Manager.execute_from_command_line(sys.argv) 13 | -------------------------------------------------------------------------------- /smart_kit/action/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/action/__init__.py -------------------------------------------------------------------------------- /smart_kit/compatibility/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/compatibility/__init__.py -------------------------------------------------------------------------------- /smart_kit/configs/logger_config.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import os 3 | 4 | import yaml 5 | 6 | from core.configs.base_config import BaseConfig 7 | from core.repositories.file_repository import FileRepository 8 | from smart_kit.utils.logger_writer.logger_formatter import SmartKitJsonFormatter 9 | 10 | SmartKitJsonFormatter.VERSION = os.getenv("VERSION") or 0 11 | 12 | LOGGING_CONFIG = "logging_config" 13 | 14 | 15 | class LoggerConfig(BaseConfig): 16 | def __init__(self, config_path): 17 | self.config_path = config_path 18 | super(LoggerConfig, self).__init__() 19 | self.repositories = [ 20 | FileRepository(self.subfolder_path("logging_config.yml"), 21 | loader=yaml.safe_load, key=LOGGING_CONFIG) 22 | ] 23 | 24 | @property 25 | def _subfolder(self): 26 | return self.config_path 27 | -------------------------------------------------------------------------------- /smart_kit/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/handlers/__init__.py -------------------------------------------------------------------------------- /smart_kit/handlers/handle_close_app.py: -------------------------------------------------------------------------------- 1 | from core.logging.logger_utils import log 2 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 3 | import scenarios.logging.logger_constants as log_const 4 | from scenarios.actions.action import ClearCurrentScenarioAction 5 | 6 | from smart_kit.handlers.handler_base import HandlerBase 7 | 8 | 9 | class HandlerCloseApp(HandlerBase): 10 | def __init__(self, app_name): 11 | super(HandlerCloseApp, self).__init__(app_name) 12 | self._clear_current_scenario = ClearCurrentScenarioAction(None) 13 | 14 | def run(self, payload, user): 15 | super().run(payload, user) 16 | text_preprocessing_result = TextPreprocessingResult(payload.get("message", {})) 17 | params = { 18 | log_const.KEY_NAME: "HandlerCloseApp" 19 | } 20 | self._clear_current_scenario.run(user, text_preprocessing_result) 21 | if payload.get("message"): 22 | params["tpr_str"] = str(text_preprocessing_result.raw) 23 | log("HandlerCloseApp with text preprocessing result", user, params) 24 | -------------------------------------------------------------------------------- /smart_kit/handlers/handle_server_action.py: -------------------------------------------------------------------------------- 1 | import scenarios.logging.logger_constants as log_const 2 | 3 | from core.names.field import SERVER_ACTION 4 | from core.logging.logger_utils import log 5 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 6 | from core.utils.pickle_copy import pickle_deepcopy 7 | 8 | from smart_kit.handlers.handler_base import HandlerBase 9 | from smart_kit.utils.monitoring import smart_kit_metrics 10 | 11 | 12 | class HandlerServerAction(HandlerBase): 13 | handler_name = "HandlerServerAction" 14 | 15 | def __init__(self, app_name, action_name=None, ): 16 | super(HandlerServerAction, self).__init__(app_name) 17 | self._action_name = action_name 18 | 19 | def get_action_name(self, payload, user): 20 | return payload[SERVER_ACTION]["action_id"] 21 | 22 | def get_action_params(self, payload): 23 | return payload[SERVER_ACTION].get("parameters", {}) 24 | 25 | def run(self, payload, user): 26 | action_params = pickle_deepcopy(self.get_action_params(payload)) 27 | params = {log_const.KEY_NAME: "handling_server_action", 28 | "server_action_params": str(action_params), 29 | "server_action_id": self.get_action_name(payload, user)} 30 | log("HandlerServerAction %(server_action_id)s started", user, params) 31 | 32 | app_info = user.message.app_info 33 | smart_kit_metrics.counter_incoming(self.app_name, user.message.message_name, self.__class__.__name__, 34 | user, app_info=app_info) 35 | 36 | action_id = self.get_action_name(payload, user) 37 | action = user.descriptions["external_actions"][action_id] 38 | return action.run(user, TextPreprocessingResult({}), action_params) 39 | -------------------------------------------------------------------------------- /smart_kit/handlers/handler_base.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from smart_kit.utils.monitoring import smart_kit_metrics 3 | 4 | 5 | class HandlerBase: 6 | TOPIC_KEY = "template_app" 7 | KAFKA_KEY = "main" 8 | 9 | def __init__(self, app_name): 10 | self.app_name = app_name 11 | 12 | def run(self, payload, user): 13 | # отправка события о входящем сообщении в систему мониторинга 14 | smart_kit_metrics.counter_incoming(self.app_name, user.message.message_name, self.__class__.__name__, 15 | user, app_info=user.message.app_info) 16 | -------------------------------------------------------------------------------- /smart_kit/handlers/handler_run_app.py: -------------------------------------------------------------------------------- 1 | # coding: utf- 2 | 3 | import scenarios.logging.logger_constants as log_const 4 | from core.logging.logger_utils import log 5 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 6 | 7 | from smart_kit.handlers.handler_base import HandlerBase 8 | 9 | 10 | class HandlerRunApp(HandlerBase): 11 | 12 | def __init__(self, app_name, dialogue_manager): 13 | super().__init__(app_name) 14 | log( 15 | f"{self.__class__.__name__}.__init__ started.", params={log_const.KEY_NAME: log_const.STARTUP_VALUE} 16 | ) 17 | self.dialogue_manager = dialogue_manager 18 | log( 19 | f"{self.__class__.__name__}.__init__ finished.", params={log_const.KEY_NAME: log_const.STARTUP_VALUE} 20 | ) 21 | 22 | def run(self, payload, user): 23 | super().run(payload, user) 24 | 25 | params = {log_const.KEY_NAME: "handling_run_app"} 26 | log(f"{self.__class__.__name__}.run started", user, params) 27 | 28 | answer = self._handle_base(user) 29 | return answer 30 | 31 | def _handle_base(self, user): 32 | answer, is_answer_found = self.dialogue_manager.run(TextPreprocessingResult({}), user) 33 | return answer or [] 34 | -------------------------------------------------------------------------------- /smart_kit/handlers/handler_text.py: -------------------------------------------------------------------------------- 1 | # coding: utf- 2 | 3 | import scenarios.logging.logger_constants as log_const 4 | from core.logging.logger_utils import log 5 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 6 | 7 | from smart_kit.handlers.handler_base import HandlerBase 8 | 9 | 10 | class HandlerText(HandlerBase): 11 | 12 | def __init__(self, app_name, dialogue_manager): 13 | super(HandlerText, self).__init__(app_name) 14 | log( 15 | f"{self.__class__.__name__}.__init__ started.", params={log_const.KEY_NAME: log_const.STARTUP_VALUE} 16 | ) 17 | self.dialogue_manager = dialogue_manager 18 | log( 19 | f"{self.__class__.__name__}.__init__ finished.", params={log_const.KEY_NAME: log_const.STARTUP_VALUE} 20 | ) 21 | 22 | def run(self, payload, user): 23 | super().run(payload, user) 24 | 25 | text_preprocessing_result = TextPreprocessingResult(payload["message"]) 26 | 27 | params = { 28 | log_const.KEY_NAME: log_const.NORMALIZED_TEXT_VALUE, 29 | "normalized_text": str(text_preprocessing_result.raw), 30 | } 31 | log("text preprocessing result: '%(normalized_text)s'", user, params) 32 | 33 | answer = self._handle_base(text_preprocessing_result, user) 34 | return answer 35 | 36 | def _handle_base(self, text_preprocessing_result, user): 37 | answer, is_answer_found = self.dialogue_manager.run(text_preprocessing_result, user) 38 | return answer or [] 39 | -------------------------------------------------------------------------------- /smart_kit/handlers/handler_timeout.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from core.logging.logger_utils import log 3 | import scenarios.logging.logger_constants as log_const 4 | from smart_kit.handlers.handler_base import HandlerBase 5 | from smart_kit.message.app_info import AppInfo 6 | from core.names.field import APP_INFO 7 | from smart_kit.names.message_names import RUN_APP, MESSAGE_TO_SKILL, SERVER_ACTION 8 | from smart_kit.utils.monitoring import smart_kit_metrics 9 | 10 | 11 | class HandlerTimeout(HandlerBase): 12 | 13 | def run(self, payload, user): 14 | super().run(payload, user) 15 | callback_id = user.message.callback_id 16 | if user.behaviors.has_callback(callback_id): 17 | params = {log_const.KEY_NAME: "handling_timeout"} 18 | log("TimeoutHandler started", user, params) 19 | action_params = user.behaviors.get_callback_action_params(callback_id) 20 | if action_params: 21 | app_info = None 22 | for original_message_name in [MESSAGE_TO_SKILL, SERVER_ACTION, RUN_APP]: 23 | if original_message_name in action_params: 24 | app_info = AppInfo(action_params[original_message_name].get(APP_INFO, {})) 25 | break 26 | 27 | smart_kit_metrics.counter_incoming(self.app_name, user.message.message_name, self.__class__.__name__, 28 | user, app_info=app_info) 29 | 30 | callback_id = user.message.callback_id 31 | result = user.behaviors.timeout(callback_id) 32 | return result 33 | -------------------------------------------------------------------------------- /smart_kit/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/management/__init__.py -------------------------------------------------------------------------------- /smart_kit/management/base.py: -------------------------------------------------------------------------------- 1 | class AppCommand: 2 | @classmethod 3 | def doc(cls): 4 | return cls.__doc__ or "No help provided" 5 | 6 | def execute(self, *args, **kwargs): 7 | raise NotImplementedError 8 | 9 | 10 | class HelpCommand(AppCommand): 11 | def __init__(self, commands): 12 | self._commands = commands 13 | 14 | def execute(self, *args, **kwargs): 15 | if not args: 16 | print("Available commands:") 17 | for cmd in self._commands: 18 | print(f"\t {cmd}") 19 | return 20 | if len(args) >= 2: 21 | raise ValueError("Wrong command usage") 22 | 23 | name = args[0] 24 | if name not in self._commands: 25 | print(f"{name} command not registered") 26 | return 27 | 28 | command = self._commands[name] 29 | print(command.doc()) 30 | 31 | 32 | class Manager: 33 | def __init__(self): 34 | self.commands = {} 35 | 36 | def register_command(self, name, command, *args, **kwargs): 37 | self.commands[name] = command(*args, **kwargs) 38 | 39 | def execute_command(self, name, *args, **kwargs): 40 | return self.commands[name].execute(*args, **kwargs) 41 | 42 | def default(self, argv): 43 | return NotImplementedError 44 | 45 | def execute_from_command_line(self, argv): 46 | if len(argv) <= 1: 47 | return self.default(argv) 48 | 49 | name = argv[1] 50 | 51 | if name not in self.commands: 52 | return self.default(argv) 53 | 54 | return self.execute_command(name, *argv[2:]) 55 | -------------------------------------------------------------------------------- /smart_kit/management/plugins.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | 3 | from core.logging.logger_utils import log 4 | 5 | 6 | def activate_plugins(app_config, manager): 7 | for plugin in app_config.PLUGINS: 8 | try: 9 | plugin = importlib.import_module(plugin) 10 | except ImportError as exc: 11 | log(f"Error due importing {plugin}.\n {exc}. Plugin {plugin} is installed?", level="ERROR") 12 | continue 13 | 14 | try: 15 | plugin.on_startup(app_config, manager) 16 | except AttributeError: 17 | log(f"Plugin {plugin.__name__} does not have on_startup method", level="ERROR") 18 | continue 19 | except Exception as exc: 20 | log(f"Error due executing {plugin.__name__} plugin.\n {exc}", level="ERROR") 21 | continue 22 | -------------------------------------------------------------------------------- /smart_kit/message/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/message/__init__.py -------------------------------------------------------------------------------- /smart_kit/message/app_info.py: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | from core.names import field 4 | 5 | 6 | class AppInfo: 7 | 8 | def __init__(self, value): 9 | self._value = value 10 | 11 | @lazy 12 | def project_id(self): 13 | return self._value.get(field.PROJECT_ID) 14 | 15 | @lazy 16 | def system_name(self): 17 | return self._value.get(field.SYSTEM_NAME) 18 | 19 | @lazy 20 | def application_id(self): 21 | return self._value.get(field.APPLICATION_ID) 22 | 23 | @lazy 24 | def app_version_id(self): 25 | return self._value.get(field.APP_VERSION_ID) 26 | 27 | @lazy 28 | def frontend_endpoint(self): 29 | return self._value.get(field.FRONTEND_ENDPOINT) 30 | 31 | @lazy 32 | def frontend_type(self): 33 | return self._value.get(field.FRONTEND_TYPE) 34 | -------------------------------------------------------------------------------- /smart_kit/message/get_to_message.py: -------------------------------------------------------------------------------- 1 | from core.model.registered import Registered 2 | from smart_kit.message.smartapp_to_message import SmartAppToMessage 3 | 4 | to_messages = Registered() 5 | 6 | 7 | def get_to_message(command_name): 8 | default = SmartAppToMessage 9 | return to_messages.get(command_name, default) 10 | -------------------------------------------------------------------------------- /smart_kit/message/smart_app_push_message.py: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | from core.message.from_message import SmartAppFromMessage 4 | from smart_kit.message.smartapp_to_message import SmartAppToMessage 5 | 6 | 7 | class SmartAppPushToMessage(SmartAppToMessage): 8 | 9 | @lazy 10 | def as_dict(self): 11 | self.incoming_message: SmartAppFromMessage 12 | fields = { 13 | "projectId": self.payload.get("project_id"), 14 | "clientId": self.incoming_message.sub, 15 | "surface": self.payload.get("surface"), 16 | "content": self.payload.get("content"), 17 | } 18 | fields.update(self.root_nodes) 19 | return fields 20 | 21 | -------------------------------------------------------------------------------- /smart_kit/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/models/__init__.py -------------------------------------------------------------------------------- /smart_kit/names/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/names/__init__.py -------------------------------------------------------------------------------- /smart_kit/names/action_params_names.py: -------------------------------------------------------------------------------- 1 | TO_MESSAGE_PARAMS = "to_message_params" 2 | TO_MESSAGE_NAME = "to_message_name" 3 | SAVED_MESSAGES = "saved_messages" 4 | SEND_TIMESTAMP = "send_timestamp" 5 | -------------------------------------------------------------------------------- /smart_kit/names/message_names.py: -------------------------------------------------------------------------------- 1 | ANSWER_TO_USER = "ANSWER_TO_USER" 2 | ERROR = "ERROR" 3 | NOTHING_FOUND = "NOTHING_FOUND" 4 | MESSAGE_TO_SKILL = "MESSAGE_TO_SKILL" 5 | LOCAL_TIMEOUT = "LOCAL_TIMEOUT" 6 | RUN_APP = "RUN_APP" 7 | CLOSE_APP = "CLOSE_APP" 8 | SERVER_ACTION = "SERVER_ACTION" 9 | -------------------------------------------------------------------------------- /smart_kit/request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/request/__init__.py -------------------------------------------------------------------------------- /smart_kit/request/kafka_request.py: -------------------------------------------------------------------------------- 1 | from core.configs.global_constants import CALLBACK_ID_HEADER 2 | from core.request.kafka_request import KafkaRequest 3 | 4 | 5 | class SmartKitKafkaRequest(KafkaRequest): 6 | KAFKA_REPLY_TOPIC = "kafka_replyTopic" 7 | KAFKA_EXTRA_HEADERS = "kafka_extraHeaders" 8 | 9 | def __init__(self, items, id=None): 10 | super(SmartKitKafkaRequest, self).__init__(items) 11 | items = items or {} 12 | self._callback_id = items.get(self._callback_id_header_name) 13 | self._kafka_replyTopic = items.get(self.KAFKA_REPLY_TOPIC) 14 | self._kafka_extraHeaders = items.get(self.KAFKA_EXTRA_HEADERS) or {} 15 | 16 | @property 17 | def _callback_id_header_name(self): 18 | return CALLBACK_ID_HEADER 19 | 20 | def _get_new_headers(self, source_mq_message): 21 | headers_dict = dict(super(SmartKitKafkaRequest, self)._get_new_headers(source_mq_message)) 22 | if self._callback_id: 23 | headers_dict[self._callback_id_header_name] = str(self._callback_id).encode() 24 | if self._kafka_replyTopic: 25 | headers_dict[self.KAFKA_REPLY_TOPIC] = str(self._kafka_replyTopic).encode() 26 | if self._kafka_extraHeaders: 27 | for k, v in self._kafka_extraHeaders.items(): 28 | headers_dict[k] = str(v).encode() 29 | headers_list = list(headers_dict.items()) 30 | return headers_list 31 | 32 | def __str__(self): 33 | return f"KafkaRequest: kafka_key={self.kafka_key}" 34 | -------------------------------------------------------------------------------- /smart_kit/start_points/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/start_points/__init__.py -------------------------------------------------------------------------------- /smart_kit/start_points/app.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import logging 3 | 4 | from core.logging.logger_utils import log 5 | 6 | 7 | def run(app_config): 8 | log("RUN APP starting", level="WARNING") 9 | 10 | log("START SETTINGS CREATE", level="WARNING") 11 | settings = app_config.SETTINGS( 12 | config_path=app_config.CONFIGS_PATH, secret_path=app_config.SECRET_PATH, 13 | references_path=app_config.REFERENCES_PATH, app_name=app_config.APP_NAME) 14 | log("FINISHED SETTINGS CREATE", level="WARNING") 15 | source = settings.get_source() 16 | log("START RESOURCES CREATE", level="WARNING") 17 | resource = app_config.RESOURCES(source, app_config.REFERENCES_PATH, settings) 18 | log("FINISHED RESOURCES CREATE", level="WARNING") 19 | log("START MODEL CREATE", level="WARNING") 20 | model = app_config.MODEL(resource, app_config.DIALOGUE_MANAGER, settings) 21 | log("FINISHED MODEL CREATE", level="WARNING") 22 | 23 | log("START MAIN_LOOP CREATE", level="WARNING") 24 | loop = app_config.MAIN_LOOP( 25 | model, app_config.USER, app_config.PARAMETRIZER, app_config.POSTPROCESSOR_MAIN_LOOP, 26 | settings, app_config.TO_MSG_VALIDATORS, app_config.FROM_MSG_VALIDATORS, 27 | ) 28 | log("FINISHED MAIN_LOOP CREATE", level="WARNING") 29 | loop.run() 30 | logging.shutdown() 31 | -------------------------------------------------------------------------------- /smart_kit/start_points/postprocess.py: -------------------------------------------------------------------------------- 1 | from typing import List, Type 2 | 3 | 4 | class PostprocessMainLoop: 5 | 6 | def postprocess(self, user, message, *args, **kwargs): 7 | pass 8 | 9 | 10 | class PostprocessCompose(PostprocessMainLoop): 11 | postprocessors: List[PostprocessMainLoop] = [] 12 | 13 | def postprocess(self, user, message, *args, **kwargs): 14 | for processor in self.postprocessors: 15 | processor.postprocess(user, message, *args, **kwargs) 16 | 17 | 18 | def postprocessor_compose(*args: List[Type[PostprocessMainLoop]]): 19 | class Compose(PostprocessCompose): 20 | postprocessors = [processor_cls() for processor_cls in args] 21 | return Compose 22 | -------------------------------------------------------------------------------- /smart_kit/system_answers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/system_answers/__init__.py -------------------------------------------------------------------------------- /smart_kit/system_answers/nothing_found_action.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional, List, Union 2 | 3 | from core.basic_models.actions.basic_actions import Action 4 | from core.basic_models.actions.string_actions import StringAction 5 | from core.basic_models.actions.command import Command 6 | from core.text_preprocessing.base import BaseTextPreprocessingResult 7 | 8 | from scenarios.user.user_model import User 9 | 10 | from smart_kit.names.message_names import NOTHING_FOUND 11 | 12 | 13 | class NothingFoundAction(Action): 14 | version: Optional[int] 15 | id: Optional[str] 16 | 17 | def __init__(self, items: Dict[str, Any] = None, id: Optional[str] = None): 18 | super(NothingFoundAction, self).__init__(items, id) 19 | self._action = StringAction({"command": NOTHING_FOUND}) 20 | 21 | def run(self, user: User, text_preprocessing_result: BaseTextPreprocessingResult, 22 | params: Optional[Dict[str, Union[str, float, int]]] = None) -> Optional[List[Command]]: 23 | return self._action.run(user, text_preprocessing_result, params=params) 24 | -------------------------------------------------------------------------------- /smart_kit/template/app/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/adapters/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/adapters/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/adapters/db_adapters.py-tpl: -------------------------------------------------------------------------------- 1 | from core.db_adapter.db_adapter import DBAdapter 2 | from core.db_adapter import error 3 | 4 | 5 | class CustomDBAdapter(DBAdapter): 6 | 7 | """ 8 | Для создания нового класса-коннектора к БД, реализуйте следующие методы или 9 | пометьте их raise error.NotSupportedOperation 10 | """ 11 | 12 | def __init__(self, config): 13 | super(CustomDBAdapter, self).__init__(config) 14 | 15 | def connect(self): 16 | pass 17 | 18 | def _open(self, filename, *args, **kwargs): 19 | pass 20 | 21 | def _save(self, id, data): 22 | pass 23 | 24 | def _replace_if_equals(self, id, sample, data): 25 | pass 26 | 27 | def _get(self, id): 28 | pass 29 | 30 | def _list_dir(self, path): 31 | raise error.NotSupportedOperation 32 | 33 | def _glob(self, path, pattern): 34 | raise error.NotSupportedOperation 35 | 36 | def _path_exists(self, path): 37 | pass 38 | 39 | def _on_prepare(self): 40 | pass 41 | -------------------------------------------------------------------------------- /smart_kit/template/app/basic_entities/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/basic_entities/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/basic_entities/actions.py-tpl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from typing import Union, Dict, Any, Optional 3 | 4 | from core.basic_models.actions.basic_actions import Action 5 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 6 | 7 | from scenarios.user.user_model import User 8 | 9 | 10 | class CustomAction(Action): 11 | 12 | """ 13 | Тут можно создать собственные Actions для использования их в сценариях 14 | """ 15 | 16 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None): 17 | super(CustomAction, self).__init__(items, id) 18 | items = items or {} 19 | self.test_param = items.get("test_param") 20 | 21 | def run(self, user: User, text_preprocessing_result: TextPreprocessingResult, 22 | params: Optional[Dict[str, Union[str, float, int]]] = None) -> None: 23 | print("Test Action") 24 | return None 25 | -------------------------------------------------------------------------------- /smart_kit/template/app/basic_entities/fillers.py-tpl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from typing import Dict, Optional, Any 3 | 4 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 5 | from core.utils.exception_handlers import exc_handler 6 | 7 | from scenarios.scenario_models.field.field_filler_description import FieldFillerDescription 8 | from scenarios.user.user_model import User 9 | 10 | 11 | class CustomFieldFiller(FieldFillerDescription): 12 | 13 | """ 14 | Тут можно создать собственные Fillers для использования их в заполнении полей форм и 15 | """ 16 | 17 | def __init__(self, items: Optional[Dict[str, Any]], id: Optional[str] = None) -> None: 18 | super(SampleFieldFiller, self).__init__(items, id) 19 | items = items or {} 20 | self.test_item = items.get("test_item") 21 | 22 | @exc_handler(on_error_obj_method_name="on_extract_error") 23 | def extract(self, text_preprocessing_result: TextPreprocessingResult, user: User, params) -> Optional[str]: 24 | return None 25 | -------------------------------------------------------------------------------- /smart_kit/template/app/basic_entities/requirements.py-tpl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from typing import Optional, Dict, Any 3 | 4 | from core.basic_models.requirement.basic_requirements import Requirement 5 | from core.text_preprocessing.preprocessing_result import TextPreprocessingResult 6 | 7 | from scenarios.user.user_model import User 8 | 9 | 10 | class CustomRequirement(Requirement): 11 | 12 | """ 13 | Тут можно создать собственные Requirements для использования их в сценариях 14 | """ 15 | 16 | def __init__(self, items: Dict[str, Any], id: Optional[str] = None) -> None: 17 | super(CustomRequirement, self).__init__(items, id) 18 | items = items or {} 19 | self.test_param = items.get("test_param") 20 | 21 | def check(self, text_preprocessing_result: TextPreprocessingResult, 22 | user: User, params: Dict[str, Any] = None) -> bool: 23 | return False 24 | -------------------------------------------------------------------------------- /smart_kit/template/app/handlers/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/handlers/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/handlers/handlers.py-tpl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from smart_kit.handlers.handler_base import HandlerBase 3 | 4 | 5 | class CustomHandler(HandlerBase): 6 | 7 | """ 8 | Тут создаются Handlers, которые используются для запуска логики в зависимости от типа входяшего сообщения 9 | """ 10 | 11 | def run(self, payload, user): 12 | return [] 13 | -------------------------------------------------------------------------------- /smart_kit/template/app/local_testing/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/local_testing/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/local_testing/custom_local_testing.py-tpl: -------------------------------------------------------------------------------- 1 | from smart_kit.testing.local import CLInterface 2 | 3 | 4 | class CustomLocalTesting(CLInterface): 5 | """ 6 | Тут модифицируется local_testing для эмулирования ответов от внешних систем 7 | формат функции - on_(self, message): 8 | где - это тип сообщения,написанный в нижнем регистре 9 | например: 10 | def on_back_get_token_request(self, message): 11 | # BACK_GET_TOKEN_REQUEST - тип сообщения 12 | return json.dumps({ 13 | "messageId": self.environment.message_id, 14 | "messageName": "BACK_GET_TOKEN_RESPONSE", 15 | "uuid": { 16 | "userChannel": self.environment.user_channel, 17 | "userId": self.environment.user_id, 18 | "chatId": self.environment.chat_id 19 | }, 20 | "payload": { 21 | "token": "test", 22 | } 23 | } 24 | ) 25 | """ 26 | -------------------------------------------------------------------------------- /smart_kit/template/app/models/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/models/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/models/dialogue_manager.py-tpl: -------------------------------------------------------------------------------- 1 | from smart_kit.models.dialogue_manager import DialogueManager 2 | 3 | 4 | class CustomDialogueManager(DialogueManager): 5 | 6 | """ 7 | В собственном DialogManager можно переопределить логику, связанную с вызовом сценариев по входяшему интенту 8 | """ 9 | 10 | def __init__(self, scenario_descriptions, app_name, **kwargs): 11 | super(CustomDialogueManager, self).__init__(scenario_descriptions, app_name, **kwargs) -------------------------------------------------------------------------------- /smart_kit/template/app/models/model.py-tpl: -------------------------------------------------------------------------------- 1 | from smart_kit.models.smartapp_model import SmartAppModel 2 | 3 | 4 | class CustomModel(SmartAppModel): 5 | 6 | """ 7 | В собственной Model можно добавить новые хендлеры для обработки входящих сообщений 8 | """ 9 | 10 | def __init__(self, resources, dialogue_manager_cls, custom_settings, **kwargs): 11 | super(CustomModel, self).__init__(resources, dialogue_manager_cls, custom_settings, **kwargs) 12 | self._handlers.update({}) 13 | 14 | -------------------------------------------------------------------------------- /smart_kit/template/app/resources/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/resources/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/resources/custom_app_resourses.py-tpl: -------------------------------------------------------------------------------- 1 | from core.basic_models.requirement.basic_requirements import requirements 2 | from core.basic_models.actions.basic_actions import actions 3 | from core.db_adapter.db_adapter import db_adapters 4 | 5 | import scenarios.scenario_models.field.field_filler_description as ffd 6 | 7 | from smart_kit.resources import SmartAppResources 8 | from app.adapters.db_adapters import CustomDBAdapter 9 | from app.basic_entities.actions import CustomAction 10 | from app.basic_entities.fillers import CustomFieldFiller 11 | from app.basic_entities.requirements import CustomRequirement 12 | 13 | 14 | class CustomAppResourses(SmartAppResources): 15 | 16 | def __init__(self, source, references_path, settings): 17 | super(CustomAppResourses, self).__init__(source, references_path, settings) 18 | 19 | def override_repositories(self, repositories: list): 20 | """ 21 | Метод предназначен для переопределения репозиториев в дочерних классах. 22 | :param repositories: Список репозиториев родителя 23 | :return: Переопределённый в наследниках список репозиториев 24 | """ 25 | return repositories 26 | 27 | def init_field_filler_description(self): 28 | super(CustomAppResourses, self).init_field_filler_description() 29 | ffd.field_filler_description["simple_filler"] = CustomFieldFiller 30 | 31 | def init_actions(self): 32 | super(CustomAppResourses, self).init_actions() 33 | actions["custom_action"] = CustomAction 34 | 35 | def init_requirements(self): 36 | super(CustomAppResourses, self).init_requirements() 37 | requirements["sample_requirement"] = CustomRequirement 38 | 39 | def init_db_adapters(self): 40 | super(CustomAppResourses, self).init_db_adapters() 41 | db_adapters["custom_db_adapter"] = CustomDBAdapter 42 | 43 | -------------------------------------------------------------------------------- /smart_kit/template/app/user/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/app/user/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/app/user/parametrizer.py-tpl: -------------------------------------------------------------------------------- 1 | from scenarios.user.parametrizer import Parametrizer 2 | 3 | 4 | class CustomParametrizer(Parametrizer): 5 | 6 | """ 7 | Тут можно добавить новые данные, которые будут доступны для использования в ASL при использовании jinja 8 | """ 9 | 10 | def __init__(self, user, items): 11 | super(CustomParametrizer, self).__init__(user, items) 12 | 13 | def _get_user_data(self, text_preprocessing_result=None): 14 | data = super(CustomParametrizer, self)._get_user_data(text_preprocessing_result) 15 | data.update({}) 16 | return data 17 | -------------------------------------------------------------------------------- /smart_kit/template/app/user/user.py-tpl: -------------------------------------------------------------------------------- 1 | from lazy import lazy 2 | 3 | from scenarios.user.user_model import User 4 | 5 | from .parametrizer import CustomParametrizer 6 | 7 | 8 | class CustomeUser(User): 9 | 10 | """ 11 | Класс User - модель для хранения данных конкретного пользователя 12 | в метод fields можно добавляются собственные поля, для использования внутри базовых сущностей 13 | """ 14 | 15 | def __init__(self, id, message, db_data, settings, descriptions, parametrizer_cls, load_error=False): 16 | super(CustomeUser, self).__init__(id, message, db_data, settings, 17 | descriptions, parametrizer_cls, load_error) 18 | 19 | @property 20 | def fields(self): 21 | return super(CustomeUser, self).fields + [] 22 | 23 | @lazy 24 | def parametrizer(self): 25 | return CustomParametrizer(self, {}) 26 | 27 | def expire(self): 28 | super(CustomeUser, self).expire() 29 | -------------------------------------------------------------------------------- /smart_kit/template/app_config.py-tpl: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from app.user.user import CustomeUser 4 | from app.user.parametrizer import CustomParametrizer 5 | from app.models.model import CustomModel 6 | from app.resources.custom_app_resourses import CustomAppResourses 7 | 8 | USER = CustomeUser 9 | PARAMETRIZER = CustomParametrizer 10 | MODEL = CustomModel 11 | RESOURCES = CustomAppResourses 12 | 13 | path = os.path.abspath(__file__) 14 | APP_NAME = os.path.dirname(path).split('/')[-1] 15 | -------------------------------------------------------------------------------- /smart_kit/template/manage.py-tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | os.environ.setdefault("SMART_KIT_APP_CONFIG", "app_config") 6 | 7 | from smart_kit.management.app_manager import execute_from_command_line 8 | 9 | execute_from_command_line(sys.argv) 10 | -------------------------------------------------------------------------------- /smart_kit/template/monitoring/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/monitoring/__init__.py-tpl -------------------------------------------------------------------------------- /smart_kit/template/monitoring/monitoring_wsgi_app.py-tpl: -------------------------------------------------------------------------------- 1 | from prometheus_client import multiprocess 2 | from prometheus_client import generate_latest, CollectorRegistry, CONTENT_TYPE_LATEST 3 | 4 | # Expose metrics. 5 | def application(env, start_response): 6 | registry = CollectorRegistry() 7 | multiprocess.MultiProcessCollector(registry) 8 | data = generate_latest(registry) 9 | status = '200 OK' 10 | response_headers = [ 11 | ('Content-type', CONTENT_TYPE_LATEST), 12 | ('Content-Length', str(len(data))) 13 | ] 14 | start_response(status, response_headers) 15 | return iter([data]) 16 | -------------------------------------------------------------------------------- /smart_kit/template/static/.text_normalizer_resources/static_workdata.json: -------------------------------------------------------------------------------- 1 | { 2 | "convert_plan": { 3 | "text": [ 4 | "Конверсия юникодовых символов", 5 | "Цифры и буквы отдельно", 6 | "Номера телефонов", 7 | "Номера карт", 8 | "Объединение сумм", 9 | "Символы валют", 10 | "Претокенизация математических операций" 11 | ], 12 | "voice": [ 13 | "Конверсия юникодовых символов", 14 | "Цифры и буквы отдельно", 15 | "Номера телефонов", 16 | "Номера карт", 17 | "Объединение сумм", 18 | "Символы валют", 19 | "Претокенизация математических операций" 20 | ] 21 | }, 22 | "processor_pipeline": [ 23 | {"name": "Synonyms"}, 24 | {"name": "Text2Num"}, 25 | {"name": "Currency"}, 26 | {"name": "Grammemes"} 27 | ], 28 | "word_false_stoppings": [ 29 | "г", 30 | "п", 31 | "д", 32 | "е" 33 | ], 34 | "word_no_splitting_point": [ 35 | "яндексденьги", 36 | "яндекстакси", 37 | "те", 38 | "тк", 39 | "тч", 40 | "тр", 41 | "сбербанконлайн", 42 | "сбербанкпремьер", 43 | "физлицо", 44 | "физлица", 45 | "физлицу", 46 | "физлицом", 47 | "физлиц", 48 | "физлице", 49 | "физлицах", 50 | "физлицам", 51 | "физлицами", 52 | "юрлицо", 53 | "юрлица", 54 | "юрлицу", 55 | "юрлицом", 56 | "юрлиц", 57 | "юрлице", 58 | "юрлицах", 59 | "юрлицам", 60 | "юрлицами", 61 | "техподдержка", 62 | "техподдержке", 63 | "техподдержки", 64 | "техподдержкой", 65 | "техподдержку" 66 | ], 67 | "unicode_symbols": { 68 | "": "?", 69 | "": "₽", 70 | "": " ", 71 | "": "½", 72 | "\ufeff": " " 73 | }, 74 | "sberbank_phones": [ 75 | "900", 76 | "9000", 77 | "9001", 78 | "8632", 79 | "6470" 80 | ] 81 | } -------------------------------------------------------------------------------- /smart_kit/template/static/configs/logging_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | loggers: 3 | {{app_name}}: 4 | handlers: 5 | - console_app_handler 6 | - file_app_handler 7 | propagate: false 8 | level: DEBUG 9 | version: 1 10 | root: 11 | handlers: 12 | - console_handler 13 | - file_handler 14 | level: DEBUG 15 | handlers: 16 | file_app_handler: 17 | backupCount: 30 18 | encoding: utf-8 19 | filename: "./app.log" 20 | formatter: file_json_formatter 21 | class: core.logging.logger_handlers.RotatingFilePidHandler 22 | maxBytes: 15728640 23 | console_app_handler: 24 | formatter: console_app_formatter 25 | class: logging.StreamHandler 26 | stream: ext://sys.stdout 27 | file_handler: 28 | backupCount: 30 29 | encoding: utf-8 30 | filename: "./system.log" 31 | formatter: file_json_formatter 32 | class: core.logging.logger_handlers.RotatingFilePidHandler 33 | maxBytes: 15728640 34 | console_handler: 35 | formatter: console_simple_formatter 36 | class: logging.StreamHandler 37 | stream: ext://sys.stdout 38 | formatters: 39 | file_json_formatter: 40 | "()": smart_kit.utils.logger_writer.logger_formatter.SmartKitJsonFormatter 41 | json_ensure_ascii: false 42 | format: "%(created)f %(msecs)d %(relativeCreated)d %(levelname)s %(name)s %(asctime)s 43 | %(module)s %(message)s" 44 | console_app_formatter: 45 | style: "{" 46 | format: "[{levelname}] {name} {asctime} {module} {args[uid]} {args[message_id]} 47 | {args[logging_uuid]} {args[class_name]}: {message}" 48 | console_simple_formatter: 49 | format: "[%(levelname)s] %(name)s %(asctime)s %(module)s: %(message)s" 50 | log_store_for_map: -------------------------------------------------------------------------------- /smart_kit/template/static/configs/template_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | environment: local 4 | health_check: 5 | enabled: false 6 | port: 1234 7 | interface: 0.0.0.0 8 | debug_envs: 9 | - local 10 | monitoring: 11 | enabled: false 12 | disabled_metrics: [] 13 | masking_fields: 14 | - token 15 | - access_token 16 | - refresh_token 17 | - epkId 18 | - profileId 19 | user_save_collisions_tries: 2 20 | self_service_with_state_save_messages: true 21 | project_id: template-app-id 22 | consumer_topic: "app" -------------------------------------------------------------------------------- /smart_kit/template/static/references/actions/actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "exception_action": { 3 | "command": "ANSWER_TO_USER", 4 | "nodes": { 5 | "answer": [ 6 | [ 7 | "Произошёл технический сбой, извините. Давайте попробуем ещё раз?" 8 | ] 9 | ] 10 | } 11 | }, 12 | "process_behavior_action": { 13 | "type": "process_behavior" 14 | }, 15 | "before_action": { 16 | "type": "requirement", 17 | "action": { 18 | "type": "clear_all_scenarios" 19 | }, 20 | "requirement": { 21 | "type": "template", 22 | "template": "{{ payload.new_session and settings['template_settings'].get('reset_context_on_new_session', False)}}" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/behaviors/behaviors.json: -------------------------------------------------------------------------------- 1 | { 2 | "common_behavior": { 3 | "timeout": 4, 4 | "success_action": { 5 | "type": "run_last_scenario" 6 | }, 7 | "fail_action": { 8 | "type": "external", 9 | "action": "exception_reset_scenario" 10 | }, 11 | "timeout_action": { 12 | "type": "external", 13 | "action": "timeout_reset_scenario" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/bundles/bundles.json: -------------------------------------------------------------------------------- 1 | { 2 | "bundle_hello_scenario": { 3 | "key_answer.Сбер": [ 4 | "Я — Сбер! Ваш виртуальный ассистент", 5 | "Меня зовут Сбер. Прошу любить и не жаловаться! Простите за глупую шутку, волнуюсь" 6 | ], 7 | "key_answer.Джой": [ 8 | "Меня зовут Джой, я твой виртуальный ассистент!" 9 | ], 10 | "key_answer.Афина": [ 11 | "Я Афина. Ваш гениальный ассистент. Простите, виртуальный" 12 | ], 13 | "key_answer": [ 14 | "Приятно познакомиться!" 15 | ] 16 | }, 17 | "bundle_examples": { 18 | "key_1.Афина.female": [ 19 | "Большое спасибо! Вы прекрасны!" 20 | ], 21 | "key_1.Джой.male": [ 22 | "Благодарю тебя, джентельмен!" 23 | ], 24 | "key_1.Сбер.female": [ 25 | "Большое спасибо! Ты очаровательна!" 26 | ], 27 | "key_1.Сбер.male": [ 28 | "Благодарю тебя" 29 | ], 30 | "key_1.male_to_male": [ 31 | "Большое спасибо" 32 | ], 33 | "key_1.male_to_female": [ 34 | "Большое спасибо! Ты очаровательна!" 35 | ], 36 | "key_1.female_to_male": [ 37 | "Благодарю тебя, джентельмен!" 38 | ], 39 | "key_1.female_to_female": [ 40 | "Большое спасибо! Вы прекрасны!" 41 | ] 42 | } 43 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/classifiers/classifiers.json: -------------------------------------------------------------------------------- 1 | { 2 | "read_book_or_not_classifier": { 3 | "type": "scikit", 4 | "threshold": 0.5, 5 | "path": "ReadBookOrNotClassifier.pkl", 6 | "intents": ["да", "нет"] 7 | }, 8 | "hello_scenario_classifier": { 9 | "type": "external", 10 | "timeout": 0.2, 11 | "classifier": "read_book_or_not_classifier" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /smart_kit/template/static/references/classifiers_data/ReadBookOrNotClassifier.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/template/static/references/classifiers_data/ReadBookOrNotClassifier.pkl -------------------------------------------------------------------------------- /smart_kit/template/static/references/field_fillers/field_fillers.json: -------------------------------------------------------------------------------- 1 | { 2 | "yes_no_soft_filler": { 3 | "type": "approve", 4 | "yes_words": [ 5 | "да", 6 | "конечно", 7 | "давай", 8 | "ага", 9 | "хорошо", 10 | "именно", 11 | "можно" 12 | ], 13 | "no_words": [ 14 | "нет", 15 | "не", 16 | "ни", 17 | "нельзя", 18 | "отнюдь", 19 | "нету" 20 | ] 21 | }, 22 | "yes_no_hard_filler": { 23 | "type": "approve_strictly", 24 | "yes_words": [ 25 | "да", 26 | "конечно", 27 | "давай", 28 | "ага", 29 | "хорошо", 30 | "именно", 31 | "можно" 32 | ], 33 | "no_words": [ 34 | "нет", 35 | "не", 36 | "ни", 37 | "нельзя", 38 | "отнюдь", 39 | "нету" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /smart_kit/template/static/references/history.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "formatter": { 4 | "type": "history_formatter_20" 5 | } 6 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/last_scenarios_descriptions.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /smart_kit/template/static/references/predefined_fields_storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_request_params": { 3 | "messageName": "MESSAGE_TO_SKILL", 4 | "uuid": { 5 | "userChannel": "None", 6 | "chatId": "1", 7 | "userId": "local_testing_1" 8 | }, 9 | "payload": { 10 | "character": { 11 | "id": "sber", 12 | "name": "Сбер", 13 | "gender": "male", 14 | "appeal": "official" 15 | }, 16 | "device": { 17 | "platformType": "IOS", 18 | "platformVersion": "11.1", 19 | "surface": "SBOL", 20 | "surfaceVersion": "testSurfaceVersion", 21 | "features": { 22 | "appTypes": [ 23 | "DIALOG", 24 | "WEB_APP" 25 | ] 26 | } 27 | }, 28 | "intent": "hello_scenario", 29 | "meta": { 30 | "time": { 31 | "timezone_id": "Europe/Moscow", 32 | "timezone_offset_sec": 10800, 33 | "timestamp": 1432233446145000 34 | } 35 | }, 36 | "personInfo": {}, 37 | "projectName": "test_project_name" 38 | } 39 | }, 40 | "default_response_scheme": { 41 | "user": {}, 42 | "messages": [ 43 | { 44 | "messageName": "ANSWER_TO_USER", 45 | "payload": { 46 | "pronounceText": null 47 | } 48 | } 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /smart_kit/template/static/references/preprocessing_messages_for_scenarios_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": 5, 3 | "lifetime": 86400 4 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/responses.json: -------------------------------------------------------------------------------- 1 | { 2 | "EXAMPLE_SYSTEM_REPLY_MESSAGE_NAME": "process_behavior_action" 3 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/scenarios/run_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "run_app": { 3 | "actions": [ 4 | { 5 | "type": "run_scenario", 6 | "scenario": "hello_scenario" 7 | } 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /smart_kit/template/static/references/test_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "test_case_1": { 3 | "user": {}, 4 | "messages": [ 5 | { 6 | "request": { 7 | "payload": { 8 | "message": {}, 9 | "intent": "hello_scenario" 10 | } 11 | }, 12 | "response": { 13 | "user": {}, 14 | "messages": [ 15 | { 16 | "messageName": "ANSWER_TO_USER", 17 | "payload": { 18 | "pronounceText": "Привет \"local_testing_1\"!" 19 | } 20 | } 21 | ] 22 | } 23 | } 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /smart_kit/template/wsgi.py-tpl: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import scenarios.logging.logger_constants as log_const 4 | from core.logging.logger_utils import log 5 | from smart_kit.configs import get_app_config 6 | 7 | 8 | def create_app(): 9 | os.environ.setdefault("SMART_KIT_APP_CONFIG", "app_config") 10 | app_config = get_app_config() 11 | 12 | app_name = app_config.APP_NAME 13 | configs_path, secret_path, settings_cls, references_path = \ 14 | app_config.CONFIGS_PATH, app_config.SECRET_PATH, app_config.SETTINGS, app_config.REFERENCES_PATH 15 | 16 | resources_cls, model_cls, dialogue_manager_cls, main_loop_cls, user_cls, parametrizer_cls = \ 17 | app_config.RESOURCES, app_config.MODEL, app_config.DIALOGUE_MANAGER, app_config.MAIN_LOOP, \ 18 | app_config.USER, app_config.PARAMETRIZER 19 | 20 | log("START SETTINGS CREATE", params={log_const.KEY_NAME: "timings"}) 21 | settings = settings_cls(config_path=configs_path, secret_path=secret_path, references_path=references_path) 22 | log("FINISHED SETTINGS CREATE", params={log_const.KEY_NAME: "timings"}) 23 | source = settings.get_source() 24 | log("START RESOURCES CREATE", params={log_const.KEY_NAME: "timings"}) 25 | resource = resources_cls(source, references_path, settings) 26 | log("FINISHED RESOURCES CREATE", params={log_const.KEY_NAME: "timings"}) 27 | log("START MODEL CREATE", params={log_const.KEY_NAME: "timings"}) 28 | model = model_cls(resource, dialogue_manager_cls, settings, app_name=app_name) 29 | log("FINISHED MODEL CREATE", params={log_const.KEY_NAME: "timings"}) 30 | log("START MAIN_LOOP CREATE", params={log_const.KEY_NAME: "timings"}) 31 | loop = main_loop_cls( 32 | model, user_cls, parametrizer_cls, settings 33 | ) 34 | log("FINISHED MAIN_LOOP CREATE", params={log_const.KEY_NAME: "timings"}) 35 | return loop 36 | -------------------------------------------------------------------------------- /smart_kit/template/wsgi_config.py-tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import os 4 | 5 | bind = '0.0.0.0:8000' 6 | workers = os.cpu_count() * 2 + 1 7 | preload_app = True 8 | 9 | 10 | # Server Hooks 11 | def on_starting(server): 12 | print("SERVER STARTING...") 13 | -------------------------------------------------------------------------------- /smart_kit/testing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/testing/__init__.py -------------------------------------------------------------------------------- /smart_kit/text_preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/text_preprocessing/__init__.py -------------------------------------------------------------------------------- /smart_kit/text_preprocessing/base_text_normalizer.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence, List 2 | 3 | from smart_kit.utils.cache import Cache 4 | 5 | 6 | class BaseTextNormalizer: 7 | TEXT_PARAM_NAME = "text" 8 | PREPROCESS_METHOD = "preprocess" 9 | CLASSIFY_METHOD = "classify" 10 | NORMALIZE_METHOD = "normalize" 11 | CACHE = Cache 12 | 13 | def load_everything(self) -> None: 14 | raise NotImplementedError 15 | 16 | def with_cache(self, *args, **kwargs) -> 'BaseTextNormalizer': 17 | raise NotImplementedError 18 | 19 | def normalize_sequence(self, texts: Sequence, batch_size) -> List: 20 | raise NotImplementedError 21 | 22 | def __call__(self, text: str): 23 | raise NotImplementedError 24 | -------------------------------------------------------------------------------- /smart_kit/text_preprocessing/remote.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | 4 | 5 | class BaseTextNormalizer: 6 | def __init__(self, text, context=None, message_type="text"): 7 | self.text = text 8 | self.context = context or [] 9 | self.message_type = message_type 10 | 11 | @classmethod 12 | def normalize(cls, original_text, *args, **kwargs): 13 | inst = cls(text=original_text) 14 | return inst.get_result(*args, **kwargs) 15 | 16 | def get_result(self, *args, **kwargs): 17 | raise NotImplementedError 18 | 19 | 20 | class HTTPTextNormalizer(BaseTextNormalizer): 21 | 22 | def get_result(self, address): 23 | response = self._get_response(address) 24 | return json.loads(response.text, encoding=response.encoding) 25 | 26 | def _get_response(self, address): 27 | response = requests.get(address, params={"original_text": self.text}) 28 | response.raise_for_status() 29 | return response 30 | -------------------------------------------------------------------------------- /smart_kit/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from core.names import field 2 | 3 | 4 | def get_callback_action_params(user): 5 | callback_id = user.message.callback_id 6 | action_params = user.behaviors.get_callback_action_params(callback_id) or {} 7 | return action_params 8 | 9 | 10 | def set_debug_info(app_name, action_params, error): 11 | saved_debug_info = action_params.setdefault(field.DEBUG_INFO, {}) 12 | dp_debug_info = saved_debug_info.setdefault(app_name, []) 13 | dp_debug_info.append({"error": error, "status": "{}_error".format(app_name)}) 14 | 15 | -------------------------------------------------------------------------------- /smart_kit/utils/cache.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | 4 | 5 | class ItemExpired(Exception): 6 | pass 7 | 8 | 9 | class Cache: 10 | def __init__(self, lifetime: float = 0): 11 | self.lifetime = lifetime 12 | self.storage = dict() 13 | 14 | def __getitem__(self, item): 15 | create_time, value = self.storage[item] 16 | if create_time + self.lifetime <= time.time(): 17 | raise ItemExpired 18 | return value 19 | 20 | def __setitem__(self, key, value): 21 | self.storage[key] = time.time(), value 22 | 23 | def load(self, *args, **kwargs): 24 | pass 25 | 26 | def save(self, *args, **kwargs): 27 | pass 28 | 29 | def invalidate(self): 30 | for key in list(self.storage): 31 | try: 32 | self[key] 33 | except ItemExpired: 34 | del self.storage[key] 35 | 36 | def clear(self): 37 | self.storage.clear() 38 | 39 | 40 | class JSONCache(Cache): # Pathetic Non OOP Design, Sorry 41 | def load(self, path): 42 | with open(path) as file: 43 | self.storage = json.load(file) 44 | 45 | def save(self, path, update=False): 46 | if update: 47 | tmp = JSONCache(lifetime=float("+inf")) 48 | tmp.load(path) 49 | tmp.storage.update(self.storage) 50 | self.storage = tmp.storage 51 | self.invalidate() 52 | with open(path, "w+", encoding='utf-8') as file: 53 | json.dump(self.storage, file, ensure_ascii=False) 54 | -------------------------------------------------------------------------------- /smart_kit/utils/logger_writer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/smart_kit/utils/logger_writer/__init__.py -------------------------------------------------------------------------------- /smart_kit/utils/logger_writer/logger_formatter.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from datetime import datetime 3 | import time 4 | 5 | from pythonjsonlogger import jsonlogger 6 | 7 | from core.model.factory import build_factory 8 | from core.model.registered import Registered 9 | 10 | loggers_formatter = Registered() 11 | 12 | loggers_formatter_factory = build_factory(loggers_formatter) 13 | 14 | 15 | class SmartKitJsonFormatter(jsonlogger.JsonFormatter): 16 | VERSION = 0 17 | DEV_TEAM = "NA" 18 | APPLICATION_NAME = "NA" 19 | 20 | def add_fields(self, log_record, record, message_dict): 21 | super(SmartKitJsonFormatter, self).add_fields(log_record, record, message_dict) 22 | dt = datetime.fromtimestamp(record.created) 23 | st = dt.strftime("%Y-%m-%dT%H:%M:%S") 24 | log_record['timestamp'] = "%s.%06d" % (st, record.msecs * 1000) 25 | log_record['version'] = self.VERSION 26 | log_record['team'] = self.DEV_TEAM 27 | log_record['application'] = self.APPLICATION_NAME 28 | if isinstance(record.args, dict): 29 | log_record['args'] = record.args -------------------------------------------------------------------------------- /smart_kit/utils/picklable_mock.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock, MagicMock 2 | 3 | 4 | class PicklableMock(Mock): 5 | def __reduce__(self): 6 | return Mock, () 7 | 8 | 9 | class PicklableMagicMock(MagicMock): 10 | def __reduce__(self): 11 | return MagicMock, () 12 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/basic_scenario_models_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/basic_scenario_models_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/basic_scenario_models_test/action_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/basic_scenario_models_test/action_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/basic_scenario_models_test/action_test/test_command.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from core.basic_models.actions.command import Command 4 | 5 | 6 | class TestCommand(TestCase): 7 | def test_1(self): 8 | expected = {"messageName": "my_name", "payload": {}} 9 | 10 | command = Command("my_name") 11 | result = command.raw 12 | 13 | self.assertDictEqual(expected, result) 14 | 15 | def test_2(self): 16 | expected = {"messageName": "my_name", "payload": {"id": 5}} 17 | 18 | command = Command("my_name", {"id": 5}) 19 | result = command.raw 20 | 21 | self.assertDictEqual(expected, result) 22 | -------------------------------------------------------------------------------- /tests/core_tests/basic_scenario_models_test/action_test/test_random_action.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from core.basic_models.actions.basic_actions import Action, action_factory, actions, DoingNothingAction, RandomAction 4 | from core.model.registered import registered_factories 5 | 6 | 7 | class TestRandomAction(TestCase): 8 | 9 | @classmethod 10 | def setUpClass(cls) -> None: 11 | registered_factories[Action] = action_factory 12 | actions["do_nothing"] = DoingNothingAction 13 | 14 | def test_1(self): 15 | 16 | items = { 17 | "actions": [ 18 | { 19 | "type": "do_nothing", 20 | "command": "ANSWER_TO_USER", 21 | "nodes": { 22 | "answer": "Доброе утро!", 23 | } 24 | }, 25 | { 26 | "type": "do_nothing", 27 | "command": "ANSWER_TO_USER", 28 | "nodes": { 29 | "answer": "Добрый вечер!", 30 | } 31 | } 32 | ] 33 | } 34 | action = RandomAction(items, 5) 35 | result = action.run(None, None) 36 | self.assertIsNotNone(result) 37 | 38 | def test_2(self): 39 | items = { 40 | "actions": [ 41 | { 42 | "type": "do_nothing", 43 | "command": "ANSWER_TO_USER", 44 | "nodes": { 45 | "answer": "Добрый вечер!", 46 | } 47 | } 48 | ] 49 | } 50 | action = RandomAction(items, 5) 51 | result = action.run(None, None) 52 | self.assertIsNotNone(result) 53 | -------------------------------------------------------------------------------- /tests/core_tests/basic_scenario_models_test/test_parametrizer.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import Mock 3 | 4 | from core.basic_models.parametrizers.parametrizer import BasicParametrizer 5 | from smart_kit.utils.picklable_mock import PicklableMock 6 | 7 | 8 | class ParametrizerTest(unittest.TestCase): 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | cls.user = Mock(message=PicklableMock()) 13 | 14 | def test_get_user_data(self): 15 | expected = ["message"] 16 | parametrizer = BasicParametrizer(self.user, {}) 17 | result = parametrizer._get_user_data(None) 18 | self.assertListEqual(expected, list(result.keys())) 19 | 20 | def test_collect(self): 21 | expected = ["message"] 22 | parametrizer = BasicParametrizer(self.user, {}) 23 | result = parametrizer._get_user_data(None) 24 | self.assertListEqual(expected, list(result.keys())) 25 | -------------------------------------------------------------------------------- /tests/core_tests/basic_scenario_models_test/test_variables.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from core.basic_models.variables.variables import Variables 5 | 6 | 7 | class VariablesTest(unittest.TestCase): 8 | def setUp(self) -> None: 9 | self.variables = Variables(None, None) 10 | 11 | def test_set(self): 12 | self.variables.set("key", "value") 13 | self.assertEqual(self.variables.values, {"key": "value"}) 14 | 15 | def test_get(self): 16 | self.variables.set("key", "value") 17 | self.assertEqual(self.variables.get("key"), "value") 18 | self.assertEqual(self.variables.get("nonexistkey", "defaultvalue"), "defaultvalue") 19 | 20 | def test_update(self): 21 | self.variables.set("key", "value") 22 | self.variables.update("key", "newvalue") 23 | self.assertEqual(self.variables.get("key"), "newvalue") 24 | 25 | def test_delete(self): 26 | self.variables.set("key", "value") 27 | self.variables.delete("key") 28 | self.assertEqual(self.variables.values, {}) 29 | 30 | def test_expire(self): 31 | self.variables.set("key", "value", ttl=0) 32 | self.assertEqual(self.variables.values, {}) 33 | 34 | def test_clear(self): 35 | self.variables.set("key_1", "value_1") 36 | self.variables.set("key_2", "value_2") 37 | self.variables.clear() 38 | self.assertEqual(self.variables.values, {}) 39 | -------------------------------------------------------------------------------- /tests/core_tests/configs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/configs/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/configs/test_global_constants.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from core.configs import global_constants 5 | 6 | 7 | class GlobalConstantsTest1(unittest.TestCase): 8 | def test_constants(self): 9 | self.assertIsNotNone(global_constants.CALLBACK_ID_HEADER) 10 | -------------------------------------------------------------------------------- /tests/core_tests/descriptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/descriptions/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/descriptions/test_smart_updatable_lazy_descriptions.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from core.descriptions.smart_updatable_descriptions_items import SmartUpdatableDescriptionsItems 4 | 5 | 6 | class MockFactory: 7 | def __init__(self, id, items): 8 | self.id = id 9 | self.data = items["data"] 10 | self.version = items["version"] 11 | 12 | 13 | class SmartUpdatableLazyDescriptionsTest(unittest.TestCase): 14 | def setUp(self): 15 | self.descr = SmartUpdatableDescriptionsItems(MockFactory, dict(id1={"data": "raw_data_value1", "version": 0}, 16 | id2={"data": "raw_data_value2", "version": 0})) 17 | 18 | def test_update_data1(self): 19 | update_item = dict(id1={"data": "raw_data_value3", "version": 1}) 20 | self.descr.update_data(update_item) 21 | expected = "raw_data_value3" 22 | obj1 = self.descr["id1"] 23 | self.assertEqual(obj1.data, expected) 24 | -------------------------------------------------------------------------------- /tests/core_tests/jinja_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/jinja_tests/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/masking_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/masking_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/model_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/model_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/model_test/test_lazy_items.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import Mock 3 | 4 | from core.model.lazy_items import LazyItems 5 | from smart_kit.utils.picklable_mock import PicklableMock 6 | 7 | 8 | class MockFactory: 9 | def __init__(self, id, items): 10 | self.id = id 11 | self.items = items 12 | 13 | 14 | class MockDescriptions: 15 | def __init__(self, factory, items): 16 | self.items = items 17 | 18 | def __contains__(self, key): 19 | return key in self.items 20 | 21 | 22 | class LazyItemsTest(unittest.TestCase): 23 | 24 | def test_clear_removed_items(self): 25 | raw_data = {} 26 | user = PicklableMock() 27 | description = PicklableMock() 28 | factory = Mock(raw_data, description, user) 29 | items = {"1": "test1", "2": "test2", "3": "test3"} 30 | raw_description = {"1": "test1", "2": "test2"} 31 | descriptions = MockDescriptions(factory, raw_description) 32 | items = LazyItems(items, descriptions, user, factory) 33 | self.assertEqual(items._raw_items, raw_description) 34 | -------------------------------------------------------------------------------- /tests/core_tests/model_test/test_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import unittest 4 | 5 | from core.model.field import Field 6 | from core.model.model import Model 7 | 8 | 9 | class MockField(): 10 | def __init__(self, value, description, *args): 11 | self.value = value 12 | self.descr = description 13 | 14 | @property 15 | def raw(self): 16 | return self.value 17 | 18 | def fill(self, origin_value): 19 | self.value = origin_value 20 | return True 21 | 22 | 23 | class MockModel(Model): 24 | @property 25 | def fields(self): 26 | return [Field("field1", MockField, "field1_descr")] 27 | 28 | 29 | class ModelTest(unittest.TestCase): 30 | def setUp(self): 31 | self.model = MockModel(values={"field1": "field1_data"}, user=None) 32 | 33 | def test_get_field(self): 34 | field1 = self.model.get_field("field1") 35 | assert field1.value, field1.descr == ("field1_data", "field1_descr") 36 | 37 | def test_get_field_error(self): 38 | exception = None 39 | try: 40 | self.model.get_field("field_unknown") 41 | except AttributeError: 42 | exception = sys.exc_info()[0] 43 | assert exception == AttributeError 44 | 45 | def test_raw(self): 46 | raw = self.model.raw 47 | assert raw == dict(field1="field1_data") 48 | 49 | 50 | if __name__ == '__main__': 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /tests/core_tests/monitoring_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/monitoring_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/mq_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/mq_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/operators_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/operators_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/operators_test/test_comparators.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from core.basic_models.operators.comparators import MoreComparator, \ 5 | LessComparator, MoreOrEqualComparator, LessOrEqualComparator, EqualComparator 6 | 7 | 8 | class ComparatorsTest(unittest.TestCase): 9 | def test_more_compare_sucsess(self): 10 | comparator = MoreComparator({}) 11 | assert comparator.compare(3, 1) 12 | 13 | def test_more_compare_fail(self): 14 | comparator = MoreComparator({}) 15 | assert not comparator.compare(3, 10) 16 | 17 | def test_less_compare_sucsess(self): 18 | comparator = LessComparator({}) 19 | assert comparator.compare(3, 10) 20 | 21 | def test_less_compare_fail(self): 22 | comparator = LessComparator({}) 23 | assert not comparator.compare(3, 1) 24 | 25 | def test_more_or_equal_sucsess(self): 26 | comparator = MoreOrEqualComparator({}) 27 | assert comparator.compare(1, 1) 28 | 29 | def test_more_or_equal_fail(self): 30 | comparator = MoreOrEqualComparator({}) 31 | assert not comparator.compare(1, 3) 32 | 33 | def test_less_or_equal_sucsess(self): 34 | comparator = LessOrEqualComparator({}) 35 | assert comparator.compare(1, 1) 36 | 37 | def test_less_or_equal_fail(self): 38 | comparator = LessOrEqualComparator({}) 39 | assert not comparator.compare(3, 1) 40 | 41 | def test_equal_sucsess(self): 42 | comparator = EqualComparator({}) 43 | assert comparator.compare(1, 1) 44 | 45 | def test_equal_fail(self): 46 | comparator = EqualComparator({}) 47 | assert not comparator.compare(1, 2) 48 | 49 | 50 | if __name__ == '__main__': 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /tests/core_tests/repositories_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/repositories_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/request_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/request_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/request_test/test_rest_request.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import MagicMock 3 | 4 | from core.request.rest_request import RestRequest 5 | from smart_kit.utils.picklable_mock import PicklableMock 6 | 7 | 8 | class RestRequestTest(unittest.TestCase): 9 | def setUp(self): 10 | self.expected = PicklableMock() 11 | 12 | def test_get(self): 13 | self.rr = RestRequest({"method": "get"}) 14 | self.rr._requests_get = MagicMock(return_value=self.expected) 15 | data = ["text"] 16 | result = self.rr.run(data) 17 | self.rr._requests_get.assert_called_once_with(data) 18 | self.assertEqual(result, self.expected) 19 | 20 | def test_post(self): 21 | self.rr = RestRequest({}) 22 | self.rr._requests_post = MagicMock(return_value=self.expected) 23 | data = ["text"] 24 | result = self.rr.run(data) 25 | self.rr._requests_post.assert_called_once_with(data) 26 | self.assertEqual(result, self.expected) 27 | 28 | def test_get_disabled(self): 29 | self.rr = RestRequest({"method": "get", "enabled": False}) 30 | self.rr._requests_get = MagicMock(return_value=self.expected) 31 | data = ["text"] 32 | result = self.rr.run(data) 33 | self.assertIsNone(result) 34 | 35 | def test_post_disabled(self): 36 | self.rr = RestRequest({"enabled": False}) 37 | self.rr._requests_post = MagicMock(return_value=self.expected) 38 | data = ["text"] 39 | result = self.rr.run(data) 40 | self.assertIsNone(result) 41 | -------------------------------------------------------------------------------- /tests/core_tests/requirements_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/requirements_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/test_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/test_utils/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/test_utils/test_delay_runner.py: -------------------------------------------------------------------------------- 1 | import time 2 | from unittest import TestCase 3 | 4 | from core.utils.delay_runner import DelayRunner 5 | from smart_kit.utils.picklable_mock import PicklableMock 6 | 7 | 8 | class TestDelayRunner(TestCase): 9 | def setUp(self): 10 | self.max_delay = 1 11 | self.max_delay_s = self.max_delay * 60 12 | self.delay_runner = DelayRunner(1) 13 | self.run = PicklableMock() 14 | self.arg = PicklableMock() 15 | self.delay_runner.schedule_run(self.run, [self.arg]) 16 | 17 | def test_set_run_time(self): 18 | self.assertEqual(self.delay_runner._run_item, self.run) 19 | self.assertListEqual(self.delay_runner._run_args, [self.arg]) 20 | self.assertTrue(0 < self.delay_runner._ts - time.time() < self.max_delay_s) 21 | 22 | def test_check_can_run(self): 23 | self.delay_runner._ts = self.delay_runner._ts - self.max_delay_s 24 | self.assertTrue(self.delay_runner.check_can_run()) 25 | 26 | def test_check_cant_run(self): 27 | self.delay_runner._ts = time.time() + self.max_delay_s 28 | self.assertFalse(self.delay_runner.check_can_run()) 29 | 30 | def test_run(self): 31 | self.delay_runner._ts = self.delay_runner._ts - self.max_delay_s 32 | self.delay_runner.run() 33 | self.run.assert_called_once_with(self.arg) 34 | self.assertLess(self.delay_runner._ts, time.time()) 35 | self.assertIsNone(self.delay_runner._run_args) 36 | self.assertIsNone(self.delay_runner._run_item) 37 | -------------------------------------------------------------------------------- /tests/core_tests/test_utils/test_logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from unittest import TestCase 3 | from unittest.mock import Mock 4 | 5 | from core.logging.logger_utils import log 6 | 7 | 8 | class TestLogger(TestCase): 9 | 10 | def test_escaping(self): 11 | # используем здесь мок, потому что ошибка 12 | # пишется в консоль, но не рейсится наружу 13 | fh = logging.StreamHandler() 14 | logging.root.handlers = [] 15 | logging.root.addHandler(fh) 16 | fh.handleError = Mock() 17 | 18 | log("%0", level="ERROR") 19 | self.assertEqual(fh.handleError.called, False) 20 | 21 | def test_render_params(self): 22 | fh = logging.StreamHandler() 23 | logging.root.handlers = [] 24 | logging.root.addHandler(fh) 25 | fh.stream.write = Mock() 26 | log("%(p)s %p %p", level="ERROR", params={'p': 'value'}) 27 | self.assertEqual(fh.stream.write.call_args[0][0], 'value %p %p\n') 28 | -------------------------------------------------------------------------------- /tests/core_tests/test_utils/test_time_check.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from core.utils.utils import time_check, current_time_ms 4 | 5 | 6 | class TestTimeCheck(TestCase): 7 | 8 | def test_true(self): 9 | time = current_time_ms() 10 | self.assertTrue(time_check(time, 1 * 1000)) 11 | 12 | def test_false(self): 13 | time = current_time_ms() - 40 * 1000 14 | self.assertFalse(time_check(time, 1 * 1000)) 15 | -------------------------------------------------------------------------------- /tests/core_tests/test_utils/test_timer.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from core.utils.stats_timer import StatsTimer 4 | 5 | 6 | class TestStatsTimer(TestCase): 7 | 8 | def test_1(self): 9 | timer = StatsTimer() 10 | with timer: 11 | a = 3 12 | 13 | self.assertTrue(timer.msecs > 0) 14 | -------------------------------------------------------------------------------- /tests/core_tests/unified_template_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/core_tests/unified_template_test/__init__.py -------------------------------------------------------------------------------- /tests/core_tests/unified_template_test/test_money2text.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from core.unified_template.currency2text import Money2Text 4 | 5 | 6 | class TestMoney2Text(TestCase): 7 | def setUp(self) -> None: 8 | self.money2text = Money2Text() 9 | 10 | def test_shows_currency_names(self): 11 | self.assertEqual('2 рубля', self.money2text(2, "RUR")) 12 | self.assertEqual('11 долларов США', self.money2text(11.0, 'USD')) 13 | self.assertEqual('12 евро', self.money2text(12, "EUR")) 14 | self.assertEqual('2 юаня', self.money2text(2, "CNY")) 15 | self.assertEqual('2 фунта стерлингов', self.money2text(2, "GBP")) 16 | 17 | def test_shows_diminutives(self): 18 | self.assertEqual('11 долларов США 11 центов', self.money2text(11.11111111, 'USD')) 19 | self.assertEqual('14 рублей 5 копеек', self.money2text(14.05, "RUR")) 20 | self.assertEqual('2 юаня 68 фынь', self.money2text(2.68, "CNY")) 21 | self.assertEqual('3 фунта стерлингов 40 пенсов', self.money2text(3.4, "GBP")) 22 | 23 | def test_can_inflect_special_cases(self): 24 | self.assertEqual('1 чешская крона', self.money2text(1, "CZK")) 25 | self.assertEqual('2 чешские кроны', self.money2text(2, "CZK")) 26 | self.assertEqual('5 чешских крон 50 геллеров', self.money2text(5.5, "CZK")) 27 | 28 | def test_shows_currency_code_if_currency_unknown(self): 29 | self.assertEqual('11 XXX', self.money2text(11, 'XXX')) 30 | -------------------------------------------------------------------------------- /tests/scenarios_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/actions_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/actions_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/behaviors_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/behaviors_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/behaviors_test/test_behavior_description.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from core.basic_models.actions.basic_actions import Action, actions, action_factory 5 | from core.model.registered import registered_factories 6 | from scenarios.behaviors.behavior_description import BehaviorDescription 7 | from smart_kit.utils.picklable_mock import PicklableMock 8 | 9 | 10 | class MockAction(Action): 11 | def run(self, user, text_preprocessing_result, params=None): 12 | return [] 13 | 14 | 15 | class TestAction: 16 | def __init__(self): 17 | self.id = '123-456-789' 18 | self.version = 21 # какая-то версия 19 | 20 | def run(self, a, b, c): 21 | return 123 # некий результат 22 | 23 | 24 | class BehaviorDescriptionTest(unittest.TestCase): 25 | 26 | def setUp(self): 27 | self.test_items = {"success_action": [TestAction()]} 28 | self.test_user = PicklableMock() 29 | self.test_user.settings = {"template_settings": {}} 30 | registered_factories[Action] = action_factory 31 | actions[None] = MockAction 32 | 33 | def test_create(self): 34 | behavior_description = BehaviorDescription({"success_action": None, "fail_action": None, "timeout": 10}) 35 | self.assertEqual(behavior_description.timeout(self.test_user), 10) 36 | self.assertIsInstance(behavior_description.success_action, Action) 37 | self.assertIsInstance(behavior_description.fail_action, Action) 38 | 39 | def test_behavior_description_init(self): 40 | obj = BehaviorDescription(self.test_items) 41 | self.assertTrue(obj.timeout(self.test_user) == 300) # значение из scenarios.behaviors.behavior_description -------------------------------------------------------------------------------- /tests/scenarios_tests/fields_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/fields_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/fields_test/test_fields.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from unittest import TestCase 4 | from unittest.mock import Mock 5 | 6 | from scenarios.scenario_models.field.fields import Fields 7 | from smart_kit.utils.picklable_mock import PicklableMock 8 | 9 | 10 | class TestFields(TestCase): 11 | 12 | def test_1(self): 13 | lifetime = 777 14 | descr1, descr2 = Mock(id="descr1"), Mock(id="descr2") 15 | items = {"descr1": PicklableMock(), "descr2": 2} 16 | descriptions = {"descr1": descr1, "descr2": descr2} 17 | 18 | factory = lambda descr, raw_data, user, lifetime: Mock(value=raw_data, lifetime=lifetime, description=descr) 19 | user = PicklableMock() 20 | fields = Fields(items, descriptions, user, factory, lifetime=lifetime) 21 | values = fields.values 22 | self.assertEqual(items, values) 23 | for field in fields: 24 | f = fields[field] 25 | self.assertEqual(lifetime, f.lifetime) 26 | -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/fillers/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/_trial_temp/_trial_marker: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/fillers/_trial_temp/_trial_marker -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/test_external_filler.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from unittest.mock import Mock 3 | 4 | from scenarios.scenario_models.field.field_filler_description import ExternalFieldFillerDescription 5 | from smart_kit.utils.picklable_mock import PicklableMock 6 | 7 | 8 | class TestExternalFieldFillerDescription(TestCase): 9 | 10 | def test_1(self): 11 | expected = 5 12 | items = {"filler": "my_key"} 13 | mock_filler = PicklableMock() 14 | mock_filler.run = Mock(return_value=expected) 15 | 16 | mock_user = PicklableMock() 17 | mock_user.descriptions = {"external_field_fillers": {"my_key": mock_filler}} 18 | 19 | filler = ExternalFieldFillerDescription(items) 20 | result = filler.extract(None, mock_user) 21 | 22 | self.assertEqual(expected, result) 23 | -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/test_first_meeting.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from scenarios.scenario_models.field.field_filler_description import FirstNumberFiller, \ 4 | FirstCurrencyFiller 5 | from smart_kit.utils.picklable_mock import PicklableMock 6 | 7 | 8 | class TestFirstNumberFiller(TestCase): 9 | def test_1(self): 10 | expected = "5" 11 | items = {} 12 | text_preprocessing_result = PicklableMock() 13 | text_preprocessing_result.num_token_values = [expected] 14 | 15 | filler = FirstNumberFiller(items) 16 | result = filler.extract(text_preprocessing_result, None) 17 | 18 | self.assertEqual(expected, result) 19 | 20 | def test_2(self): 21 | items = {} 22 | text_preprocessing_result = PicklableMock() 23 | text_preprocessing_result.num_token_values = [] 24 | 25 | filler = FirstNumberFiller(items) 26 | result = filler.extract(text_preprocessing_result, None) 27 | 28 | self.assertIsNone(result) 29 | 30 | 31 | class TestFirstCurrencyFiller(TestCase): 32 | def test_1(self): 33 | expected = "ru" 34 | items = {} 35 | text_preprocessing_result = PicklableMock() 36 | text_preprocessing_result.ccy_token_values = [expected] 37 | 38 | filler = FirstCurrencyFiller(items) 39 | result = filler.extract(text_preprocessing_result, None) 40 | 41 | self.assertEqual(expected, result) 42 | 43 | def test_2(self): 44 | items = {} 45 | text_preprocessing_result = PicklableMock() 46 | text_preprocessing_result.ccy_token_values = [] 47 | 48 | filler = FirstCurrencyFiller(items) 49 | result = filler.extract(text_preprocessing_result, None) 50 | 51 | self.assertIsNone(result) 52 | -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/test_geo_token_filler.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from scenarios.scenario_models.field.field_filler_description import FirstGeoFiller 4 | from smart_kit.utils.picklable_mock import PicklableMock 5 | 6 | 7 | class TestFirstGeoFiller(TestCase): 8 | def setUp(self): 9 | items = {} 10 | self.filler = FirstGeoFiller(items) 11 | 12 | def test_1(self): 13 | expected = "москва" 14 | 15 | text_preprocessing_result = PicklableMock() 16 | text_preprocessing_result.geo_token_values = ["москва"] 17 | result = self.filler.extract(text_preprocessing_result, None) 18 | 19 | self.assertEqual(expected, result) 20 | 21 | def test_2(self): 22 | expected = "москва" 23 | 24 | text_preprocessing_result = PicklableMock() 25 | text_preprocessing_result.geo_token_values = ["москва", "питер", "казань"] 26 | result = self.filler.extract(text_preprocessing_result, None) 27 | 28 | self.assertEqual(expected, result) 29 | 30 | def test_3(self): 31 | text_preprocessing_result = PicklableMock() 32 | text_preprocessing_result.geo_token_values = [] 33 | 34 | result = self.filler.extract(text_preprocessing_result, None) 35 | 36 | self.assertIsNone(result) 37 | -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/test_org_token_filler.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from scenarios.scenario_models.field.field_filler_description import FirstOrgFiller 4 | from smart_kit.utils.picklable_mock import PicklableMock 5 | 6 | 7 | class TestFirstOrgFiller(TestCase): 8 | def setUp(self): 9 | items = {} 10 | self.filler = FirstOrgFiller(items) 11 | 12 | def test_1(self): 13 | expected = "тинькофф" 14 | 15 | text_preprocessing_result = PicklableMock() 16 | text_preprocessing_result.org_token_values = ["тинькофф"] 17 | result = self.filler.extract(text_preprocessing_result, None) 18 | 19 | self.assertEqual(expected, result) 20 | 21 | def test_2(self): 22 | expected = "тинькофф" 23 | 24 | text_preprocessing_result = PicklableMock() 25 | text_preprocessing_result.org_token_values = ["тинькофф", "втб", "мегафон"] 26 | result = self.filler.extract(text_preprocessing_result, None) 27 | 28 | self.assertEqual(expected, result) 29 | 30 | def test_3(self): 31 | text_preprocessing_result = PicklableMock() 32 | text_preprocessing_result.org_token_values = [] 33 | 34 | result = self.filler.extract(text_preprocessing_result, None) 35 | 36 | self.assertIsNone(result) 37 | -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/test_person_filler.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from scenarios.scenario_models.field.field_filler_description import FirstPersonFiller 4 | from smart_kit.utils.picklable_mock import PicklableMock 5 | 6 | 7 | class TestFirstPersonFiller(TestCase): 8 | def setUp(self): 9 | items = {} 10 | self.filler = FirstPersonFiller(items) 11 | 12 | def test_1(self): 13 | expected = {"name": "иван"} 14 | 15 | text_preprocessing_result = PicklableMock() 16 | text_preprocessing_result.person_token_values = [{"name": "иван"}] 17 | result = self.filler.extract(text_preprocessing_result, None) 18 | 19 | self.assertDictEqual(expected, result) 20 | 21 | def test_2(self): 22 | expected = {"name": "иван"} 23 | 24 | text_preprocessing_result = PicklableMock() 25 | text_preprocessing_result.person_token_values = [{"name": "иван"}, {"name": "иван", "patronymic": "иванович"}] 26 | result = self.filler.extract(text_preprocessing_result, None) 27 | 28 | self.assertDictEqual(expected, result) 29 | 30 | def test_3(self): 31 | text_preprocessing_result = PicklableMock() 32 | text_preprocessing_result.person_token_values = [] 33 | 34 | result = self.filler.extract(text_preprocessing_result, None) 35 | 36 | self.assertIsNone(result) -------------------------------------------------------------------------------- /tests/scenarios_tests/fillers/test_previous_messages_filler.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from core.model.registered import registered_factories 4 | from scenarios.scenario_models.field.field_filler_description import FieldFillerDescription, PreviousMessagesFiller 5 | from scenarios.scenario_models.field.field_filler_description import field_filler_description, field_filler_factory 6 | from smart_kit.utils.picklable_mock import PicklableMock 7 | 8 | 9 | class MockFiller: 10 | def __init__(self, items=None): 11 | self.count = 0 12 | 13 | def extract(self, text_preprocessing_result, user, params): 14 | self.count += 1 15 | 16 | 17 | class PreviousMessagesFillerTest(unittest.TestCase): 18 | def test_fill_1(self): 19 | registered_factories[FieldFillerDescription] = field_filler_factory 20 | field_filler_description["mock_filler"] = MockFiller 21 | expected = "first" 22 | items = {"filler": {"type": "mock_filler", "result": expected}} 23 | user = PicklableMock() 24 | user.preprocessing_messages_for_scenarios = PicklableMock() 25 | user.preprocessing_messages_for_scenarios.processed_items = [{}, {}, {}] 26 | filler = PreviousMessagesFiller(items) 27 | filler.extract(None, user) 28 | self.assertEqual(filler.filler.count, 4) 29 | 30 | def test_fill_2(self): 31 | registered_factories[FieldFillerDescription] = field_filler_factory 32 | field_filler_description["mock_filler"] = MockFiller 33 | expected = "first" 34 | items = {"filler": {"type": "mock_filler", "result": expected}, "count": 2} 35 | user = PicklableMock() 36 | user.preprocessing_messages_for_scenarios = PicklableMock() 37 | user.preprocessing_messages_for_scenarios.processed_items = [{}, {}, {}] 38 | filler = PreviousMessagesFiller(items) 39 | filler.extract(None, user) 40 | self.assertEqual(filler.filler.count, 2) 41 | -------------------------------------------------------------------------------- /tests/scenarios_tests/forms_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/forms_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/preprocessing_messages_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/preprocessing_messages_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/preprocessing_messages_test/test_preprocessing_messages_description.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from scenarios.user.preprocessing_messages.preprocessing_messages_description import \ 4 | PreprocessingMessagesDescription 5 | 6 | 7 | class PreprocessingMessagesDescriptionTest(unittest.TestCase): 8 | 9 | def test_description(self): 10 | items = {"messages": 5, "lifetime": 600} 11 | description = PreprocessingMessagesDescription(items) 12 | self.assertEqual(description.max_message_count, 5) 13 | self.assertEqual(description.lifetime, 600) 14 | -------------------------------------------------------------------------------- /tests/scenarios_tests/requirements_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/requirements_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/scenario_models_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/scenario_models_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/scenarios_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/scenarios_test/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/user_models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/scenarios_tests/user_models/__init__.py -------------------------------------------------------------------------------- /tests/scenarios_tests/user_models/test_is_int_value.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from unittest import TestCase 3 | 4 | from scenarios.scenario_models.field_requirements.field_requirements import IsIntFieldRequirement 5 | 6 | 7 | class IsIntFieldRequirementTest(TestCase): 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | items = {} 12 | cls.requirement = IsIntFieldRequirement(items) 13 | 14 | def test_is_int_number_string(self): 15 | text = "123" 16 | self.assertTrue(self.requirement.check(text)) 17 | 18 | def test_is_int_float_string(self): 19 | text = "1.23" 20 | self.assertFalse(self.requirement.check(text)) 21 | 22 | def test_is_int_text_string(self): 23 | text = "test" 24 | self.assertFalse(self.requirement.check(text)) 25 | 26 | def test_is_int_empty_string(self): 27 | text = "" 28 | self.assertFalse(self.requirement.check(text)) 29 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/action/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/action/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/adapters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/adapters/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/configs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/configs/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/configs/test_logger_config.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from smart_kit.configs import logger_config 5 | 6 | 7 | class ConfigsTest1(unittest.TestCase): 8 | def test_LoggerConfig(self): 9 | obj = logger_config.LoggerConfig("./testing/data") 10 | self.assertTrue(obj.config_path == "./testing/data") 11 | self.assertTrue(obj.repositories[0].key == 'logging_config') 12 | self.assertTrue(obj.repositories[0].filename == "./testing/data/logging_config.yml") 13 | self.assertTrue(obj._subfolder == "./testing/data") 14 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/configs/test_settings.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | 5 | class SettingsTest1(unittest.TestCase): 6 | def test_Settings(self): 7 | pass 8 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/handlers/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/handlers/test_handle_server_action.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | from unittest.mock import Mock 4 | 5 | from smart_kit.handlers import handle_server_action 6 | 7 | 8 | class HandlersTest3(unittest.TestCase): 9 | def setUp(self): 10 | self.app_name = "TestAppName" 11 | self.test_payload_1 = {"server_action": {}} 12 | self.test_payload_2 = {"server_action": {"action_id": 1, "parameters": 1}} 13 | self.test_user = Mock('user') 14 | 15 | def test_handle_server_action_init(self): 16 | obj = handle_server_action.HandlerServerAction(self.app_name) 17 | self.assertTrue(obj.get_action_params(self.test_payload_1) == {}) 18 | self.assertIsNotNone(handle_server_action.SERVER_ACTION) 19 | 20 | def test_handle_server_action_get_action_name(self): 21 | obj = handle_server_action.HandlerServerAction(self.app_name) 22 | with self.assertRaises(KeyError): 23 | obj.get_action_name(self.test_payload_1, self.test_user) 24 | self.assertTrue(obj.get_action_name(self.test_payload_2, self.test_user) == 1) 25 | 26 | def test_handle_server_action_get_action_params(self): 27 | obj = handle_server_action.HandlerServerAction(self.app_name) 28 | self.assertTrue(obj.get_action_params(self.test_payload_1) == {}) 29 | self.assertTrue(obj.get_action_params(self.test_payload_2) == 1) 30 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/handlers/test_handler_base.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | from unittest.mock import Mock 4 | 5 | from smart_kit.handlers import handler_base 6 | 7 | 8 | class HandlersTest1(unittest.TestCase): 9 | def test_handler_base(self): 10 | self.test_user = Mock('user') 11 | self.test_user.message = Mock("messsage") 12 | self.test_user.message.message_name = "test" 13 | self.test_user.message.channel = "test_channel" 14 | self.test_user.message.device = Mock("device") 15 | self.test_user.message.device.surface = "test_surface" 16 | obj = handler_base.HandlerBase("TestAppName") 17 | self.assertIsNotNone(obj.TOPIC_KEY) 18 | self.assertIsNotNone(obj.KAFKA_KEY) 19 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/handlers/test_handler_timeout.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | from unittest.mock import Mock 4 | 5 | from smart_kit.handlers import handler_timeout 6 | from smart_kit.utils.picklable_mock import PicklableMock, PicklableMagicMock 7 | 8 | 9 | class HandlerTest2(unittest.TestCase): 10 | def setUp(self): 11 | self.app_name = "TastAppName" 12 | self.test_user = Mock('user') 13 | self.test_user.id = '123-345-678' 14 | self.test_user.message = Mock('message') 15 | self.test_user.message.callback_id = 11 16 | self.test_user.message.incremental_id = 22 17 | self.test_user.message.logging_uuid = '321-654-987' 18 | self.test_user.message.channel = "channel" 19 | self.test_user.message.message_name = "test" 20 | self.test_user.message.app_info = None 21 | self.test_user.message.device = PicklableMock() 22 | self.test_user.message.device.surface = "surface" 23 | 24 | self.test_user.behaviors = Mock('behaviors') 25 | self.test_user.behaviors.timeout = lambda x: 120 26 | self.test_user.behaviors.has_callback = lambda *x, **y: PicklableMagicMock() 27 | self.test_user.behaviors.get_callback_action_params = lambda *x, **y: {} 28 | self.test_payload = Mock('payload') 29 | 30 | def test_handler_timeout(self): 31 | obj = handler_timeout.HandlerTimeout(self.app_name) 32 | self.assertIsNotNone(obj.KAFKA_KEY) 33 | self.assertIsNotNone(handler_timeout.log_const.KEY_NAME) 34 | self.assertTrue(obj.run(self.test_payload, self.test_user) == 120) 35 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/management/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/message/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/message/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/message/test_app_info.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from smart_kit.message import app_info 5 | 6 | 7 | class MessageAppInfoTest1(unittest.TestCase): 8 | def test_app_info1(self): 9 | obj = app_info.AppInfo(dict()) 10 | self.assertIsNone(obj.project_id) 11 | self.assertIsNone(obj.application_id) 12 | self.assertIsNone(obj.app_version_id) 13 | self.assertIsNone(obj.frontend_endpoint) 14 | self.assertIsNone(obj.frontend_type) 15 | 16 | def test_app_info2(self): 17 | obj = app_info.AppInfo({"projectId": 1, "applicationId": 1, "appversionId": 1, "frontendEndpoint": 1, 18 | "frontendType": 1}) 19 | self.assertTrue(obj.project_id == 1) 20 | self.assertTrue(obj.application_id == 1) 21 | self.assertTrue(obj.app_version_id == 1) 22 | self.assertTrue(obj.frontend_endpoint == 1) 23 | self.assertTrue(obj.frontend_type == 1) 24 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/message/test_device.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from core.message import device 5 | 6 | 7 | class MessageDeviceTest1(unittest.TestCase): 8 | def test_device1(self): 9 | obj = device.Device(dict()) 10 | self.assertTrue(obj.value == {}) 11 | self.assertTrue(obj.platform_type == "") 12 | self.assertTrue(obj.platform_version == "") 13 | self.assertTrue(obj.surface == "") 14 | self.assertTrue(obj.surface_version == "") 15 | self.assertTrue(obj.features == {}) 16 | self.assertTrue(obj.capabilities == {}) 17 | self.assertTrue(obj.additional_info == {}) 18 | 19 | def test_device2(self): 20 | test_dict = {"platformType": "1", "platformVersion": "1", "surface": "1", "surfaceVersion": "1", 21 | "features": {1: 1}, "capabilities": {1: 1}, "additionalInfo": {1: 1}} 22 | obj = device.Device(test_dict) 23 | self.assertTrue(obj.value == test_dict) 24 | self.assertTrue(obj.platform_type == "1") 25 | self.assertTrue(obj.platform_version == "1") 26 | self.assertTrue(obj.surface == "1") 27 | self.assertTrue(obj.surface_version == "1") 28 | self.assertTrue(obj.features == {1: 1}) 29 | self.assertTrue(obj.capabilities == {1: 1}) 30 | self.assertTrue(obj.additional_info == {1: 1}) 31 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/message/test_smart_app_push_message.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import json 3 | import unittest 4 | from unittest.mock import Mock 5 | 6 | from smart_kit.message.smart_app_push_message import SmartAppPushToMessage 7 | 8 | 9 | class TestSmartAppPushMessage(unittest.TestCase): 10 | def setUp(self): 11 | self.command_ = Mock() 12 | self.request_ = Mock() 13 | self.message_ = Mock() 14 | self.command_.loader = "json.dumps" 15 | self.request_.header = "json" 16 | self.command_.payload = { 17 | "content": {"test_param": ""}, 18 | "surface": "some_surface", 19 | "project_id": "project_id", 20 | } 21 | self.message_.sub = 'sub' 22 | 23 | def test_smart_app_push_message_as_dict(self): 24 | obj = SmartAppPushToMessage(self.command_, self.message_, self.request_) 25 | self.assertEqual(obj.as_dict, { 26 | "content": {"test_param": ""}, 27 | "surface": "some_surface", 28 | "clientId": "sub", 29 | "projectId": "project_id" 30 | }) 31 | 32 | def test_smart_app_push_message_value(self): 33 | obj = SmartAppPushToMessage(self.command_, self.message_, self.request_) 34 | self.assertEqual(obj.value, json.dumps({ 35 | "projectId": "project_id", 36 | "clientId": "sub", 37 | "surface": "some_surface", 38 | "content": {"test_param": ""}, 39 | }, ensure_ascii=False)) 40 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/models/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/names/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/names/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/names/test_field.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from core.names import field 5 | 6 | 7 | class FieldTest1(unittest.TestCase): 8 | def test_fields(self): 9 | self.assertIsNotNone(field.PRONOUNCE_TEXT) 10 | self.assertIsNotNone(field.ITEMS) 11 | self.assertIsNotNone(field.MESSAGE_NAME) 12 | self.assertIsNotNone(field.AUTO_LISTENING) 13 | self.assertIsNotNone(field.FINISHED) 14 | self.assertIsNotNone(field.DEVICE) 15 | self.assertIsNotNone(field.SUB) 16 | self.assertIsNotNone(field.USER_ID) 17 | self.assertIsNotNone(field.USER_CHANNEL) 18 | self.assertIsNotNone(field.SMART_BIO) 19 | self.assertIsNotNone(field.SERVER_ACTION) 20 | self.assertIsNotNone(field.PROJECT_NAME) 21 | self.assertIsNotNone(field.INTENT) 22 | self.assertIsNotNone(field.INTENT_META) 23 | self.assertIsNotNone(field.APP_INFO) 24 | self.assertIsNotNone(field.PROJECT_ID) 25 | self.assertIsNotNone(field.APPLICATION_ID) 26 | self.assertIsNotNone(field.APP_VERSION_ID) 27 | self.assertIsNotNone(field.FRONTEND_ENDPOINT) 28 | self.assertIsNotNone(field.FRONTEND_TYPE) 29 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/names/test_manage_names.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from smart_kit.names import message_names 5 | 6 | 7 | class MessageNamesTest1(unittest.TestCase): 8 | def test_message_names(self): 9 | self.assertIsNotNone(message_names.ANSWER_TO_USER) 10 | self.assertIsNotNone(message_names.ERROR) 11 | self.assertIsNotNone(message_names.NOTHING_FOUND) 12 | self.assertIsNotNone(message_names.MESSAGE_TO_SKILL) 13 | self.assertIsNotNone(message_names.LOCAL_TIMEOUT) 14 | self.assertIsNotNone(message_names.RUN_APP) 15 | self.assertIsNotNone(message_names.CLOSE_APP) 16 | self.assertIsNotNone(message_names.SERVER_ACTION) 17 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/request/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/requirement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/requirement/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/system_answers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/system_answers/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/system_answers/test_nothing_found_action.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | from unittest.mock import Mock 4 | 5 | from core.basic_models.actions.command import Command 6 | from core.basic_models.actions.string_actions import StringAction 7 | from smart_kit.names.message_names import NOTHING_FOUND 8 | from smart_kit.system_answers import nothing_found_action 9 | 10 | 11 | class SystemAnswersTest1(unittest.TestCase): 12 | def setUp(self): 13 | self.test_command_1 = Mock('Command') 14 | self.test_id = '123-345-678' # пусть чему-то равняется 15 | self.test_items1 = {"123-ASDF": {"command": [], "no_empty_nodes": True}} 16 | self.test_text_preprocessing_result = Mock('text_preprocessing_result') 17 | self.test_user1 = Mock('user') 18 | self.test_user1.parametrizer = Mock('parametrizer') 19 | self.test_user1.parametrizer.collect = lambda x, filter_params: {'input': x, 'result': x} 20 | 21 | def test_system_answers_nothing_found_action_init(self): 22 | obj1 = nothing_found_action.NothingFoundAction() 23 | obj2 = nothing_found_action.NothingFoundAction(self.test_items1, self.test_id) 24 | self.assertIsNone(obj1.id) 25 | self.assertTrue(obj2.id == self.test_id) 26 | self.assertTrue(isinstance(obj1._action, StringAction)) 27 | self.assertTrue(obj1._action.command == NOTHING_FOUND) 28 | 29 | def test_system_answer_nothing_found_action_run(self): 30 | obj1 = nothing_found_action.NothingFoundAction() 31 | obj2 = nothing_found_action.NothingFoundAction(self.test_items1, self.test_id) 32 | self.assertTrue(isinstance(obj1.run(self.test_user1, self.test_text_preprocessing_result).pop(), Command)) 33 | self.assertTrue(isinstance(obj2.run(self.test_user1, self.test_text_preprocessing_result).pop(), Command)) 34 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/text_preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/text_preprocessing/__init__.py -------------------------------------------------------------------------------- /tests/smart_kit_tests/text_preprocessing/test_normalization.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import unittest 3 | 4 | from smart_kit.text_preprocessing import http_text_normalizer 5 | 6 | 7 | class TextPrerocessingTest2(unittest.TestCase): 8 | def setUp(self): 9 | self.test_text = 'Any text' 10 | self.test_context = ['Any context'] 11 | self.test_message_type = 'Any message' 12 | 13 | def test_normalization_words_tokenized_set(self): 14 | self.assertTrue(http_text_normalizer.words_tokenized_set('a s d f') == {'a', 's', 'd', 'f'}) 15 | self.assertTrue(http_text_normalizer.words_tokenized_set(' ') == {'', '', ''}) 16 | 17 | def test_normalization_print_current_state_init(self): 18 | obj1 = http_text_normalizer.HttpTextNormalizer("http://any-url", 111, 8, True) 19 | self.assertTrue(obj1.TEXT_PARAM_NAME == "text") 20 | self.assertTrue(obj1.NORMALIZE_METHOD == "normalize") 21 | self.assertTrue(obj1._preprocess_url == 'http://any-url/preprocess') 22 | self.assertTrue(obj1._classify_url == 'http://any-url/classify') 23 | self.assertTrue(obj1._normalize_url == 'http://any-url/normalize') 24 | self.assertTrue(obj1._timeout == 8) 25 | self.assertTrue(obj1._verbose) 26 | self.assertTrue(obj1._tqdm_func == http_text_normalizer.tqdm) 27 | 28 | def test_normalization_set_preprocess_mode(self): 29 | obj1 = http_text_normalizer.HttpTextNormalizer("http://any-url", 111, 8, True) 30 | obj2 = http_text_normalizer.HttpTextNormalizer("http://any-url", 111, 8, False) 31 | obj1.set_preprocess_mode() 32 | obj1.set_classify_mode() 33 | obj1.set_normalize_mode() 34 | obj2.set_preprocess_mode() 35 | obj2.set_classify_mode() 36 | obj2.set_normalize_mode() 37 | -------------------------------------------------------------------------------- /tests/smart_kit_tests/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sberdevices/smart_app_framework/57ed98fce3f7ff85c2f735485c365ef097216def/tests/smart_kit_tests/user/__init__.py --------------------------------------------------------------------------------