component-local 0.0.1b3800__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: component-local
3
+ Version: 0.0.1b3800
4
+ Summary: PyPI component-local Python Package owned by Circlez.ai
5
+ Home-page: https://github.com/circles-zone/component-main-local-python-restapi-serverless-com
6
+ Author: Circles
7
+ Author-email: info@circlez.ai
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: python-sdk-remote
12
+ Requires-Dist: logger-local
13
+ Requires-Dist: database-mysql-local
14
+ Requires-Dist: smart-datastore-local
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: requires-dist
22
+ Dynamic: summary
23
+
24
+ PyPI component-local Python Package owned by Circlez.ai
25
+ JIRA Work Item: https://circles-zone.atlassian.net/browse/BU-3800
26
+ GHA: https://github.com/circles-zone/component-main-local-python-restapi-serverless-com/actions
@@ -0,0 +1,46 @@
1
+ # General
2
+
3
+ component-local is a Python package which returns the `component_id` of a component, from the JSON file of the component or from `component.component_table`.<br>
4
+
5
+ Unlike the README.md in the root directory, this is a README.md of this specific repo/package<br>
6
+
7
+ # Usage
8
+
9
+ ```python
10
+ from component_local.src.component_local import ComponentLocal
11
+
12
+ component_local = ComponentLocal()
13
+
14
+ # 1st priority: the componentId in the JSON file (see component.json.example), 2nd priority: component.component_table
15
+ component_id = component_local.get_component_id("event", json_path="component.json")
16
+
17
+ # Only from component.component_table, returns None if there is no such component
18
+ component_id = component_local.get_component_id_by_name("event")
19
+
20
+ # Only from the JSON file, returns None if the component or its componentId are not in the file
21
+ component_id = ComponentLocal.get_component_id_from_json("event", "component.json")
22
+ ```
23
+
24
+ # TODO
25
+
26
+ - Add tests which use the real component.component_table with is_test_data (they need the RDS environment variables)<br>
27
+ - Wrap the lookup with a script so it can also be used from TypeScript and PowerShell<br>
28
+ - Add serverless.com, as planned in the repo description<br>
29
+
30
+ # Versions
31
+
32
+ [pub] 0.0.1b3800 Fix the template placeholders and add ComponentLocal.get_component_id (BU-3800)<br>
33
+
34
+ # Installing instructions
35
+
36
+ It's advised to use venv<br>
37
+ If you have Windows, it's also advised to use Windows Subsystem for Linux<br>
38
+
39
+ ## Install dependencies
40
+
41
+ Run pip install -r requirements.txt from the bash terminal<br>
42
+ Run pip install -r requirements-dev.txt from the bash terminal to run the tests<br>
43
+
44
+ ## Environment variables
45
+
46
+ Copy .env.play1.example to .env and fill in the values (.env is in .gitignore)<br>
File without changes
@@ -0,0 +1,54 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Optional, Union
4
+
5
+ from database_mysql_local.generic_crud import GenericCRUD
6
+ from logger_local.MetaLogger import MetaLogger
7
+
8
+ from .constants_src_component_local import ConstantsSrcComponent
9
+
10
+
11
+ # TODO When reading components.json5, We should get the componentId from component_table using a function wrapped with a script in component-main-local-python-package (so we can also use it in TypeScript and Powershell) # noqa E501
12
+ class ComponentLocal(GenericCRUD, metaclass=MetaLogger,
13
+ object=ConstantsSrcComponent.COMPONENT_LOCAL_PYTHON_CODE_LOGGER_OBJECT): # noqa E501
14
+ def __init__(self, is_test_data: bool = False) -> None:
15
+ GenericCRUD.__init__(
16
+ self,
17
+ default_schema_name=ConstantsSrcComponent.COMPONENT_SCHEMA_NAME,
18
+ default_table_name=ConstantsSrcComponent.COMPONENT_TABLE_NAME,
19
+ default_view_table_name=ConstantsSrcComponent.COMPONENT_VIEW_NAME,
20
+ default_column_name=ConstantsSrcComponent.COMPONENT_COLUMN_NAME,
21
+ is_test_data=is_test_data)
22
+
23
+ def get_component_id(self, component_name: str,
24
+ json_path: Union[str, Path, None] = None) -> Optional[int]:
25
+ """Returns the component_id of component_name.
26
+ 1st priority: the componentId in the JSON file (if json_path was given and it has the component).
27
+ 2nd priority: the component_id from component.component_table."""
28
+ if json_path is not None:
29
+ component_id = self.get_component_id_from_json(component_name=component_name, json_path=json_path)
30
+ if component_id is not None:
31
+ return component_id
32
+ return self.get_component_id_by_name(component_name=component_name)
33
+
34
+ def get_component_id_by_name(self, component_name: str) -> Optional[int]:
35
+ """Returns the component_id with this name in component.component_table, None if there is no such component.
36
+ If more than one component has this name, the one with the lowest component_id is returned."""
37
+ if not component_name:
38
+ raise ValueError("component_name is required")
39
+ component_id = self.select_one_value_by_column_and_value(
40
+ select_clause_value=ConstantsSrcComponent.COMPONENT_COLUMN_NAME,
41
+ column_name="name", column_value=component_name,
42
+ order_by=ConstantsSrcComponent.COMPONENT_COLUMN_NAME)
43
+ return component_id
44
+
45
+ @staticmethod
46
+ def get_component_id_from_json(component_name: str, json_path: Union[str, Path]) -> Optional[int]:
47
+ """Returns the componentId of component_name from a JSON file in the format of component.json.example,
48
+ None if the component or its componentId are not in the file."""
49
+ with open(json_path, encoding="utf-8") as json_file:
50
+ components = json.load(json_file)
51
+ component = components.get(component_name)
52
+ if not isinstance(component, dict):
53
+ return None
54
+ return component.get("componentId")
@@ -0,0 +1,26 @@
1
+ from typing import Any
2
+ from logger_local.LoggerComponentEnum import LoggerComponentEnum
3
+
4
+
5
+ class ConstantsSrcComponent:
6
+ """This is a class of all the constants of Component"""
7
+
8
+ COMPONENT_LOCAL_PYTHON_DEVELOPER_EMAIL_ADDRESS: str = 'shirlee.g@circ.zone'
9
+
10
+ # The CODE component_id of component-local in component.component_table
11
+ COMPONENT_LOCAL_PYTHON_CODE_COMPONENT_ID: int = 306
12
+
13
+ COMPONENT_LOCAL_PYTHON_COMPONENT_NAME = 'Component local Python package'
14
+ COMPONENT_LOCAL_PYTHON_CODE_LOGGER_OBJECT: dict[str, Any] = {
15
+ 'component_id': COMPONENT_LOCAL_PYTHON_CODE_COMPONENT_ID,
16
+ 'component_name': COMPONENT_LOCAL_PYTHON_COMPONENT_NAME,
17
+ 'component_category': LoggerComponentEnum.ComponentCategory.Code.value,
18
+ 'developer_email_address': COMPONENT_LOCAL_PYTHON_DEVELOPER_EMAIL_ADDRESS # noqa E501
19
+ }
20
+
21
+ UNKNOWN_COMPONENT_ID = 0
22
+
23
+ COMPONENT_SCHEMA_NAME = 'component'
24
+ COMPONENT_TABLE_NAME = 'component_table'
25
+ COMPONENT_VIEW_NAME = 'component_view'
26
+ COMPONENT_COLUMN_NAME = 'component_id'
@@ -0,0 +1,107 @@
1
+ # So we can run debugger without launch.json in VSCode
2
+ from dotenv import load_dotenv
3
+ load_dotenv() # Load environment variables before any imports that need them
4
+
5
+ import json
6
+ import sys
7
+ from unittest.mock import MagicMock
8
+
9
+ import pytest
10
+
11
+ from logger_local.LoggerLocal import Logger
12
+
13
+ from ..src.component_local import ComponentLocal
14
+ from ..src.constants_src_component_local import ConstantsSrcComponent
15
+ from .constants_tests_component_local import ComponentTestsConstants
16
+
17
+ logger = Logger.create_logger(object=ComponentTestsConstants.COMPONENT_LOCAL_PYTHON_TEST_LOGGER_OBJECT) # noqa E501
18
+
19
+ # Local test values, not real component ids (the real ones are in component.component_table)
20
+ EVENT_COMPONENT_ID = 247
21
+ UNKNOWN_COMPONENT_NAME = "no-such-component"
22
+
23
+
24
+ @pytest.fixture(name="component_local")
25
+ def component_local_fixture() -> ComponentLocal:
26
+ # Skips __init__ so these tests don't need a database connection
27
+ component_local = ComponentLocal.__new__(ComponentLocal)
28
+ component_local.select_one_value_by_column_and_value = MagicMock(return_value=EVENT_COMPONENT_ID)
29
+ return component_local
30
+
31
+
32
+ @pytest.fixture(name="json_path")
33
+ def json_path_fixture(tmp_path):
34
+ path = tmp_path / "component.json"
35
+ path.write_text(json.dumps({
36
+ "event": {"componentId": EVENT_COMPONENT_ID, "environmentName": "local"},
37
+ "group": {"environmentName": "local", "port": 3001}}), encoding="utf-8")
38
+ return path
39
+
40
+
41
+ def test_component_table_constants():
42
+ logger.start("test component table constants")
43
+ assert ConstantsSrcComponent.COMPONENT_SCHEMA_NAME == 'component'
44
+ assert ConstantsSrcComponent.COMPONENT_TABLE_NAME == 'component_table'
45
+ assert ConstantsSrcComponent.COMPONENT_COLUMN_NAME == 'component_id'
46
+ logger.end("test successful")
47
+
48
+
49
+ def test_get_component_id_from_json(json_path):
50
+ logger.start("test get_component_id_from_json")
51
+ assert ComponentLocal.get_component_id_from_json("event", json_path) == EVENT_COMPONENT_ID
52
+ logger.end("test successful")
53
+
54
+
55
+ def test_get_component_id_from_json_without_component_id(json_path):
56
+ logger.start("test get_component_id_from_json without componentId")
57
+ assert ComponentLocal.get_component_id_from_json("group", json_path) is None
58
+ logger.end("test successful")
59
+
60
+
61
+ def test_get_component_id_from_json_unknown_component(json_path):
62
+ logger.start("test get_component_id_from_json unknown component")
63
+ assert ComponentLocal.get_component_id_from_json(UNKNOWN_COMPONENT_NAME, json_path) is None
64
+ logger.end("test successful")
65
+
66
+
67
+ def test_get_component_id_by_name(component_local):
68
+ logger.start("test get_component_id_by_name")
69
+ assert component_local.get_component_id_by_name("event") == EVENT_COMPONENT_ID
70
+ component_local.select_one_value_by_column_and_value.assert_called_once_with(
71
+ select_clause_value='component_id', column_name='name', column_value="event", order_by='component_id')
72
+ logger.end("test successful")
73
+
74
+
75
+ def test_get_component_id_by_name_empty_name(component_local):
76
+ logger.start("test get_component_id_by_name empty name")
77
+ with pytest.raises(ValueError):
78
+ component_local.get_component_id_by_name("")
79
+ logger.end("test successful")
80
+
81
+
82
+ def test_get_component_id_prefers_json(component_local, json_path):
83
+ logger.start("test get_component_id prefers the JSON")
84
+ assert component_local.get_component_id("event", json_path=json_path) == EVENT_COMPONENT_ID
85
+ component_local.select_one_value_by_column_and_value.assert_not_called()
86
+ logger.end("test successful")
87
+
88
+
89
+ def test_get_component_id_falls_back_to_database(component_local, json_path):
90
+ logger.start("test get_component_id falls back to the database")
91
+ assert component_local.get_component_id("group", json_path=json_path) == EVENT_COMPONENT_ID
92
+ component_local.select_one_value_by_column_and_value.assert_called_once()
93
+ logger.end("test successful")
94
+
95
+
96
+ def test_get_component_id_unknown_component(component_local):
97
+ logger.start("test get_component_id unknown component")
98
+ component_local.select_one_value_by_column_and_value.return_value = None
99
+ assert component_local.get_component_id(UNKNOWN_COMPONENT_NAME) is None
100
+ logger.end("test successful")
101
+
102
+
103
+ # If we use only a call to some_test(), this can cause unexpected behavior, for example
104
+ # it can cause the test to be run multiple times simultaneously. This is why we use the following
105
+ # Which cause the test to be run only once with pytest:
106
+ if __name__ == "__main__":
107
+ pytest.main(sys.argv[1:])
@@ -0,0 +1,17 @@
1
+ from logger_local.LoggerComponentEnum import LoggerComponentEnum
2
+
3
+
4
+ class ComponentTestsConstants:
5
+ # The TEST component_id of component-local in component.component_table
6
+ COMPONENT_TEST_COMPONENT_ID: int = 307
7
+ COMPONENT_TEST_COMPONENT_NAME = "Component Tests"
8
+ COMPONENT_DEVELOPER_EMAIL_ADDRESS = "shirlee.g@circ.zone"
9
+
10
+ COMPONENT_LOCAL_PYTHON_TEST_LOGGER_OBJECT = {
11
+ # At this point of time we do not support camelCase in logger-local
12
+ 'component_id': COMPONENT_TEST_COMPONENT_ID,
13
+ 'component_name': COMPONENT_TEST_COMPONENT_NAME,
14
+ 'component_category': LoggerComponentEnum.ComponentCategory.Unit_Test.value, # noqa E501
15
+ 'testing_framework': LoggerComponentEnum.testingFramework.pytest.value,
16
+ 'developer_email_address': COMPONENT_DEVELOPER_EMAIL_ADDRESS
17
+ }
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: component-local
3
+ Version: 0.0.1b3800
4
+ Summary: PyPI component-local Python Package owned by Circlez.ai
5
+ Home-page: https://github.com/circles-zone/component-main-local-python-restapi-serverless-com
6
+ Author: Circles
7
+ Author-email: info@circlez.ai
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: python-sdk-remote
12
+ Requires-Dist: logger-local
13
+ Requires-Dist: database-mysql-local
14
+ Requires-Dist: smart-datastore-local
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: requires-dist
22
+ Dynamic: summary
23
+
24
+ PyPI component-local Python Package owned by Circlez.ai
25
+ JIRA Work Item: https://circles-zone.atlassian.net/browse/BU-3800
26
+ GHA: https://github.com/circles-zone/component-main-local-python-restapi-serverless-com/actions
@@ -0,0 +1,15 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ component_local/__init__.py
5
+ component_local.egg-info/PKG-INFO
6
+ component_local.egg-info/SOURCES.txt
7
+ component_local.egg-info/dependency_links.txt
8
+ component_local.egg-info/requires.txt
9
+ component_local.egg-info/top_level.txt
10
+ component_local/src/__init__.py
11
+ component_local/src/component_local.py
12
+ component_local/src/constants_src_component_local.py
13
+ component_local/tests/__init__.py
14
+ component_local/tests/component_local_test.py
15
+ component_local/tests/constants_tests_component_local.py
@@ -0,0 +1,4 @@
1
+ python-sdk-remote
2
+ logger-local
3
+ database-mysql-local
4
+ smart-datastore-local
@@ -0,0 +1 @@
1
+ component_local
@@ -0,0 +1,29 @@
1
+ # This file should be in the future instead of setup.py
2
+ # https://python-poetry.org/docs/pyproject
3
+ # https://stackoverflow.com/questions/78048223/adding-folder-with-data-with-pyproject-toml
4
+ # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license
5
+
6
+ # This file is mandatory for the `poetry version patch`
7
+
8
+ # It seems we need to copy this file also to serverless-com repo, as required by dialog-workflow-python-package
9
+
10
+ [build-system]
11
+ requires = ["setuptools>=61.0"]
12
+ build-backend = "setuptools.build_meta"
13
+
14
+ [tool.pytest.ini_options]
15
+ pythonpath = ["."]
16
+
17
+ [tool.poetry]
18
+ name = "component-local"
19
+ # I believe we are still using the version from setup.py and not from here until Potery will work
20
+ version = "0.0.1" # https://pypi.org/project/component-local/
21
+ description = "component-local Python Package"
22
+ readme = "README.md"
23
+ authors = [
24
+ "Circlez.ai <info@circlez.ai>",
25
+ ]
26
+
27
+ [tool.poetry.dev-dependencies]
28
+ pytest = "^8.0"
29
+ pytest-cov = "^5.0"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,73 @@
1
+ # serverless.com repo "python -m build" works without setup.py
2
+ # Should update package_dir if not using our recommended directory structure
3
+
4
+ import setuptools
5
+
6
+ # TODO: Change the PACKAGE_NAME to the package's name - Either xxx-local or
7
+ # xxx-remote (without the -python-package suffix). Only lowercase, no
8
+ # underlines.
9
+ # Used by pypa/gh-action-pypi-publish
10
+ # Package Name should be identical to the inner directory name
11
+ # Changing the package name here, will cause a change in the package directory
12
+ # name as well
13
+ # PACKAGE_NAME should be singular if handling only one instance
14
+ # PACKAGE_NAME should not include the word "main"
15
+
16
+ # e.g.: queue-local, without python-package suffix
17
+ PACKAGE_NAME = "component-local"
18
+
19
+ package_dir = PACKAGE_NAME.replace("-", "_")
20
+ # If we need backward-compatible:
21
+ # old_package_dir = "old_package_name"
22
+
23
+ PACKAGE_DESCRIPTION = f"PyPI {PACKAGE_NAME} Python Package owned by Circlez.ai"
24
+ NEW_LINE = "\n"
25
+
26
+ setuptools.setup(
27
+ name=PACKAGE_NAME,
28
+ # Increase this number every time you make a change you
29
+ # want to publish. After 0.0.9 switch to 0.0.10 and not 0.1.0
30
+ # version can't be 0.0.
31
+ # If you find yourself increasing the version in more than one patch number (it might indicate there are multiple branches which you should submit their PRs before your PR) # noqa: E501
32
+ version='0.0.1b3800', # https://pypi.org/project/component-local/
33
+ author="Circles",
34
+ author_email="info@circlez.ai",
35
+ description=f"{PACKAGE_DESCRIPTION}",
36
+ # long_description=f"{description}",
37
+ long_description =f"{PACKAGE_DESCRIPTION}" + NEW_LINE # noqa E501
38
+ + "JIRA Work Item: https://circles-zone.atlassian.net/browse/BU-3800" + NEW_LINE # noqa E501
39
+ + "GHA: https://github.com/circles-zone/component-main-local-python-restapi-serverless-com/actions", # noqa E501
40
+
41
+ long_description_content_type='text/markdown',
42
+ url="https://github.com/circles-zone/component-main-local-python-restapi-serverless-com", # noqa E501
43
+ # packages=[package_dir],
44
+ # packages=[package_dir, old_package_dir],
45
+ packages=setuptools.find_packages(),
46
+
47
+ # I'm not sure we need package_dir
48
+ # package_dir={package_dir: f'{package_dir}/src'},
49
+ # TODO Unfortunately in event-main-local-restapi there are no
50
+ # repo-directory and no package directory (flat directory structure)
51
+ # package_dir={package_dir: f'src'},
52
+
53
+ # package_dir={package_dir: f'{package_dir}/src',
54
+ # old_package_dir: f'{package_dir}/src'},
55
+ package_data={package_dir: ['*.py']},
56
+ classifiers=[
57
+ "Programming Language :: Python :: 3",
58
+ # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license
59
+ # TODO Fix all repos, especially the Python templates
60
+ # "License :: MIT AND (Apache-2.0 OR BSD-2-Clause)",
61
+ "Operating System :: OS Independent",
62
+ ],
63
+ # Do not add packages needed only for tests i.e. packages for get_test_xxx(
64
+ # TODO: Update which packages to include with this package in production
65
+ # (dependencies) - Not for development/testing
66
+ install_requires=[
67
+ 'python-sdk-remote',
68
+ 'logger-local',
69
+ 'database-mysql-local',
70
+ 'smart-datastore-local',
71
+ # 'logzio-python-handler>=4.1.2', # https://pypi.org/project/logzio-python-handler/ # noqa E501
72
+ ]
73
+ )