clarity-api-sdk-python 0.3.35__tar.gz → 0.3.36__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.
Files changed (58) hide show
  1. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/PKG-INFO +1 -1
  2. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/pyproject.toml +1 -1
  3. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/clarity_api_sdk_python.egg-info/PKG-INFO +1 -1
  4. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/cli/main.py +50 -0
  5. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/processing_log.py +11 -4
  6. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/tests/test_cli.py +83 -0
  7. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/README.md +0 -0
  8. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/setup.cfg +0 -0
  9. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/clarity_api_sdk_python.egg-info/SOURCES.txt +0 -0
  10. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/clarity_api_sdk_python.egg-info/dependency_links.txt +0 -0
  11. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/clarity_api_sdk_python.egg-info/entry_points.txt +0 -0
  12. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/clarity_api_sdk_python.egg-info/requires.txt +0 -0
  13. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/clarity_api_sdk_python.egg-info/top_level.txt +0 -0
  14. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/__init__.py +0 -0
  15. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/api/__init__.py +0 -0
  16. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/api/async_client.py +0 -0
  17. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/api/client.py +0 -0
  18. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/api/session.py +0 -0
  19. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/api/sonar_wiz_api.py +0 -0
  20. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/api/sonar_wiz_async_api.py +0 -0
  21. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/cli/__init__.py +0 -0
  22. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/cli/__main__.py +0 -0
  23. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/cli/client.py +0 -0
  24. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/logger/__init__.py +0 -0
  25. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/logger/logger.py +0 -0
  26. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/main.py +0 -0
  27. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/main_api.py +0 -0
  28. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/__init__.py +0 -0
  29. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/altitude_source.py +0 -0
  30. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/attitude_source.py +0 -0
  31. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/deferred_object_deletion.py +0 -0
  32. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/depth_source.py +0 -0
  33. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/device.py +0 -0
  34. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/device_type.py +0 -0
  35. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/final_product.py +0 -0
  36. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/hierarchy.py +0 -0
  37. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/layback_algorithm.py +0 -0
  38. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/layback_source.py +0 -0
  39. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/layback_type.py +0 -0
  40. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/organization.py +0 -0
  41. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/platform.py +0 -0
  42. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/platform_type.py +0 -0
  43. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/position_source.py +0 -0
  44. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/processing_job.py +0 -0
  45. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/project.py +0 -0
  46. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/projection_option.py +0 -0
  47. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/raw_file.py +0 -0
  48. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/raw_file_configuration.py +0 -0
  49. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/raw_file_device_mapping.py +0 -0
  50. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/raw_file_state.py +0 -0
  51. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/s3.py +0 -0
  52. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/sidescan_ping_source.py +0 -0
  53. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/source.py +0 -0
  54. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/survey.py +0 -0
  55. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/target.py +0 -0
  56. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/src/cti/model/tow_system.py +0 -0
  57. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/tests/test_sdk_async_methods.py +0 -0
  58. {clarity_api_sdk_python-0.3.35 → clarity_api_sdk_python-0.3.36}/tests/test_sdk_methods.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clarity-api-sdk-python
