splight-lib 5.22.5.dev100__tar.gz → 5.23.0.dev0__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.
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/PKG-INFO +1 -1
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/pyproject.toml +1 -1
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v4/remote_client.py +2 -3
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/__init__.py +3 -3
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/__init__.py +19 -5
- splight_lib-5.23.0.dev0/splight_lib/models/_v4/datalake.py +209 -0
- splight_lib-5.23.0.dev0/splight_lib/models/_v4/datalake_base.py +141 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/native.py +62 -3
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/uv.lock +1 -1
- splight_lib-5.22.5.dev100/splight_lib/models/_v4/datalake.py +0 -168
- splight_lib-5.22.5.dev100/splight_lib/models/_v4/datalake_base.py +0 -114
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/code-style.yaml +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/developer.yaml +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/pre-release.yaml +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/release.yaml +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/check_dev_version.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/check_master_version.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/check_pypi_version.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/check_release_version.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/check_version_uploaded.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/cleanup_pypi.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.github/workflows/scripts/requirements.txt +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.gitignore +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/.pre-commit-config.yaml +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/DOCS.md +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/LICENSE.txt +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/Makefile +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/README.md +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/abstract/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/abstract/client.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/auth/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/auth/token.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/database/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/database/abstract.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/database/builder.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/database/classmap.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/database/remote_client.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/builder.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/common/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/common/abstract.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/common/buffer.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v3/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v3/builder.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v3/classmap.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v3/constants.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v3/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v3/remote_client.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v4/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v4/builder.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/datalake/v4/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/hub/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/hub/abstract.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/hub/client.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/tests/test_database.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/client/tests/test_datalake.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/component/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/component/abstract.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/component/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/component/spec.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/component/tests/test_spec.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/config.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/conftest.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/constants.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/encryption.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/engine.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/scheduling.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/task.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/tests/test_execution.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/tests/test_scheduling.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/execution/trigger.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/logging/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/logging/_internal.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/logging/component.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/logging/constants.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/logging/logging.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/logging/tests/test_logging.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/actions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/alert.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/asset.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/attribute.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/bus.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/component.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/dashboard.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/data_address.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/datalake.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/datalake_base.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/external_grid.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/file.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/function.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/generator.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/generic.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/grid.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/hub.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/hub_server.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/inverter.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/line.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/metadata.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/native.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/secret.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/segment.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/server.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/slack_generator.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/slack_line.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/tag.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/tests/models.json +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/tests/test_component_object_instance.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/tests/test_database_model.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/tests/test_metadata.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/tests/test_models.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/three_winding_transformer.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/transformer.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v3/variable_types.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/asset.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/attribute.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/base.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/battery.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/bus.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/component.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/data_address.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/external_grid.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/file.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/generator.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/generic.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/grid.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/hub.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/hub_server.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/line.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/load.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/metadata.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/secret.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/segment.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/server.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/slack_line.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/tag.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/transformer.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/_v4/variable_types.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/models/database.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/restclient/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/restclient/client.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/restclient/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/restclient/tests/test_restclient.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/restclient/types.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/server/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/server/exceptions.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/server/server.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/settings.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/testing/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/tests/FakeProc.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/tests/asset_geometries.json +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/tests/test_api_contracts.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/utils/__init__.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/utils/custom_model.py +0 -0
- {splight_lib-5.22.5.dev100 → splight_lib-5.23.0.dev0}/splight_lib/version.py +0 -0
|
@@ -26,7 +26,6 @@ class SyncRemoteDatalakeClient(AbstractDatalakeClient):
|
|
|
26
26
|
def __init__(
|
|
27
27
|
self,
|
|
28
28
|
base_url: str,
|
|
29
|
-
resource: str,
|
|
30
29
|
access_id: str,
|
|
31
30
|
secret_key: str,
|
|
32
31
|
api_version: SplightAPIVersion = SplightAPIVersion.V4,
|
|
@@ -35,7 +34,6 @@ class SyncRemoteDatalakeClient(AbstractDatalakeClient):
|
|
|
35
34
|
):
|
|
36
35
|
super().__init__()
|
|
37
36
|
self._base_url = furl(base_url)
|
|
38
|
-
self.resource = resource
|
|
39
37
|
token = SplightAuthToken(
|
|
40
38
|
access_key=access_id,
|
|
41
39
|
secret_key=secret_key,
|
|
@@ -86,7 +84,8 @@ class SyncRemoteDatalakeClient(AbstractDatalakeClient):
|
|
|
86
84
|
|
|
87
85
|
@property
|
|
88
86
|
def prefix(self) -> str:
|
|
89
|
-
return
|
|
87
|
+
return "v3/data/"
|
|
88
|
+
# return f"v4/data/{self.resource}"
|
|
90
89
|
|
|
91
90
|
|
|
92
91
|
class BufferedAsyncRemoteDatalakeClient(SyncRemoteDatalakeClient):
|
|
@@ -169,7 +169,7 @@ elif api_version == SplightAPIVersion.V4:
|
|
|
169
169
|
AssetKind,
|
|
170
170
|
AssetRelationship,
|
|
171
171
|
Attribute,
|
|
172
|
-
AttributeDocument,
|
|
172
|
+
# AttributeDocument,
|
|
173
173
|
AttributeType,
|
|
174
174
|
Battery,
|
|
175
175
|
Boolean,
|
|
@@ -196,8 +196,8 @@ elif api_version == SplightAPIVersion.V4:
|
|
|
196
196
|
Output,
|
|
197
197
|
Parameter,
|
|
198
198
|
PrivacyPolicy,
|
|
199
|
-
Query,
|
|
200
|
-
Records,
|
|
199
|
+
# Query,
|
|
200
|
+
# Records,
|
|
201
201
|
ResourceSummary,
|
|
202
202
|
Routine,
|
|
203
203
|
RoutineEvaluation,
|
|
@@ -28,10 +28,15 @@ from splight_lib.models._v4.component import (
|
|
|
28
28
|
)
|
|
29
29
|
from splight_lib.models._v4.data_address import DataAddresses as DataAddress
|
|
30
30
|
from splight_lib.models._v4.datalake import (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
DataRecords,
|
|
32
|
+
DataRequest,
|
|
33
|
+
PipelineStep,
|
|
34
|
+
Trace,
|
|
35
|
+
TraceType,
|
|
36
|
+
# AttributeDocument,
|
|
37
|
+
# Query,
|
|
38
|
+
# Records,
|
|
39
|
+
# SolutionOutputDocument,
|
|
35
40
|
)
|
|
36
41
|
from splight_lib.models._v4.datalake_base import SplightDatalakeBaseModel
|
|
37
42
|
from splight_lib.models._v4.external_grid import ExternalGrid
|
|
@@ -43,7 +48,12 @@ from splight_lib.models._v4.hub_server import HubServer
|
|
|
43
48
|
from splight_lib.models._v4.line import Line
|
|
44
49
|
from splight_lib.models._v4.load import Load
|
|
45
50
|
from splight_lib.models._v4.metadata import Metadata
|
|
46
|
-
from splight_lib.models._v4.native import
|
|
51
|
+
from splight_lib.models._v4.native import (
|
|
52
|
+
Boolean,
|
|
53
|
+
Number,
|
|
54
|
+
SolutionOutputDocument,
|
|
55
|
+
String,
|
|
56
|
+
)
|
|
47
57
|
from splight_lib.models._v4.secret import Secret
|
|
48
58
|
from splight_lib.models._v4.segment import Segment
|
|
49
59
|
from splight_lib.models._v4.server import Server
|
|
@@ -113,4 +123,8 @@ __all__ = [
|
|
|
113
123
|
"Segment",
|
|
114
124
|
"SlackLine",
|
|
115
125
|
"Transformer",
|
|
126
|
+
"SolutionOutputDocument",
|
|
127
|
+
"DataRequest",
|
|
128
|
+
"DataRecords",
|
|
129
|
+
"TraceType",
|
|
116
130
|
]
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from hashlib import sha256
|
|
4
|
+
from typing import Annotated, Any, Generator, Generic, Literal, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
|
|
7
|
+
from typing_extensions import Self
|
|
8
|
+
|
|
9
|
+
from splight_lib.client.datalake import DatalakeClientBuilder
|
|
10
|
+
from splight_lib.client.datalake.common.abstract import AbstractDatalakeClient
|
|
11
|
+
from splight_lib.client.datalake.v3.constants import StepName
|
|
12
|
+
from splight_lib.models._v3.asset import Asset
|
|
13
|
+
from splight_lib.models._v3.attribute import Attribute
|
|
14
|
+
from splight_lib.models._v3.exceptions import TraceAlreadyExistsError
|
|
15
|
+
from splight_lib.settings import (
|
|
16
|
+
SplightAPIVersion,
|
|
17
|
+
datalake_settings,
|
|
18
|
+
workspace_settings,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
MAX_NUM_TRACES = 500
|
|
22
|
+
T = TypeVar("T")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def hash(string: str) -> str:
|
|
26
|
+
return sha256(string.encode("utf-8")).hexdigest()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_datalake_client() -> AbstractDatalakeClient:
|
|
30
|
+
return DatalakeClientBuilder.build(
|
|
31
|
+
version=SplightAPIVersion.V4,
|
|
32
|
+
dl_client_type=datalake_settings.DL_CLIENT_TYPE,
|
|
33
|
+
parameters={
|
|
34
|
+
"base_url": workspace_settings.SPLIGHT_PLATFORM_API_HOST,
|
|
35
|
+
"access_id": workspace_settings.SPLIGHT_ACCESS_ID,
|
|
36
|
+
"secret_key": workspace_settings.SPLIGHT_SECRET_KEY,
|
|
37
|
+
"api_version": SplightAPIVersion.V4,
|
|
38
|
+
"buffer_size": datalake_settings.DL_BUFFER_SIZE,
|
|
39
|
+
"buffer_timeout": datalake_settings.DL_BUFFER_TIMEOUT,
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TraceType(str, Enum):
|
|
45
|
+
QUERY = "QUERY"
|
|
46
|
+
EXPRESSION = "EXPRESSION"
|
|
47
|
+
METADATA = "METADATA"
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def choices(cls):
|
|
51
|
+
return tuple((i.name, i.value) for i in cls)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PipelineStep(BaseModel):
|
|
55
|
+
name: StepName
|
|
56
|
+
operation: str | int | dict[str, Any]
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_dict(cls, step_dict: dict[str, Any]) -> Self:
|
|
60
|
+
(name, operation), *aux = step_dict.items()
|
|
61
|
+
return cls(name=name.lstrip("$"), operation=operation)
|
|
62
|
+
|
|
63
|
+
def to_step(self) -> dict[str, dict[str, Any]]:
|
|
64
|
+
return {f"${self.name.value}": self.operation}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Trace(BaseModel):
|
|
68
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
69
|
+
ref_id: str
|
|
70
|
+
type: TraceType = TraceType.QUERY
|
|
71
|
+
expression: dict | None = None
|
|
72
|
+
# TODO: Review if it should be list[PipelineStep]
|
|
73
|
+
pipeline: list[dict] = []
|
|
74
|
+
address: Annotated[dict, Field(exclude=True)]
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def from_address(
|
|
78
|
+
cls, asset: str | Asset, attribute: str | Attribute
|
|
79
|
+
) -> Self:
|
|
80
|
+
asset_id = asset.id if isinstance(asset, Asset) else asset
|
|
81
|
+
attribute_id = (
|
|
82
|
+
attribute.id if isinstance(attribute, Attribute) else attribute
|
|
83
|
+
)
|
|
84
|
+
return cls(
|
|
85
|
+
ref_id=hash(f"{asset_id}_{attribute_id}"),
|
|
86
|
+
type=TraceType.QUERY,
|
|
87
|
+
pipeline=[
|
|
88
|
+
PipelineStep(
|
|
89
|
+
name=StepName.MATCH,
|
|
90
|
+
operation={"asset": asset_id, "attribute": attribute_id},
|
|
91
|
+
).to_step()
|
|
92
|
+
],
|
|
93
|
+
address={"asset": asset_id, "attribute": attribute_id},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_so_filter(cls, asset: str, solution: str, output: str) -> Self:
|
|
98
|
+
return cls(
|
|
99
|
+
ref_id=hash(f"{asset}_{solution}_{output}"),
|
|
100
|
+
type=TraceType.QUERY,
|
|
101
|
+
pipeline=[
|
|
102
|
+
PipelineStep(
|
|
103
|
+
name=StepName.MATCH,
|
|
104
|
+
operation={
|
|
105
|
+
"asset": asset,
|
|
106
|
+
"solution": solution,
|
|
107
|
+
"output": output,
|
|
108
|
+
},
|
|
109
|
+
).to_step()
|
|
110
|
+
],
|
|
111
|
+
address={"asset": asset, "solution": solution, "output": output},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def add_step(self, step: PipelineStep) -> None:
|
|
115
|
+
self.pipeline.append(step.to_step())
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class DataRequest(Generic[T], BaseModel):
|
|
119
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
120
|
+
collection: str = "default"
|
|
121
|
+
sort_field: str = "timestamp"
|
|
122
|
+
sort_direction: Literal[-1, 1] = -1
|
|
123
|
+
limit: Annotated[int, Field(ge=1, le=10000)] = 10000
|
|
124
|
+
max_time_ms: Annotated[int, Field(ge=1, le=10000)] = 10000
|
|
125
|
+
from_timestamp: datetime | None = None
|
|
126
|
+
to_timestamp: datetime | None = None
|
|
127
|
+
traces: list[Trace] = []
|
|
128
|
+
|
|
129
|
+
_dl_client: AbstractDatalakeClient = PrivateAttr()
|
|
130
|
+
_traces_ref: dict[str, dict] = {}
|
|
131
|
+
|
|
132
|
+
def add_trace(self, trace: Trace) -> None:
|
|
133
|
+
if trace.ref_id in self._traces_ref:
|
|
134
|
+
raise TraceAlreadyExistsError(trace.ref_id)
|
|
135
|
+
self.traces.append(trace)
|
|
136
|
+
self._traces_ref.update({trace.ref_id: trace.address})
|
|
137
|
+
|
|
138
|
+
def as_pipeline(self) -> list[dict[str, Any]]:
|
|
139
|
+
return [step.to_step() for step in self.pipeline]
|
|
140
|
+
|
|
141
|
+
def apply(self) -> list[T]:
|
|
142
|
+
dl_client = get_datalake_client()
|
|
143
|
+
request = self.model_dump(mode="json")
|
|
144
|
+
traces = request.pop("traces")
|
|
145
|
+
data = []
|
|
146
|
+
for batch in chunk_list(traces, MAX_NUM_TRACES):
|
|
147
|
+
request["traces"] = batch
|
|
148
|
+
response = dl_client.get(request)
|
|
149
|
+
data.extend(self._parse_respose(response["results"]))
|
|
150
|
+
return data
|
|
151
|
+
|
|
152
|
+
async def async_apply(self) -> list[T]:
|
|
153
|
+
dl_client = get_datalake_client()
|
|
154
|
+
request = self.model_dump(mode="json")
|
|
155
|
+
traces = request.pop("traces")
|
|
156
|
+
data = []
|
|
157
|
+
for batch in chunk_list(traces, MAX_NUM_TRACES):
|
|
158
|
+
request["traces"] = batch
|
|
159
|
+
response = await dl_client.async_get(request)
|
|
160
|
+
data.extend(self._parse_respose(response["results"]))
|
|
161
|
+
return data
|
|
162
|
+
|
|
163
|
+
def _parse_respose(self, response: dict) -> list[T]:
|
|
164
|
+
model_class = self.__orig_class__.__args__[0]
|
|
165
|
+
data = []
|
|
166
|
+
for item in response:
|
|
167
|
+
timestamp = item.pop("timestamp")
|
|
168
|
+
values = [
|
|
169
|
+
model_class(
|
|
170
|
+
timestamp=timestamp, value=value, **self._traces_ref[key]
|
|
171
|
+
)
|
|
172
|
+
for key, value in item.items()
|
|
173
|
+
if value is not None
|
|
174
|
+
]
|
|
175
|
+
data.extend(values)
|
|
176
|
+
return data
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class DataRecords(BaseModel):
|
|
180
|
+
collection: str = "default"
|
|
181
|
+
records: list[dict[str, Any]] = []
|
|
182
|
+
|
|
183
|
+
def apply(self) -> None:
|
|
184
|
+
dl_client = get_datalake_client()
|
|
185
|
+
dl_client.save(self.model_dump(mode="json"))
|
|
186
|
+
|
|
187
|
+
async def async_apply(self) -> None:
|
|
188
|
+
dl_client = get_datalake_client()
|
|
189
|
+
await dl_client.async_save(self.model_dump(mode="json"))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def chunk_list(
|
|
193
|
+
datas: list[Any], chunksize: int
|
|
194
|
+
) -> Generator[list[Any], None, None]:
|
|
195
|
+
"""Split list into chunks
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
datas : list[Any]
|
|
200
|
+
the list of data
|
|
201
|
+
chunksize : int
|
|
202
|
+
the size of chunk
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
Generator with the chunks
|
|
207
|
+
"""
|
|
208
|
+
for i in range(0, len(datas), chunksize):
|
|
209
|
+
yield datas[i : i + chunksize]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from typing import Any, ClassVar, Dict, TypeVar
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
from splight_lib.models._v4.datalake import (
|
|
9
|
+
DataRecords,
|
|
10
|
+
DataRequest,
|
|
11
|
+
PipelineStep,
|
|
12
|
+
Trace,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SplightDatalakeBaseModel(BaseModel):
|
|
17
|
+
timestamp: datetime = Field(
|
|
18
|
+
default_factory=lambda: datetime.now(timezone.utc)
|
|
19
|
+
)
|
|
20
|
+
_collection_name: ClassVar[str] = "DatalakeModel"
|
|
21
|
+
_model_type: ClassVar[str] = ...
|
|
22
|
+
|
|
23
|
+
model_config = ConfigDict(extra="ignore")
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _get(
|
|
27
|
+
cls,
|
|
28
|
+
filters: dict[str, str],
|
|
29
|
+
extra_pipeline: list[dict[str, Any]] = [],
|
|
30
|
+
**params: dict,
|
|
31
|
+
) -> list[Self]:
|
|
32
|
+
request = _to_data_request(
|
|
33
|
+
cls,
|
|
34
|
+
filters,
|
|
35
|
+
extra_pipeline,
|
|
36
|
+
**params,
|
|
37
|
+
)
|
|
38
|
+
return request.apply()
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
async def _async_get(
|
|
42
|
+
cls,
|
|
43
|
+
filters: dict[str, str],
|
|
44
|
+
extra_pipeline: list[dict[str, Any]] = [],
|
|
45
|
+
**params: dict,
|
|
46
|
+
) -> list[Self]:
|
|
47
|
+
request = _to_data_request(cls, filters, extra_pipeline, **params)
|
|
48
|
+
instances = await request.async_apply()
|
|
49
|
+
return instances
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def _get_dataframe(
|
|
53
|
+
cls,
|
|
54
|
+
filters: dict[str, str],
|
|
55
|
+
extra_pipeline: list[dict[str, Any]] = [],
|
|
56
|
+
**params: dict,
|
|
57
|
+
) -> pd.DataFrame:
|
|
58
|
+
request = _to_data_request(
|
|
59
|
+
cls,
|
|
60
|
+
filters,
|
|
61
|
+
extra_pipeline,
|
|
62
|
+
**params,
|
|
63
|
+
)
|
|
64
|
+
instances = request.apply()
|
|
65
|
+
df = pd.DataFrame([instance.model_dump() for instance in instances])
|
|
66
|
+
if not df.empty:
|
|
67
|
+
df.index = df["timestamp"]
|
|
68
|
+
df.drop(columns="timestamp", inplace=True)
|
|
69
|
+
return df
|
|
70
|
+
|
|
71
|
+
def save(self) -> None:
|
|
72
|
+
records = self._to_record()
|
|
73
|
+
records.apply()
|
|
74
|
+
|
|
75
|
+
async def async_save(self) -> None:
|
|
76
|
+
records = self._to_record()
|
|
77
|
+
await records.async_apply()
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def save_dataframe(cls, df: pd.DataFrame):
|
|
81
|
+
df = _fix_dataframe_timestamp(df)
|
|
82
|
+
instances = df.to_dict("records")
|
|
83
|
+
records = DataRecords(
|
|
84
|
+
collection=cls._collection_name,
|
|
85
|
+
records=instances,
|
|
86
|
+
)
|
|
87
|
+
records.apply()
|
|
88
|
+
|
|
89
|
+
def dict(self, *args, **kwargs) -> Dict:
|
|
90
|
+
d = super().model_dump(*args, **kwargs)
|
|
91
|
+
return {
|
|
92
|
+
k: v["id"] if isinstance(v, dict) and "id" in v.keys() else v
|
|
93
|
+
for k, v in d.items()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def _to_record(self) -> DataRecords:
|
|
97
|
+
return DataRecords(
|
|
98
|
+
collection=self._collection_name,
|
|
99
|
+
records=[self.model_dump(mode="json")],
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _to_data_request(
|
|
104
|
+
model_class: TypeVar("T"),
|
|
105
|
+
filters: dict[str, str],
|
|
106
|
+
extra_pipeline: list[dict[str, Any]] = [],
|
|
107
|
+
**params: Dict,
|
|
108
|
+
) -> DataRequest:
|
|
109
|
+
if not isinstance(extra_pipeline, list):
|
|
110
|
+
raise ValueError("extra_pipeline must be a list of dicts")
|
|
111
|
+
model_type = model_class._model_type
|
|
112
|
+
collection = (
|
|
113
|
+
"default" if model_type == "attribute_document" else "solutions"
|
|
114
|
+
)
|
|
115
|
+
request = DataRequest[model_class](
|
|
116
|
+
collection=collection,
|
|
117
|
+
from_timestamp=params.get("from_timestamp"),
|
|
118
|
+
to_timestamp=params.get("to_timestamp"),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if model_type == "attribute_document":
|
|
122
|
+
trace = Trace.from_address(**filters)
|
|
123
|
+
elif model_type == "solution_output_document":
|
|
124
|
+
trace = Trace.from_so_filter(**filters)
|
|
125
|
+
for step in extra_pipeline:
|
|
126
|
+
trace.add_step(PipelineStep.from_dict(step))
|
|
127
|
+
|
|
128
|
+
request.add_trace(trace)
|
|
129
|
+
return request
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _fix_dataframe_timestamp(df: pd.DataFrame) -> pd.DataFrame:
|
|
133
|
+
if df["timestamp"][0].tz is None:
|
|
134
|
+
df["timestamp"] = df["timestamp"].apply(
|
|
135
|
+
lambda x: x.tz_localize(tz="UTC").strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
136
|
+
)
|
|
137
|
+
else:
|
|
138
|
+
df["timestamp"] = df["timestamp"].apply(
|
|
139
|
+
lambda x: x.tz_convert(tz="UTC").strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
140
|
+
)
|
|
141
|
+
return df
|
|
@@ -14,7 +14,9 @@ class NativeOutput(SplightDatalakeBaseModel):
|
|
|
14
14
|
asset: str | Asset
|
|
15
15
|
attribute: str | Attribute
|
|
16
16
|
output_format: str | None = None
|
|
17
|
+
_collection_name: ClassVar[Literal["default"]] = "default"
|
|
17
18
|
_output_format: ClassVar[str] = "default"
|
|
19
|
+
_model_type: ClassVar[str] = "attribute_document"
|
|
18
20
|
|
|
19
21
|
@field_validator("output_format", mode="before")
|
|
20
22
|
def set_output_format(cls, v) -> str:
|
|
@@ -24,19 +26,23 @@ class NativeOutput(SplightDatalakeBaseModel):
|
|
|
24
26
|
def get(
|
|
25
27
|
cls, asset: str | Asset, attribute: str | Attribute, **params: dict
|
|
26
28
|
) -> list[Self]:
|
|
27
|
-
return super().
|
|
29
|
+
return super()._get({"asset": asset, "attribute": attribute}, **params)
|
|
28
30
|
|
|
29
31
|
@classmethod
|
|
30
32
|
async def async_get(
|
|
31
33
|
cls, asset: str | Asset, attribute: str | Attribute, **params: dict
|
|
32
34
|
) -> list[Self]:
|
|
33
|
-
return await super().
|
|
35
|
+
return await super()._async_get(
|
|
36
|
+
{"asset": asset, "attribute": attribute}, **params
|
|
37
|
+
)
|
|
34
38
|
|
|
35
39
|
@classmethod
|
|
36
40
|
def get_dataframe(
|
|
37
41
|
cls, asset: str | Asset, attribute: str | Attribute, **params: dict
|
|
38
42
|
) -> pd.DataFrame:
|
|
39
|
-
df = super().
|
|
43
|
+
df = super()._get_dataframe(
|
|
44
|
+
{"asset": asset, "attribute": attribute}, **params
|
|
45
|
+
)
|
|
40
46
|
df["output_format"] = cls._output_format
|
|
41
47
|
return df
|
|
42
48
|
|
|
@@ -77,3 +83,56 @@ class Boolean(NativeOutput):
|
|
|
77
83
|
value: bool
|
|
78
84
|
output_format: Literal["Boolean"] = "Boolean"
|
|
79
85
|
_output_format: ClassVar[str] = "Boolean"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class SolutionOutputDocument(SplightDatalakeBaseModel):
|
|
89
|
+
asset: str | Asset
|
|
90
|
+
solution: str
|
|
91
|
+
output: str
|
|
92
|
+
value: bool | int | float | str
|
|
93
|
+
|
|
94
|
+
_collection_name: ClassVar[Literal["solutions"]] = "solutions"
|
|
95
|
+
_model_type: ClassVar[str] = "solution_output_document"
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def get(
|
|
99
|
+
cls, solution: str, output: str, asset: str, **params: dict
|
|
100
|
+
) -> list[Self]:
|
|
101
|
+
filters = {"solution": solution, "output": output, "asset": asset}
|
|
102
|
+
return super()._get(filters, **params)
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
async def async_get(
|
|
106
|
+
cls, solution: str, output: str, asset: str, **params: dict
|
|
107
|
+
) -> list[Self]:
|
|
108
|
+
filters = {"solution": solution, "output": output, "asset": asset}
|
|
109
|
+
return await super()._async_get(filters, **params)
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def get_dataframe(
|
|
113
|
+
cls, solution: str, output: str, asset: str, **params: dict
|
|
114
|
+
) -> pd.DataFrame:
|
|
115
|
+
filters = {"solution": solution, "output": output, "asset": asset}
|
|
116
|
+
return super()._get_dataframe(filters, **params)
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def latest(
|
|
120
|
+
cls,
|
|
121
|
+
solution: str,
|
|
122
|
+
output: str,
|
|
123
|
+
asset: str,
|
|
124
|
+
expiration: timedelta | None = None,
|
|
125
|
+
) -> Self:
|
|
126
|
+
from_timestamp = None
|
|
127
|
+
if expiration:
|
|
128
|
+
from_timestamp = datetime.now(timezone.utc) - expiration
|
|
129
|
+
result = cls.get(
|
|
130
|
+
solution=solution,
|
|
131
|
+
output=output,
|
|
132
|
+
asset=asset,
|
|
133
|
+
from_timestamp=from_timestamp,
|
|
134
|
+
limit=1,
|
|
135
|
+
)
|
|
136
|
+
if result:
|
|
137
|
+
return result[0]
|
|
138
|
+
return None
|