clarity-api-sdk-python 0.3.38__tar.gz → 0.4.0__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.
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/PKG-INFO +1 -1
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/pyproject.toml +1 -1
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/clarity_api_sdk_python.egg-info/PKG-INFO +1 -1
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/clarity_api_sdk_python.egg-info/SOURCES.txt +2 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/sonar_wiz_api.py +104 -4
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/sonar_wiz_async_api.py +108 -4
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/cli/main.py +31 -6
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/raw_file_device_mapping.py +43 -2
- clarity_api_sdk_python-0.4.0/src/cti/model/user_layer.py +125 -0
- clarity_api_sdk_python-0.4.0/tests/test_raw_file_device_mapping_model.py +110 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/tests/test_sdk_async_methods.py +78 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/tests/test_sdk_methods.py +55 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/README.md +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/setup.cfg +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/clarity_api_sdk_python.egg-info/dependency_links.txt +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/clarity_api_sdk_python.egg-info/entry_points.txt +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/clarity_api_sdk_python.egg-info/requires.txt +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/clarity_api_sdk_python.egg-info/top_level.txt +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/__init__.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/__init__.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/async_client.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/client.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/session.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/cli/__init__.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/cli/__main__.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/cli/client.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/logger/__init__.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/logger/logger.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/main.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/main_api.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/__init__.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/altitude_source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/attitude_source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/deferred_object_deletion.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/depth_source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/device.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/device_type.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/final_product.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/hierarchy.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/layback_algorithm.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/layback_source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/layback_type.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/organization.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/platform.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/platform_type.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/position_source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/processing_job.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/processing_log.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/project.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/projection_option.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/raw_file.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/raw_file_configuration.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/raw_file_state.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/s3.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/sidescan_ping_source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/source.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/survey.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/target.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/tow_system.py +0 -0
- {clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clarity-api-sdk-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A Python SDK to connect to the CTI Clarity API server.
|
|
5
5
|
Author-email: "Chesapeake Technology Inc." <support@chesapeaketech.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/chesapeake-tech/clarity-api-sdk-python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clarity-api-sdk-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A Python SDK to connect to the CTI Clarity API server.
|
|
5
5
|
Author-email: "Chesapeake Technology Inc." <support@chesapeaketech.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/chesapeake-tech/clarity-api-sdk-python
|
|
@@ -51,6 +51,8 @@ src/cti/model/source.py
|
|
|
51
51
|
src/cti/model/survey.py
|
|
52
52
|
src/cti/model/target.py
|
|
53
53
|
src/cti/model/tow_system.py
|
|
54
|
+
src/cti/model/user_layer.py
|
|
54
55
|
tests/test_cli.py
|
|
56
|
+
tests/test_raw_file_device_mapping_model.py
|
|
55
57
|
tests/test_sdk_async_methods.py
|
|
56
58
|
tests/test_sdk_methods.py
|
|
@@ -99,6 +99,10 @@ from cti.model.target import TargetUpdate
|
|
|
99
99
|
from cti.model.tow_system import TowSystem
|
|
100
100
|
from cti.model.tow_system import TowSystemCreate
|
|
101
101
|
from cti.model.tow_system import TowSystemUpdate
|
|
102
|
+
from cti.model.user_layer import UserLayer
|
|
103
|
+
from cti.model.user_layer import UserLayerCreate
|
|
104
|
+
from cti.model.user_layer import UserLayerUpdate
|
|
105
|
+
from cti.model.user_layer import UserLayerWithUpload
|
|
102
106
|
|
|
103
107
|
from .client import ClarityApiClient
|
|
104
108
|
|
|
@@ -1975,13 +1979,34 @@ class SonarWizApi:
|
|
|
1975
1979
|
response.raise_for_status()
|
|
1976
1980
|
return ProcessingLog.model_validate(response.json())
|
|
1977
1981
|
|
|
1978
|
-
def list_processing_logs(
|
|
1979
|
-
|
|
1982
|
+
def list_processing_logs(
|
|
1983
|
+
self,
|
|
1984
|
+
*,
|
|
1985
|
+
raw_file_id: UUID | str | None = None,
|
|
1986
|
+
survey_id: UUID | str | None = None,
|
|
1987
|
+
processing_step: str | None = None,
|
|
1988
|
+
) -> list[ProcessingLog]:
|
|
1989
|
+
"""List processing logs, optionally filtered.
|
|
1990
|
+
|
|
1991
|
+
Filters compose: each provided filter narrows the result set.
|
|
1992
|
+
|
|
1993
|
+
Args:
|
|
1994
|
+
raw_file_id: Match only logs for this raw file.
|
|
1995
|
+
survey_id: Match only logs for this survey.
|
|
1996
|
+
processing_step: Match only logs with this processing step
|
|
1997
|
+
(e.g. ``"scan"``, ``"ingest"``, ``"products"``).
|
|
1980
1998
|
|
|
1981
1999
|
Returns:
|
|
1982
|
-
List of processing
|
|
2000
|
+
List of matching processing logs (empty list if none match).
|
|
1983
2001
|
"""
|
|
1984
|
-
|
|
2002
|
+
params: dict[str, str] = {}
|
|
2003
|
+
if raw_file_id is not None:
|
|
2004
|
+
params["raw_file_id"] = str(raw_file_id)
|
|
2005
|
+
if survey_id is not None:
|
|
2006
|
+
params["survey_id"] = str(survey_id)
|
|
2007
|
+
if processing_step is not None:
|
|
2008
|
+
params["processing_step"] = processing_step
|
|
2009
|
+
response = self._client.get("/api/v1/processing-logs", params=params or None)
|
|
1985
2010
|
response.raise_for_status()
|
|
1986
2011
|
return [ProcessingLog.model_validate(item) for item in response.json()]
|
|
1987
2012
|
|
|
@@ -2004,6 +2029,81 @@ class SonarWizApi:
|
|
|
2004
2029
|
response.raise_for_status()
|
|
2005
2030
|
return ProcessingLog.model_validate(response.json())
|
|
2006
2031
|
|
|
2032
|
+
# ── User Layers ──────────────────────────────────────────────────────
|
|
2033
|
+
|
|
2034
|
+
def create_user_layer(self, user_layer: UserLayerCreate) -> UserLayerWithUpload:
|
|
2035
|
+
"""Create a new user layer and initiate upload.
|
|
2036
|
+
|
|
2037
|
+
Args:
|
|
2038
|
+
user_layer: User layer creation data.
|
|
2039
|
+
|
|
2040
|
+
Returns:
|
|
2041
|
+
Created user layer instance with upload information.
|
|
2042
|
+
"""
|
|
2043
|
+
response = self._client.post(
|
|
2044
|
+
"/api/v1/user-layers/upload",
|
|
2045
|
+
json=user_layer.model_dump(mode="json"),
|
|
2046
|
+
)
|
|
2047
|
+
response.raise_for_status()
|
|
2048
|
+
return UserLayerWithUpload.model_validate(response.json())
|
|
2049
|
+
|
|
2050
|
+
def get_user_layer(self, user_layer_id: UUID) -> UserLayer:
|
|
2051
|
+
"""Fetch a user layer by ID.
|
|
2052
|
+
|
|
2053
|
+
Args:
|
|
2054
|
+
user_layer_id: User layer UUID to fetch.
|
|
2055
|
+
|
|
2056
|
+
Returns:
|
|
2057
|
+
UserLayer instance.
|
|
2058
|
+
"""
|
|
2059
|
+
response = self._client.get(f"/api/v1/user-layers/{user_layer_id}")
|
|
2060
|
+
response.raise_for_status()
|
|
2061
|
+
return UserLayer.model_validate(response.json())
|
|
2062
|
+
|
|
2063
|
+
def list_user_layers(self, project_id: UUID) -> list[UserLayer]:
|
|
2064
|
+
"""List all user layers for a project.
|
|
2065
|
+
|
|
2066
|
+
Args:
|
|
2067
|
+
project_id: Project UUID to filter by.
|
|
2068
|
+
|
|
2069
|
+
Returns:
|
|
2070
|
+
List of user layer instances.
|
|
2071
|
+
"""
|
|
2072
|
+
response = self._client.get(
|
|
2073
|
+
"/api/v1/user-layers",
|
|
2074
|
+
params={"project_id": str(project_id)},
|
|
2075
|
+
)
|
|
2076
|
+
response.raise_for_status()
|
|
2077
|
+
return [UserLayer.model_validate(item) for item in response.json()]
|
|
2078
|
+
|
|
2079
|
+
def update_user_layer(
|
|
2080
|
+
self, user_layer_id: UUID, user_layer: UserLayerUpdate
|
|
2081
|
+
) -> UserLayer:
|
|
2082
|
+
"""Update a user layer.
|
|
2083
|
+
|
|
2084
|
+
Args:
|
|
2085
|
+
user_layer_id: User layer UUID to update.
|
|
2086
|
+
user_layer: User layer update data.
|
|
2087
|
+
|
|
2088
|
+
Returns:
|
|
2089
|
+
Updated user layer instance.
|
|
2090
|
+
"""
|
|
2091
|
+
response = self._client.patch(
|
|
2092
|
+
f"/api/v1/user-layers/{user_layer_id}",
|
|
2093
|
+
json=user_layer.model_dump(mode="json", exclude_none=True),
|
|
2094
|
+
)
|
|
2095
|
+
response.raise_for_status()
|
|
2096
|
+
return UserLayer.model_validate(response.json())
|
|
2097
|
+
|
|
2098
|
+
def delete_user_layer(self, user_layer_id: UUID) -> None:
|
|
2099
|
+
"""Delete a user layer.
|
|
2100
|
+
|
|
2101
|
+
Args:
|
|
2102
|
+
user_layer_id: User layer UUID to delete.
|
|
2103
|
+
"""
|
|
2104
|
+
response = self._client.delete(f"/api/v1/user-layers/{user_layer_id}")
|
|
2105
|
+
response.raise_for_status()
|
|
2106
|
+
|
|
2007
2107
|
# ── Processing Jobs ──────────────────────────────────────────────────
|
|
2008
2108
|
|
|
2009
2109
|
def create_job(self, job_data: ProcessingJobCreate) -> ProcessingJob:
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/api/sonar_wiz_async_api.py
RENAMED
|
@@ -97,6 +97,10 @@ from cti.model.target import TargetUpdate
|
|
|
97
97
|
from cti.model.tow_system import TowSystem
|
|
98
98
|
from cti.model.tow_system import TowSystemCreate
|
|
99
99
|
from cti.model.tow_system import TowSystemUpdate
|
|
100
|
+
from cti.model.user_layer import UserLayer
|
|
101
|
+
from cti.model.user_layer import UserLayerCreate
|
|
102
|
+
from cti.model.user_layer import UserLayerUpdate
|
|
103
|
+
from cti.model.user_layer import UserLayerWithUpload
|
|
100
104
|
|
|
101
105
|
from .async_client import ClarityApiAsyncClient
|
|
102
106
|
|
|
@@ -2023,13 +2027,36 @@ class SonarWizAsyncApi:
|
|
|
2023
2027
|
response.raise_for_status()
|
|
2024
2028
|
return ProcessingLog.model_validate(response.json())
|
|
2025
2029
|
|
|
2026
|
-
async def list_processing_logs(
|
|
2027
|
-
|
|
2030
|
+
async def list_processing_logs(
|
|
2031
|
+
self,
|
|
2032
|
+
*,
|
|
2033
|
+
raw_file_id: UUID | str | None = None,
|
|
2034
|
+
survey_id: UUID | str | None = None,
|
|
2035
|
+
processing_step: str | None = None,
|
|
2036
|
+
) -> list[ProcessingLog]:
|
|
2037
|
+
"""List processing logs, optionally filtered.
|
|
2038
|
+
|
|
2039
|
+
Filters compose: each provided filter narrows the result set.
|
|
2040
|
+
|
|
2041
|
+
Args:
|
|
2042
|
+
raw_file_id: Match only logs for this raw file.
|
|
2043
|
+
survey_id: Match only logs for this survey.
|
|
2044
|
+
processing_step: Match only logs with this processing step
|
|
2045
|
+
(e.g. ``"scan"``, ``"ingest"``, ``"products"``).
|
|
2028
2046
|
|
|
2029
2047
|
Returns:
|
|
2030
|
-
List of processing
|
|
2048
|
+
List of matching processing logs (empty list if none match).
|
|
2031
2049
|
"""
|
|
2032
|
-
|
|
2050
|
+
params: dict[str, str] = {}
|
|
2051
|
+
if raw_file_id is not None:
|
|
2052
|
+
params["raw_file_id"] = str(raw_file_id)
|
|
2053
|
+
if survey_id is not None:
|
|
2054
|
+
params["survey_id"] = str(survey_id)
|
|
2055
|
+
if processing_step is not None:
|
|
2056
|
+
params["processing_step"] = processing_step
|
|
2057
|
+
response = await self._client.get(
|
|
2058
|
+
"/api/v1/processing-logs", params=params or None
|
|
2059
|
+
)
|
|
2033
2060
|
response.raise_for_status()
|
|
2034
2061
|
return [ProcessingLog.model_validate(item) for item in response.json()]
|
|
2035
2062
|
|
|
@@ -2052,6 +2079,83 @@ class SonarWizAsyncApi:
|
|
|
2052
2079
|
response.raise_for_status()
|
|
2053
2080
|
return ProcessingLog.model_validate(response.json())
|
|
2054
2081
|
|
|
2082
|
+
# ── User Layers ──────────────────────────────────────────────────────
|
|
2083
|
+
|
|
2084
|
+
async def create_user_layer(
|
|
2085
|
+
self, user_layer: UserLayerCreate
|
|
2086
|
+
) -> UserLayerWithUpload:
|
|
2087
|
+
"""Create a new user layer and initiate upload.
|
|
2088
|
+
|
|
2089
|
+
Args:
|
|
2090
|
+
user_layer: User layer creation data.
|
|
2091
|
+
|
|
2092
|
+
Returns:
|
|
2093
|
+
Created user layer instance with upload information.
|
|
2094
|
+
"""
|
|
2095
|
+
response = await self._client.post(
|
|
2096
|
+
"/api/v1/user-layers/upload",
|
|
2097
|
+
json=user_layer.model_dump(mode="json"),
|
|
2098
|
+
)
|
|
2099
|
+
response.raise_for_status()
|
|
2100
|
+
return UserLayerWithUpload.model_validate(response.json())
|
|
2101
|
+
|
|
2102
|
+
async def get_user_layer(self, user_layer_id: UUID) -> UserLayer:
|
|
2103
|
+
"""Fetch a user layer by ID.
|
|
2104
|
+
|
|
2105
|
+
Args:
|
|
2106
|
+
user_layer_id: User layer UUID to fetch.
|
|
2107
|
+
|
|
2108
|
+
Returns:
|
|
2109
|
+
UserLayer instance.
|
|
2110
|
+
"""
|
|
2111
|
+
response = await self._client.get(f"/api/v1/user-layers/{user_layer_id}")
|
|
2112
|
+
response.raise_for_status()
|
|
2113
|
+
return UserLayer.model_validate(response.json())
|
|
2114
|
+
|
|
2115
|
+
async def list_user_layers(self, project_id: UUID) -> list[UserLayer]:
|
|
2116
|
+
"""List all user layers for a project.
|
|
2117
|
+
|
|
2118
|
+
Args:
|
|
2119
|
+
project_id: Project UUID to filter by.
|
|
2120
|
+
|
|
2121
|
+
Returns:
|
|
2122
|
+
List of user layer instances.
|
|
2123
|
+
"""
|
|
2124
|
+
response = await self._client.get(
|
|
2125
|
+
"/api/v1/user-layers",
|
|
2126
|
+
params={"project_id": str(project_id)},
|
|
2127
|
+
)
|
|
2128
|
+
response.raise_for_status()
|
|
2129
|
+
return [UserLayer.model_validate(item) for item in response.json()]
|
|
2130
|
+
|
|
2131
|
+
async def update_user_layer(
|
|
2132
|
+
self, user_layer_id: UUID, user_layer: UserLayerUpdate
|
|
2133
|
+
) -> UserLayer:
|
|
2134
|
+
"""Update a user layer.
|
|
2135
|
+
|
|
2136
|
+
Args:
|
|
2137
|
+
user_layer_id: User layer UUID to update.
|
|
2138
|
+
user_layer: User layer update data.
|
|
2139
|
+
|
|
2140
|
+
Returns:
|
|
2141
|
+
Updated user layer instance.
|
|
2142
|
+
"""
|
|
2143
|
+
response = await self._client.patch(
|
|
2144
|
+
f"/api/v1/user-layers/{user_layer_id}",
|
|
2145
|
+
json=user_layer.model_dump(mode="json", exclude_none=True),
|
|
2146
|
+
)
|
|
2147
|
+
response.raise_for_status()
|
|
2148
|
+
return UserLayer.model_validate(response.json())
|
|
2149
|
+
|
|
2150
|
+
async def delete_user_layer(self, user_layer_id: UUID) -> None:
|
|
2151
|
+
"""Delete a user layer.
|
|
2152
|
+
|
|
2153
|
+
Args:
|
|
2154
|
+
user_layer_id: User layer UUID to delete.
|
|
2155
|
+
"""
|
|
2156
|
+
response = await self._client.delete(f"/api/v1/user-layers/{user_layer_id}")
|
|
2157
|
+
response.raise_for_status()
|
|
2158
|
+
|
|
2055
2159
|
# ── Processing Jobs ──────────────────────────────────────────────────
|
|
2056
2160
|
|
|
2057
2161
|
async def create_job(self, job_data: ProcessingJobCreate) -> ProcessingJob:
|
|
@@ -59,10 +59,14 @@ def upload(
|
|
|
59
59
|
False, "--no-mappings", help="Skip automatic device mapping creation"
|
|
60
60
|
),
|
|
61
61
|
sidescan_stream: str = typer.Option(
|
|
62
|
-
"
|
|
62
|
+
"xtf://sidescan(source=ping_header,pair=0)",
|
|
63
|
+
"--sidescan-stream",
|
|
64
|
+
help="Source URI for the sidescan stream identifier",
|
|
63
65
|
),
|
|
64
66
|
gnss_stream: str = typer.Option(
|
|
65
|
-
"
|
|
67
|
+
"xtf://position(source=ping_header,description=ship_position)",
|
|
68
|
+
"--gnss-stream",
|
|
69
|
+
help="Source URI for the GNSS stream identifier",
|
|
66
70
|
),
|
|
67
71
|
output_json: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
68
72
|
) -> None:
|
|
@@ -71,7 +75,22 @@ def upload(
|
|
|
71
75
|
After uploading, automatically creates device mappings linking the file
|
|
72
76
|
to the survey's sidescan and GNSS devices. Use --no-mappings to skip.
|
|
73
77
|
"""
|
|
74
|
-
from cti.model.raw_file_device_mapping import
|
|
78
|
+
from cti.model.raw_file_device_mapping import (
|
|
79
|
+
RawFileDeviceMappingCreate,
|
|
80
|
+
SourceSelection,
|
|
81
|
+
StreamIdentifier,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def _identifier_from_uri(uri: str, roles: tuple[str, ...]) -> StreamIdentifier:
|
|
85
|
+
return StreamIdentifier(
|
|
86
|
+
source=SourceSelection(available=[uri], selected=uri),
|
|
87
|
+
fields_by_source={
|
|
88
|
+
uri: {
|
|
89
|
+
role: SourceSelection(available=[role], selected=role)
|
|
90
|
+
for role in roles
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
)
|
|
75
94
|
|
|
76
95
|
if not file.exists():
|
|
77
96
|
typer.echo(f"Error: file not found: {file}", err=True)
|
|
@@ -115,9 +134,13 @@ def upload(
|
|
|
115
134
|
if not dt:
|
|
116
135
|
continue
|
|
117
136
|
if dt.enum_name == "sidescan_sonar":
|
|
118
|
-
stream_id =
|
|
137
|
+
stream_id = _identifier_from_uri(
|
|
138
|
+
sidescan_stream, ("samples", "timestamp")
|
|
139
|
+
)
|
|
119
140
|
elif dt.enum_name == "gnss_gps_position_sensor":
|
|
120
|
-
stream_id =
|
|
141
|
+
stream_id = _identifier_from_uri(
|
|
142
|
+
gnss_stream, ("position", "timestamp")
|
|
143
|
+
)
|
|
121
144
|
else:
|
|
122
145
|
continue
|
|
123
146
|
|
|
@@ -130,7 +153,9 @@ def upload(
|
|
|
130
153
|
input_timezone=tz,
|
|
131
154
|
)
|
|
132
155
|
)
|
|
133
|
-
typer.echo(
|
|
156
|
+
typer.echo(
|
|
157
|
+
f"Mapped: {device.name} → stream {stream_id.source.selected}"
|
|
158
|
+
)
|
|
134
159
|
mappings_created += 1
|
|
135
160
|
|
|
136
161
|
if mappings_created == 0:
|
|
@@ -7,20 +7,61 @@ from uuid import UUID
|
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
class SourceSelection(BaseModel):
|
|
11
|
+
"""An ``{available, selected}`` pair.
|
|
12
|
+
|
|
13
|
+
Used at both levels of ``StreamIdentifier``: outer source URI and
|
|
14
|
+
inner field-role selection.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
available: All candidate values produced by the scan.
|
|
18
|
+
selected: The currently chosen value; must be one of ``available``.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
available: list[str]
|
|
22
|
+
selected: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StreamIdentifier(BaseModel):
|
|
26
|
+
"""Two-level source / field selection persisted on RawFileDeviceMapping.
|
|
27
|
+
|
|
28
|
+
Mirrors the shape produced by the engine's manifest serializer for
|
|
29
|
+
one stream descriptor (see clarity-engine
|
|
30
|
+
``pipeline/ingest/manifest_serializer.py``):
|
|
31
|
+
|
|
32
|
+
- ``source`` — which packet-level source within the file feeds this
|
|
33
|
+
device (e.g., ``xtf://sidescan(source=ping_header,pair=0)``).
|
|
34
|
+
- ``fields_by_source`` — for each available source URI, which raw
|
|
35
|
+
field provides each role (``samples``, ``timestamp``, ``position``,
|
|
36
|
+
…).
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
source: Outer source-URI selection.
|
|
40
|
+
fields_by_source: Per-source role → field selection. Outer key is
|
|
41
|
+
the source URI; inner key is the role name.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
source: SourceSelection
|
|
45
|
+
fields_by_source: dict[str, dict[str, SourceSelection]]
|
|
46
|
+
|
|
47
|
+
|
|
10
48
|
class RawFileDeviceMappingBase(BaseModel):
|
|
11
49
|
"""Base model for raw file device mapping.
|
|
12
50
|
|
|
13
51
|
Attributes:
|
|
14
52
|
raw_file_id: Foreign key reference to RawFile.
|
|
15
53
|
device_id: Foreign key reference to Device.
|
|
16
|
-
source_stream_identifier:
|
|
54
|
+
source_stream_identifier: Two-level source / field-role selection.
|
|
55
|
+
auto_mapped: True for system-generated mappings, False for user
|
|
56
|
+
overrides. Defaults to True.
|
|
17
57
|
input_srid: Spatial reference ID for the input data.
|
|
18
58
|
input_timezone: IANA timezone name for the input data (e.g. Etc/UTC).
|
|
19
59
|
"""
|
|
20
60
|
|
|
21
61
|
raw_file_id: UUID
|
|
22
62
|
device_id: UUID
|
|
23
|
-
source_stream_identifier:
|
|
63
|
+
source_stream_identifier: StreamIdentifier
|
|
64
|
+
auto_mapped: bool = True
|
|
24
65
|
input_srid: int = Field(ge=2000, le=900913)
|
|
25
66
|
input_timezone: str
|
|
26
67
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Pydantic models for user layer. User-uploaded geospatial layers associated with a project."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UserLayerState(str, Enum):
|
|
11
|
+
"""Status of a user layer."""
|
|
12
|
+
|
|
13
|
+
UPLOADING = "uploading"
|
|
14
|
+
READY = "ready"
|
|
15
|
+
ERROR = "error"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UserLayerType(str, Enum):
|
|
19
|
+
"""Supported user layer file types."""
|
|
20
|
+
|
|
21
|
+
GEOJSON = "geojson"
|
|
22
|
+
KML = "kml"
|
|
23
|
+
KMZ = "kmz"
|
|
24
|
+
SHAPEFILE = "shapefile"
|
|
25
|
+
GPX = "gpx"
|
|
26
|
+
CSV = "csv"
|
|
27
|
+
GEOTIFF = "geotiff"
|
|
28
|
+
BAG = "bag"
|
|
29
|
+
LAS = "las"
|
|
30
|
+
LAZ = "laz"
|
|
31
|
+
PLY = "ply"
|
|
32
|
+
PCD = "pcd"
|
|
33
|
+
MAGNETOMETRY = "magnetometry"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class UserLayerBase(BaseModel):
|
|
37
|
+
"""Base model for user layer data.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
project_id: Foreign key reference to Project.
|
|
41
|
+
layer_name: Display name for the layer.
|
|
42
|
+
layer_type: Type of the user layer file.
|
|
43
|
+
file_name: Name of the uploaded file.
|
|
44
|
+
file_size: Size of the uploaded file in bytes.
|
|
45
|
+
style: Optional styling configuration for the layer.
|
|
46
|
+
metadata: Optional metadata for the layer.
|
|
47
|
+
sort_order: Display sort order.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
project_id: UUID
|
|
51
|
+
layer_name: str
|
|
52
|
+
layer_type: UserLayerType
|
|
53
|
+
file_name: str | None = None
|
|
54
|
+
file_size: int | None = None
|
|
55
|
+
style: dict | None = None
|
|
56
|
+
metadata: dict | None = None
|
|
57
|
+
sort_order: int = 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class UserLayerCreate(UserLayerBase):
|
|
61
|
+
"""Model for creating a user layer."""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class UserLayerUpdate(BaseModel):
|
|
65
|
+
"""Model for updating a user layer.
|
|
66
|
+
|
|
67
|
+
Attributes:
|
|
68
|
+
layer_name: Optional updated display name.
|
|
69
|
+
style: Optional updated styling configuration.
|
|
70
|
+
metadata: Optional updated metadata.
|
|
71
|
+
sort_order: Optional updated sort order.
|
|
72
|
+
state: Optional updated state.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
layer_name: str | None = None
|
|
76
|
+
style: dict | None = None
|
|
77
|
+
metadata: dict | None = None
|
|
78
|
+
sort_order: int | None = None
|
|
79
|
+
state: UserLayerState | None = None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class UserLayer(UserLayerBase):
|
|
83
|
+
"""Model for user layer data with database fields.
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
user_layer_id: Unique identifier for the user layer.
|
|
87
|
+
user_id: ID of the user who created the layer.
|
|
88
|
+
state: Upload state (uploading, ready, error).
|
|
89
|
+
uri: URI for the layer file storage location.
|
|
90
|
+
upload_id: S3 multipart upload ID (present while uploading, cleared when complete).
|
|
91
|
+
created_date: Timestamp when the layer was created.
|
|
92
|
+
updated_date: Timestamp when the layer was last updated.
|
|
93
|
+
updated_by: ID of the user who last updated the layer.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
user_layer_id: UUID
|
|
97
|
+
user_id: str
|
|
98
|
+
state: UserLayerState = UserLayerState.UPLOADING
|
|
99
|
+
uri: str | None = None
|
|
100
|
+
upload_id: str | None = None
|
|
101
|
+
created_date: datetime
|
|
102
|
+
updated_date: datetime
|
|
103
|
+
updated_by: str
|
|
104
|
+
|
|
105
|
+
model_config = ConfigDict(from_attributes=True)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class UserLayerWithUpload(UserLayer):
|
|
109
|
+
"""UserLayer response for the create-with-upload endpoint.
|
|
110
|
+
|
|
111
|
+
The endpoint initiates an S3 multipart upload before returning, so `uri`
|
|
112
|
+
and `upload_id` are guaranteed to be present. Overrides those fields to
|
|
113
|
+
be non-nullable so callers don't have to defensively check.
|
|
114
|
+
|
|
115
|
+
Attributes:
|
|
116
|
+
uri: S3 URI for the layer file storage location. Always populated.
|
|
117
|
+
upload_id: S3 multipart upload ID. Always populated; clients pass it
|
|
118
|
+
to subsequent part-upload and complete-upload calls.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
# Tightening parent's `str | None` to required `str`. Pyright flags the
|
|
122
|
+
# type narrowing as an override issue; Pydantic treats the bare field
|
|
123
|
+
# annotation as required at runtime, which is what we want.
|
|
124
|
+
uri: str # type: ignore[assignment]
|
|
125
|
+
upload_id: str # type: ignore[assignment]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Round-trip tests for the RawFileDeviceMapping SDK Pydantic model.
|
|
2
|
+
|
|
3
|
+
Phase II Step 10 reshaped ``source_stream_identifier`` from a free-form
|
|
4
|
+
string to a nested ``StreamIdentifier``. These tests pin the wire shape
|
|
5
|
+
the engine and server already agree on so future drift fails loudly.
|
|
6
|
+
|
|
7
|
+
See clarity-server#104 / #105 and
|
|
8
|
+
designs/spec-file-prescan-stream-mapping.md §lines 489–495, 939–953.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from uuid import uuid4
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
from pydantic import ValidationError
|
|
17
|
+
|
|
18
|
+
from cti.model.raw_file_device_mapping import (
|
|
19
|
+
RawFileDeviceMappingCreate,
|
|
20
|
+
SourceSelection,
|
|
21
|
+
StreamIdentifier,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_SOURCE_URI = "xtf://sidescan(source=ping_header,pair=0)"
|
|
26
|
+
_SAMPLE_IDENTIFIER_DICT = {
|
|
27
|
+
"source": {"available": [_SOURCE_URI], "selected": _SOURCE_URI},
|
|
28
|
+
"fields_by_source": {
|
|
29
|
+
_SOURCE_URI: {
|
|
30
|
+
"samples": {"available": ["samples"], "selected": "samples"},
|
|
31
|
+
"timestamp": {"available": ["timestamp"], "selected": "timestamp"},
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_stream_identifier_round_trips_from_dict() -> None:
|
|
38
|
+
"""A dict in the manifest-serializer shape parses and serializes back unchanged."""
|
|
39
|
+
ident = StreamIdentifier.model_validate(_SAMPLE_IDENTIFIER_DICT)
|
|
40
|
+
|
|
41
|
+
assert isinstance(ident.source, SourceSelection)
|
|
42
|
+
assert ident.source.selected == _SOURCE_URI
|
|
43
|
+
assert ident.fields_by_source[_SOURCE_URI]["samples"].selected == "samples"
|
|
44
|
+
|
|
45
|
+
assert ident.model_dump() == _SAMPLE_IDENTIFIER_DICT
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_create_payload_round_trips_through_json() -> None:
|
|
49
|
+
"""End-to-end: build a Create model, serialize to JSON, parse it back."""
|
|
50
|
+
payload = RawFileDeviceMappingCreate(
|
|
51
|
+
raw_file_id=uuid4(),
|
|
52
|
+
device_id=uuid4(),
|
|
53
|
+
source_stream_identifier=StreamIdentifier.model_validate(
|
|
54
|
+
_SAMPLE_IDENTIFIER_DICT
|
|
55
|
+
),
|
|
56
|
+
input_srid=4326,
|
|
57
|
+
input_timezone="Etc/UTC",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
parsed = RawFileDeviceMappingCreate.model_validate_json(payload.model_dump_json())
|
|
61
|
+
assert parsed == payload
|
|
62
|
+
assert parsed.auto_mapped is True
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_auto_mapped_defaults_to_true_when_omitted() -> None:
|
|
66
|
+
"""``auto_mapped`` is a system-generated default, not required from clients."""
|
|
67
|
+
payload = RawFileDeviceMappingCreate.model_validate(
|
|
68
|
+
{
|
|
69
|
+
"raw_file_id": str(uuid4()),
|
|
70
|
+
"device_id": str(uuid4()),
|
|
71
|
+
"source_stream_identifier": _SAMPLE_IDENTIFIER_DICT,
|
|
72
|
+
"input_srid": 4326,
|
|
73
|
+
"input_timezone": "Etc/UTC",
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
assert payload.auto_mapped is True
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_auto_mapped_false_persists_for_user_overrides() -> None:
|
|
80
|
+
"""User-supplied overrides must round-trip ``auto_mapped: false``."""
|
|
81
|
+
payload = RawFileDeviceMappingCreate.model_validate(
|
|
82
|
+
{
|
|
83
|
+
"raw_file_id": str(uuid4()),
|
|
84
|
+
"device_id": str(uuid4()),
|
|
85
|
+
"source_stream_identifier": _SAMPLE_IDENTIFIER_DICT,
|
|
86
|
+
"auto_mapped": False,
|
|
87
|
+
"input_srid": 4326,
|
|
88
|
+
"input_timezone": "Etc/UTC",
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
assert payload.auto_mapped is False
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_string_source_stream_identifier_is_rejected() -> None:
|
|
95
|
+
"""The pre-Phase-II free-form string form must no longer validate.
|
|
96
|
+
|
|
97
|
+
Pins the breaking change so a future regression that loosens the type
|
|
98
|
+
back to ``str`` fails this test instead of silently re-allowing
|
|
99
|
+
unstructured identifiers.
|
|
100
|
+
"""
|
|
101
|
+
with pytest.raises(ValidationError):
|
|
102
|
+
RawFileDeviceMappingCreate.model_validate(
|
|
103
|
+
{
|
|
104
|
+
"raw_file_id": str(uuid4()),
|
|
105
|
+
"device_id": str(uuid4()),
|
|
106
|
+
"source_stream_identifier": "channel-0",
|
|
107
|
+
"input_srid": 4326,
|
|
108
|
+
"input_timezone": "Etc/UTC",
|
|
109
|
+
}
|
|
110
|
+
)
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/tests/test_sdk_async_methods.py
RENAMED
|
@@ -18,6 +18,7 @@ from .conftest import (
|
|
|
18
18
|
RAW_FILE_ID,
|
|
19
19
|
SURVEY_ID,
|
|
20
20
|
make_job_json,
|
|
21
|
+
make_processing_log_json,
|
|
21
22
|
make_raw_file_json,
|
|
22
23
|
make_raw_file_with_upload_json,
|
|
23
24
|
)
|
|
@@ -219,3 +220,80 @@ async def test_wait_for_job_raises_on_timeout():
|
|
|
219
220
|
with patch.object(loop, "time", side_effect=mock_time):
|
|
220
221
|
with pytest.raises(TimeoutError, match="did not complete"):
|
|
221
222
|
await api.wait_for_job(job_id=JOB_ID, timeout=10)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ── list_processing_logs filter kwargs (WI-0106 A1) ─────────────────────
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@pytest.mark.asyncio
|
|
229
|
+
async def test_list_processing_logs_no_filters_omits_params():
|
|
230
|
+
"""No kwargs → request sent with params=None (preserves existing behavior)."""
|
|
231
|
+
client = AsyncMock()
|
|
232
|
+
client.get.return_value = _mock_response([make_processing_log_json()])
|
|
233
|
+
|
|
234
|
+
api = SonarWizAsyncApi(client)
|
|
235
|
+
logs = await api.list_processing_logs()
|
|
236
|
+
|
|
237
|
+
assert len(logs) == 1
|
|
238
|
+
client.get.assert_awaited_once_with("/api/v1/processing-logs", params=None)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@pytest.mark.asyncio
|
|
242
|
+
async def test_list_processing_logs_all_filters_passed_as_query_params():
|
|
243
|
+
"""All three filters compose into the query string; UUIDs stringified."""
|
|
244
|
+
client = AsyncMock()
|
|
245
|
+
client.get.return_value = _mock_response(
|
|
246
|
+
[make_processing_log_json(processing_step="scan", result='{"streams":[]}')]
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
api = SonarWizAsyncApi(client)
|
|
250
|
+
logs = await api.list_processing_logs(
|
|
251
|
+
raw_file_id=RAW_FILE_ID,
|
|
252
|
+
survey_id=SURVEY_ID,
|
|
253
|
+
processing_step="scan",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
client.get.assert_awaited_once_with(
|
|
257
|
+
"/api/v1/processing-logs",
|
|
258
|
+
params={
|
|
259
|
+
"raw_file_id": str(RAW_FILE_ID),
|
|
260
|
+
"survey_id": str(SURVEY_ID),
|
|
261
|
+
"processing_step": "scan",
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
assert logs[0].processing_step == "scan"
|
|
265
|
+
assert logs[0].result == '{"streams":[]}'
|
|
266
|
+
assert logs[0].processing_hash == "a" * 64
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@pytest.mark.asyncio
|
|
270
|
+
async def test_list_processing_logs_partial_filter_only_includes_provided():
|
|
271
|
+
"""Unset filters are omitted from the query string."""
|
|
272
|
+
client = AsyncMock()
|
|
273
|
+
client.get.return_value = _mock_response([])
|
|
274
|
+
|
|
275
|
+
api = SonarWizAsyncApi(client)
|
|
276
|
+
logs = await api.list_processing_logs(
|
|
277
|
+
raw_file_id=RAW_FILE_ID, processing_step="scan"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
client.get.assert_awaited_once_with(
|
|
281
|
+
"/api/v1/processing-logs",
|
|
282
|
+
params={"raw_file_id": str(RAW_FILE_ID), "processing_step": "scan"},
|
|
283
|
+
)
|
|
284
|
+
assert logs == []
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@pytest.mark.asyncio
|
|
288
|
+
async def test_list_processing_logs_accepts_string_uuid():
|
|
289
|
+
"""String UUIDs are accepted and stringified the same way."""
|
|
290
|
+
client = AsyncMock()
|
|
291
|
+
client.get.return_value = _mock_response([])
|
|
292
|
+
|
|
293
|
+
api = SonarWizAsyncApi(client)
|
|
294
|
+
await api.list_processing_logs(raw_file_id=str(RAW_FILE_ID))
|
|
295
|
+
|
|
296
|
+
client.get.assert_awaited_once_with(
|
|
297
|
+
"/api/v1/processing-logs",
|
|
298
|
+
params={"raw_file_id": str(RAW_FILE_ID)},
|
|
299
|
+
)
|
|
@@ -19,6 +19,7 @@ from .conftest import (
|
|
|
19
19
|
RAW_FILE_ID,
|
|
20
20
|
SURVEY_ID,
|
|
21
21
|
make_job_json,
|
|
22
|
+
make_processing_log_json,
|
|
22
23
|
make_raw_file_json,
|
|
23
24
|
make_raw_file_with_upload_json,
|
|
24
25
|
)
|
|
@@ -262,3 +263,57 @@ class TestWaitForJob:
|
|
|
262
263
|
mock_mono.side_effect = [0.0, 0.0, 0.0, 100.0]
|
|
263
264
|
with pytest.raises(TimeoutError, match="did not complete"):
|
|
264
265
|
api.wait_for_job(job_id=JOB_ID, timeout=10)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# ── list_processing_logs filter kwargs (WI-0106 A1) ─────────────────────
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def test_list_processing_logs_no_filters_omits_params():
|
|
272
|
+
"""No kwargs → request sent with params=None."""
|
|
273
|
+
client = MagicMock()
|
|
274
|
+
client.get.return_value = _mock_response([make_processing_log_json()])
|
|
275
|
+
|
|
276
|
+
api = SonarWizApi(client)
|
|
277
|
+
logs = api.list_processing_logs()
|
|
278
|
+
|
|
279
|
+
assert len(logs) == 1
|
|
280
|
+
client.get.assert_called_once_with("/api/v1/processing-logs", params=None)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_list_processing_logs_all_filters_passed_as_query_params():
|
|
284
|
+
"""All three filters compose into the query string."""
|
|
285
|
+
client = MagicMock()
|
|
286
|
+
client.get.return_value = _mock_response(
|
|
287
|
+
[make_processing_log_json(processing_step="scan")]
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
api = SonarWizApi(client)
|
|
291
|
+
api.list_processing_logs(
|
|
292
|
+
raw_file_id=RAW_FILE_ID,
|
|
293
|
+
survey_id=SURVEY_ID,
|
|
294
|
+
processing_step="scan",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
client.get.assert_called_once_with(
|
|
298
|
+
"/api/v1/processing-logs",
|
|
299
|
+
params={
|
|
300
|
+
"raw_file_id": str(RAW_FILE_ID),
|
|
301
|
+
"survey_id": str(SURVEY_ID),
|
|
302
|
+
"processing_step": "scan",
|
|
303
|
+
},
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_list_processing_logs_partial_filter_only_includes_provided():
|
|
308
|
+
"""Unset filters are omitted from the query string."""
|
|
309
|
+
client = MagicMock()
|
|
310
|
+
client.get.return_value = _mock_response([])
|
|
311
|
+
|
|
312
|
+
api = SonarWizApi(client)
|
|
313
|
+
logs = api.list_processing_logs(survey_id=SURVEY_ID)
|
|
314
|
+
|
|
315
|
+
client.get.assert_called_once_with(
|
|
316
|
+
"/api/v1/processing-logs",
|
|
317
|
+
params={"survey_id": str(SURVEY_ID)},
|
|
318
|
+
)
|
|
319
|
+
assert logs == []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/altitude_source.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/attitude_source.py
RENAMED
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/depth_source.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/final_product.py
RENAMED
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/layback_algorithm.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/layback_source.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/layback_type.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/organization.py
RENAMED
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/platform_type.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/position_source.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/processing_job.py
RENAMED
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/processing_log.py
RENAMED
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/projection_option.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/raw_file_state.py
RENAMED
|
File without changes
|
|
File without changes
|
{clarity_api_sdk_python-0.3.38 → clarity_api_sdk_python-0.4.0}/src/cti/model/sidescan_ping_source.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|