3
- Version: 0.3.35
3
+ Version: 0.3.36
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
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "clarity-api-sdk-python"
8
- version = "0.3.35"
8
+ version = "0.3.36"
9
9
  authors = [
10
10
  { name="Chesapeake Technology Inc.", email="support@chesapeaketech.com" },
11
11
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clarity-api-sdk-python
3
- Version: 0.3.35
3
+ Version: 0.3.36
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
@@ -21,6 +21,9 @@ app = typer.Typer(
21
21
  list_app = typer.Typer(help="List resources (surveys, projects, jobs).", no_args_is_help=True)
22
22
  app.add_typer(list_app, name="list")
23
23
 
24
+ delete_app = typer.Typer(help="Delete resources (rawfile, survey, project).", no_args_is_help=True)
25
+ app.add_typer(delete_app, name="delete")
26
+
24
27
 
25
28
  def _output(data: dict, as_json: bool) -> None:
26
29
  """Print output as JSON or human-readable."""
@@ -453,6 +456,53 @@ def list_jobs(
453
456
  typer.echo(f" {j.job_id} {j.status} {j.job_type} {j.created_at}")
454
457
 
455
458
 
459
+ def _delete_resource(path: str, resource_label: str, resource_id: UUID, yes: bool) -> None:
460
+ """Confirm and DELETE a resource by its API path. Exits non-zero on error."""
461
+ if not yes and not typer.confirm(f"Delete {resource_label} {resource_id}?"):
462
+ typer.echo("Aborted.")
463
+ raise typer.Exit(1)
464
+
465
+ api = get_api()
466
+ try:
467
+ response = api._client.delete(path)
468
+ response.raise_for_status()
469
+ except httpx.HTTPStatusError as e:
470
+ if e.response.status_code == 404:
471
+ typer.echo(f"Error: {resource_label} {resource_id} not found.", err=True)
472
+ else:
473
+ typer.echo(f"Error: {_get_error_detail(e)}", err=True)
474
+ raise typer.Exit(1)
475
+
476
+ typer.echo(f"Deleted {resource_label} {resource_id}.")
477
+
478
+
479
+ @delete_app.command("rawfile")
480
+ def delete_rawfile(
481
+ raw_file_id: UUID = typer.Argument(..., help="Raw file ID"),
482
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
483
+ ) -> None:
484
+ """Delete a raw file."""
485
+ _delete_resource(f"/api/v1/rawfiles/{raw_file_id}", "rawfile", raw_file_id, yes)
486
+
487
+
488
+ @delete_app.command("survey")
489
+ def delete_survey(
490
+ survey_id: UUID = typer.Argument(..., help="Survey ID"),
491
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
492
+ ) -> None:
493
+ """Delete a survey."""
494
+ _delete_resource(f"/api/v1/surveys/{survey_id}", "survey", survey_id, yes)
495
+
496
+
497
+ @delete_app.command("project")
498
+ def delete_project(
499
+ project_id: UUID = typer.Argument(..., help="Project ID"),
500
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
501
+ ) -> None:
502
+ """Delete a project."""
503
+ _delete_resource(f"/api/v1/projects/{project_id}", "project", project_id, yes)
504
+
505
+
456
506
  def main() -> None:
457
507
  """Entry point for the ftm CLI."""
458
508
  app()
@@ -17,6 +17,9 @@ class ProcessingLogBase(BaseModel):
17
17
  end_timestamp: End timestamp of processing.
18
18
  status: Status of the processing step.
19
19
  error_details: Error details if processing failed.
20
+ result: Processing step result payload (e.g., stream manifest JSON
21
+ for scan steps); opaque string.
22
+ processing_hash: Content-addressable cache key (SHA-256 hex digest).
20
23
  """
21
24
 
22
25
  processing_step: str
@@ -26,6 +29,8 @@ class ProcessingLogBase(BaseModel):
26
29
  end_timestamp: datetime
27
30
  status: str
28
31
  error_details: str | None = None
32
+ result: str | None = None
33
+ processing_hash: str
29
34
 
30
35
 
31
36
  class ProcessingLogCreate(ProcessingLogBase):
@@ -33,11 +38,11 @@ class ProcessingLogCreate(ProcessingLogBase):
33
38
 
34
39
  Attributes:
35
40
  survey_id: Foreign key reference to Survey.
36
- source_id: Foreign key reference to Source.
41
+ raw_file_id: Foreign key reference to RawFile.
37
42
  """
38
43
 
39
44
  survey_id: UUID
40
- source_id: UUID
45
+ raw_file_id: UUID
41
46
 
42
47
 
43
48
  class ProcessingLogUpdate(ProcessingLogBase):
@@ -51,6 +56,8 @@ class ProcessingLogUpdate(ProcessingLogBase):
51
56
  end_timestamp: End timestamp of processing.
52
57
  status: Status of the processing step.
53
58
  error_details: Error details if processing failed.
59
+ result: Processing step result payload.
60
+ processing_hash: Content-addressable cache key.
54
61
  """
55
62
 
56
63
 
@@ -60,11 +67,11 @@ class ProcessingLog(ProcessingLogBase):
60
67
  Attributes:
61
68
  processing_log_id: Unique identifier for the processing log.
62
69
  survey_id: Foreign key reference to Survey.
63
- source_id: Foreign key reference to Source.
70
+ raw_file_id: Foreign key reference to RawFile.
64
71
  """
65
72
 
66
73
  processing_log_id: UUID
67
74
  survey_id: UUID
68
- source_id: UUID
75
+ raw_file_id: UUID
69
76
 
70
77
  model_config = ConfigDict(from_attributes=True)
@@ -469,6 +469,89 @@ class TestListProjectsCommand:
469
469
  assert "No projects found" in result.output
470
470
 
471
471
 
472
+ class TestDeleteCommand:
473
+ """Tests for `ftm delete rawfile|survey|project`."""
474
+
475
+ def _mock_delete_response(self, status_code: int = 204):
476
+ resp = MagicMock()
477
+ resp.status_code = status_code
478
+ if status_code >= 400:
479
+ import httpx as _httpx
480
+ resp.raise_for_status.side_effect = _httpx.HTTPStatusError(
481
+ "error", request=MagicMock(), response=resp,
482
+ )
483
+ resp.headers = {"content-type": "application/json"}
484
+ resp.json.return_value = {"detail": "boom"}
485
+ return resp
486
+
487
+ def test_delete_rawfile_with_yes(self):
488
+ """delete rawfile -y skips confirmation and calls DELETE."""
489
+ mock_api = MagicMock()
490
+ mock_api._client.delete.return_value = self._mock_delete_response(204)
491
+
492
+ with patch("cti.cli.main.get_api", return_value=mock_api):
493
+ result = runner.invoke(app, ["delete", "rawfile", str(RAW_FILE_ID), "--yes"])
494
+
495
+ assert result.exit_code == 0
496
+ assert f"Deleted rawfile {RAW_FILE_ID}" in result.output
497
+ mock_api._client.delete.assert_called_once_with(f"/api/v1/rawfiles/{RAW_FILE_ID}")
498
+
499
+ def test_delete_survey_with_yes(self):
500
+ """delete survey -y calls the surveys endpoint."""
501
+ mock_api = MagicMock()
502
+ mock_api._client.delete.return_value = self._mock_delete_response(204)
503
+
504
+ with patch("cti.cli.main.get_api", return_value=mock_api):
505
+ result = runner.invoke(app, ["delete", "survey", str(SURVEY_ID), "-y"])
506
+
507
+ assert result.exit_code == 0
508
+ mock_api._client.delete.assert_called_once_with(f"/api/v1/surveys/{SURVEY_ID}")
509
+
510
+ def test_delete_project_with_yes(self):
511
+ """delete project -y calls the projects endpoint."""
512
+ mock_api = MagicMock()
513
+ mock_api._client.delete.return_value = self._mock_delete_response(204)
514
+
515
+ with patch("cti.cli.main.get_api", return_value=mock_api):
516
+ result = runner.invoke(app, ["delete", "project", str(PROJECT_ID), "-y"])
517
+
518
+ assert result.exit_code == 0
519
+ mock_api._client.delete.assert_called_once_with(f"/api/v1/projects/{PROJECT_ID}")
520
+
521
+ def test_delete_confirmation_abort(self):
522
+ """delete without --yes aborts on 'n' confirmation and does not call API."""
523
+ mock_api = MagicMock()
524
+
525
+ with patch("cti.cli.main.get_api", return_value=mock_api):
526
+ result = runner.invoke(app, ["delete", "rawfile", str(RAW_FILE_ID)], input="n\n")
527
+
528
+ assert result.exit_code == 1
529
+ assert "Aborted" in result.output
530
+ mock_api._client.delete.assert_not_called()
531
+
532
+ def test_delete_confirmation_confirm(self):
533
+ """delete without --yes proceeds on 'y' confirmation."""
534
+ mock_api = MagicMock()
535
+ mock_api._client.delete.return_value = self._mock_delete_response(204)
536
+
537
+ with patch("cti.cli.main.get_api", return_value=mock_api):
538
+ result = runner.invoke(app, ["delete", "rawfile", str(RAW_FILE_ID)], input="y\n")
539
+
540
+ assert result.exit_code == 0
541
+ mock_api._client.delete.assert_called_once()
542
+
543
+ def test_delete_not_found(self):
544
+ """delete exits 1 with 'not found' message on 404."""
545
+ mock_api = MagicMock()
546
+ mock_api._client.delete.return_value = self._mock_delete_response(404)
547
+
548
+ with patch("cti.cli.main.get_api", return_value=mock_api):
549
+ result = runner.invoke(app, ["delete", "rawfile", str(RAW_FILE_ID), "-y"])
550
+
551
+ assert result.exit_code == 1
552
+ assert "not found" in result.output
553
+
554
+
472
555
  class TestListJobsCommand:
473
556
  """Tests for `ftm list jobs`."""
474
557