aignostics 0.2.37__tar.gz → 0.2.39__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 (147) hide show
  1. {aignostics-0.2.37 → aignostics-0.2.39}/PKG-INFO +1 -1
  2. {aignostics-0.2.37 → aignostics-0.2.39}/examples/notebook.py +1 -1
  3. {aignostics-0.2.37 → aignostics-0.2.39}/pyproject.toml +2 -2
  4. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/__init__.py +3 -1
  5. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_cli.py +1 -1
  6. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_service.py +5 -5
  7. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/_cli.py +0 -11
  8. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/constants.py +1 -0
  9. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/notebook/_notebook.py +2 -3
  10. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_service.py +2 -2
  11. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_settings.py +29 -11
  12. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/_cli.py +0 -27
  13. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/_service.py +14 -9
  14. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_settings.py +6 -0
  15. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/_service.py +6 -7
  16. {aignostics-0.2.37 → aignostics-0.2.39}/.gitignore +0 -0
  17. {aignostics-0.2.37 → aignostics-0.2.39}/LICENSE +0 -0
  18. {aignostics-0.2.37 → aignostics-0.2.39}/README.md +0 -0
  19. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/.openapi-generator/FILES +0 -0
  20. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/.openapi-generator/VERSION +0 -0
  21. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/api/public_api.py +0 -0
  22. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/api_client.py +0 -0
  23. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/api_response.py +0 -0
  24. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/configuration.py +0 -0
  25. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/exceptions.py +0 -0
  26. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/__init__.py +0 -0
  27. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/application_read_response.py +0 -0
  28. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/application_run_status.py +0 -0
  29. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/application_version_read_response.py +0 -0
  30. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/http_validation_error.py +0 -0
  31. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/input_artifact_creation_request.py +0 -0
  32. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/input_artifact_read_response.py +0 -0
  33. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/item_creation_request.py +0 -0
  34. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/item_result_read_response.py +0 -0
  35. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/item_status.py +0 -0
  36. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/output_artifact_read_response.py +0 -0
  37. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py +0 -0
  38. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/output_artifact_scope.py +0 -0
  39. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/payload_input_artifact.py +0 -0
  40. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/payload_item.py +0 -0
  41. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/payload_output_artifact.py +0 -0
  42. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/run_creation_request.py +0 -0
  43. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/run_creation_response.py +0 -0
  44. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/run_read_response.py +0 -0
  45. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/transfer_urls.py +0 -0
  46. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/user_payload.py +0 -0
  47. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/validation_error.py +0 -0
  48. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/models/validation_error_loc_inner.py +0 -0
  49. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/aignx/codegen/rest.py +0 -0
  50. {aignostics-0.2.37 → aignostics-0.2.39}/codegen/out/docs/PublicApi.md +0 -0
  51. {aignostics-0.2.37 → aignostics-0.2.39}/examples/__init__.py +0 -0
  52. {aignostics-0.2.37 → aignostics-0.2.39}/examples/e2e.py +0 -0
  53. {aignostics-0.2.37 → aignostics-0.2.39}/examples/notebook.ipynb +0 -0
  54. {aignostics-0.2.37 → aignostics-0.2.39}/examples/script.py +0 -0
  55. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/__init__.py +0 -0
  56. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/_frame.py +0 -0
  57. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/_page_application_describe.py +0 -0
  58. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/_page_application_run_describe.py +0 -0
  59. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/_page_builder.py +0 -0
  60. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/_page_index.py +0 -0
  61. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/_utils.py +0 -0
  62. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/canceled.lottie +0 -0
  63. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/done.lottie +0 -0
  64. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/empty.lottie +0 -0
  65. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/error.lottie +0 -0
  66. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/error_alt.lottie +0 -0
  67. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/home-card-1.png +0 -0
  68. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/home-card-2.png +0 -0
  69. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/home-card-3.png +0 -0
  70. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/home-card-4.png +0 -0
  71. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/home-card-5.png +0 -0
  72. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/image-not-found.png +0 -0
  73. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/pending.lottie +0 -0
  74. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/pending_alt.lottie +0 -0
  75. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/ruo.png +0 -0
  76. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_gui/assets/ruo.svg +0 -0
  77. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_settings.py +0 -0
  78. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/application/_utils.py +0 -0
  79. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/__init__.py +0 -0
  80. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/_gui.py +0 -0
  81. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/_service.py +0 -0
  82. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/_settings.py +0 -0
  83. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/assets/Google-Cloud-logo.png +0 -0
  84. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/bucket/assets/Google-Cloud-logo.svg +0 -0
  85. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/cli.py +0 -0
  86. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/dataset/__init__.py +0 -0
  87. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/dataset/_cli.py +0 -0
  88. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/dataset/_gui.py +0 -0
  89. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/dataset/_idc_index.py +0 -0
  90. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/dataset/_service.py +0 -0
  91. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/dataset/assets/NIH-IDC-logo.svg +0 -0
  92. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/gui/__init__.py +0 -0
  93. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/gui/_frame.py +0 -0
  94. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/gui/_theme.py +0 -0
  95. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 +0 -0
  96. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/gui/assets/logo.png +0 -0
  97. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/notebook/__init__.py +0 -0
  98. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/notebook/_gui.py +0 -0
  99. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/notebook/_service.py +0 -0
  100. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/notebook/assets/python.lottie +0 -0
  101. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/__init__.py +0 -0
  102. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_authentication.py +0 -0
  103. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_client.py +0 -0
  104. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_constants.py +0 -0
  105. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_messages.py +0 -0
  106. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/_utils.py +0 -0
  107. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/resources/__init__.py +0 -0
  108. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/resources/applications.py +0 -0
  109. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/resources/runs.py +0 -0
  110. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/platform/resources/utils.py +0 -0
  111. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/__init__.py +0 -0
  112. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/_gui.py +0 -0
  113. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/_settings.py +0 -0
  114. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/annotate.groovy +0 -0
  115. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/assets/download.lottie +0 -0
  116. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/assets/microscope.lottie +0 -0
  117. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/qupath/assets/update.lottie +0 -0
  118. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/__init__.py +0 -0
  119. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/_cli.py +0 -0
  120. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/_exceptions.py +0 -0
  121. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/_gui.py +0 -0
  122. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/_service.py +0 -0
  123. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/_settings.py +0 -0
  124. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/system/assets/system.lottie +0 -0
  125. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/.vendored/bottle.py +0 -0
  126. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/__init__.py +0 -0
  127. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_cli.py +0 -0
  128. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_console.py +0 -0
  129. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_constants.py +0 -0
  130. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_di.py +0 -0
  131. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_fs.py +0 -0
  132. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_gui.py +0 -0
  133. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_health.py +0 -0
  134. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_log.py +0 -0
  135. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_logfire.py +0 -0
  136. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_notebook.py +0 -0
  137. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_process.py +0 -0
  138. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_sentry.py +0 -0
  139. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/_service.py +0 -0
  140. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/utils/boot.py +0 -0
  141. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/__init__.py +0 -0
  142. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/_cli.py +0 -0
  143. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/_gui.py +0 -0
  144. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/_openslide_handler.py +0 -0
  145. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/_pydicom_handler.py +0 -0
  146. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/_utils.py +0 -0
  147. {aignostics-0.2.37 → aignostics-0.2.39}/src/aignostics/wsi/assets/fallback.png +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aignostics
3
- Version: 0.2.37
3
+ Version: 0.2.39
4
4
  Summary: 🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library.
5
5
  Project-URL: Homepage, https://aignostics.readthedocs.io/en/latest/
6
6
  Project-URL: Documentation, https://aignostics.readthedocs.io/en/latest/
@@ -2,7 +2,7 @@
2
2
  # requires-python = ">=3.13"
3
3
  # dependencies = [
4
4
  # "marimo",
5
- # "aignostics==0.2.37",
5
+ # "aignostics==0.2.39",
6
6
  # ]
7
7
  # ///
8
8
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aignostics"
3
- version = "0.2.37"
3
+ version = "0.2.39"
4
4
  description = "🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -356,7 +356,7 @@ source = ["src/"]
356
356
 
357
357
 
358
358
  [tool.bumpversion]
359
- current_version = "0.2.37"
359
+ current_version = "0.2.39"
360
360
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
361
361
  serialize = ["{major}.{minor}.{patch}"]
362
362
  search = "{current_version}"
@@ -2,7 +2,7 @@
2
2
 
3
3
  import os
4
4
 
5
- from .constants import MODULES_TO_INSTRUMENT
5
+ from .constants import MODULES_TO_INSTRUMENT, WSI_SUPPORTED_FILE_EXTENSIONS
6
6
  from .utils.boot import boot
7
7
 
8
8
  # Add scheme to HTTP proxy environment variables if missing
@@ -12,3 +12,5 @@ for proxy_var in ["HTTP_PROXY", "HTTPS_PROXY"]:
12
12
  os.environ[proxy_var] = f"http://{proxy_url}"
13
13
 
14
14
  boot(modules_to_instrument=MODULES_TO_INSTRUMENT)
15
+
16
+ __all__ = ["WSI_SUPPORTED_FILE_EXTENSIONS"]
@@ -300,7 +300,7 @@ def run_prepare(
300
300
  ) -> None:
301
301
  """Prepare metadata CSV file required for submitting a run.
302
302
 
303
- (1) Scans source_directory for whole slide images (.tif, .tiff and .dcm.
303
+ (1) Scans source_directory for whole slide images.
304
304
  (2) Extracts metadata from whole slide images such as width, height, mpp.
305
305
  (3) Creates CSV file with columns as required by the given application version.
306
306
  (4) Optionally applies mappings to amend the metadata CSV file for columns
@@ -15,6 +15,7 @@ import requests
15
15
  from pydantic import BaseModel, computed_field
16
16
 
17
17
  from aignostics.bucket import Service as BucketService
18
+ from aignostics.constants import WSI_SUPPORTED_FILE_EXTENSIONS
18
19
  from aignostics.platform import (
19
20
  LIST_APPLICATION_RUNS_MAX_PAGE_SIZE,
20
21
  ApiException,
@@ -154,13 +155,13 @@ class Service(BaseService):
154
155
  """Initialize service."""
155
156
  super().__init__(Settings) # automatically loads and validates the settings
156
157
 
157
- def info(self) -> dict[str, Any]: # noqa: PLR6301
158
+ def info(self) -> dict[str, Any]:
158
159
  """Determine info of this service.
159
160
 
160
161
  Returns:
161
162
  dict[str,Any]: The info of this service.
162
163
  """
163
- return {}
164
+ return {"settings": self._settings.model_dump()}
164
165
 
165
166
  def health(self) -> Health: # noqa: PLR6301
166
167
  """Determine health of this service.
@@ -350,7 +351,7 @@ class Service(BaseService):
350
351
  """Generate metadata from the source directory.
351
352
 
352
353
  Steps:
353
- 1. Recursively files ending with .tiff, .tif, .svs and .dcm in the source directory
354
+ 1. Recursively files ending with supported extensions in the source directory
354
355
  2. Creates a dict with the following columns
355
356
  - reference (str): The reference of the file, by default equivalent to the absolute file name
356
357
  - source (str): The absolute filename
@@ -385,10 +386,9 @@ class Service(BaseService):
385
386
  application_version = Service().application_version(application_version_id, use_latest_if_no_version_given=True) # noqa: F841
386
387
 
387
388
  metadata = []
388
- file_extensions = [".tiff", ".tif", ".svs", ".dcm"]
389
389
 
390
390
  try:
391
- for extension in file_extensions:
391
+ for extension in list(WSI_SUPPORTED_FILE_EXTENSIONS):
392
392
  for file_path in source_directory.glob(f"**/*{extension}"):
393
393
  # Generate CRC32C checksum with google_crc32c and encode as base64
394
394
  hash_sum = google_crc32c.Checksum() # type: ignore[no-untyped-call]
@@ -59,11 +59,9 @@ def upload(
59
59
 
60
60
  console.print(f"Uploading {source} to bucket...")
61
61
 
62
- # Create a counter for total bytes
63
62
  total_bytes = 0
64
63
  files_count = 0
65
64
 
66
- # First, calculate total size and count files
67
65
  if source.is_file():
68
66
  total_bytes = source.stat().st_size
69
67
  files_count = 1
@@ -75,14 +73,9 @@ def upload(
75
73
 
76
74
  console.print(f"Found {files_count} files with total size of {humanize.naturalsize(total_bytes)}")
77
75
 
78
- # Generate base prefix with placeholders replaced
79
76
  username = psutil.Process().username()
80
77
  timestamp = datetime.datetime.now(tz=datetime.UTC).strftime("%Y%m%d_%H%M%S")
81
-
82
- # Replace placeholders in the destination_prefix
83
78
  base_prefix = destination_prefix.format(username=username, timestamp=timestamp)
84
-
85
- # Remove leading/trailing slashes for consistency
86
79
  base_prefix = base_prefix.strip("/")
87
80
 
88
81
  with Progress(
@@ -98,18 +91,14 @@ def upload(
98
91
  TransferSpeedColumn(),
99
92
  TextColumn("[progress.description]{task.description}"),
100
93
  ) as progress:
101
- # Create a task for overall progress
102
94
  task = progress.add_task(f"Uploading to {base_prefix}/...", total=total_bytes)
103
95
 
104
- # Callback function to update progress
105
96
  def update_progress(bytes_uploaded: int, file: Path) -> None:
106
97
  relpath = file.relative_to(source)
107
98
  progress.update(task, advance=bytes_uploaded, description=f"{relpath}")
108
99
 
109
- # Upload with progress reporting
110
100
  results = Service().upload(source, base_prefix, update_progress)
111
101
 
112
- # Print results summary
113
102
  if results["success"]:
114
103
  console.print(f"[green]Successfully uploaded {len(results['success'])} files:[/green]")
115
104
  for key in results["success"]:
@@ -12,3 +12,4 @@ NOTEBOOK_APP = Path(__file__).parent.parent.parent / "examples" / "notebook.py"
12
12
  # Project specific configuration
13
13
  os.environ["MATPLOTLIB"] = "false"
14
14
  os.environ["NICEGUI_STORAGE_PATH"] = str(Path.home().resolve() / ".aignostics" / ".nicegui")
15
+ WSI_SUPPORTED_FILE_EXTENSIONS = {".dcm", ".tiff", ".tif", ".svs"}
@@ -27,7 +27,7 @@ def _():
27
27
  import matplotlib.pyplot as plt
28
28
  from cloudpathlib import GSPath
29
29
  from dotenv import load_dotenv
30
- from aignostics import dataset
30
+ from aignostics import dataset, WSI_SUPPORTED_FILE_EXTENSIONS
31
31
  from wsidicom import WsiDicom
32
32
 
33
33
  mo.sidebar([
@@ -44,7 +44,6 @@ def _():
44
44
  orientation="vertical",
45
45
  ),
46
46
  ])
47
- return Path, WsiDicom, dataset, mo, plt
48
47
 
49
48
 
50
49
  @app.cell
@@ -55,7 +54,7 @@ def _(Path, mo):
55
54
  mo.vstack([
56
55
  mo.ui.file_browser(
57
56
  Path(query_params["results_folder"]),
58
- filetypes=[".dcm", ".tif", ".tiff", ".svs"],
57
+ filetypes=list(WSI_SUPPORTED_FILE_EXTENSIONS),
59
58
  multiple=True,
60
59
  restrict_navigation=True,
61
60
  )
@@ -24,13 +24,13 @@ class Service(BaseService):
24
24
  """Initialize service."""
25
25
  super().__init__(Settings) # automatically loads and validates the settings
26
26
 
27
- def info(self) -> dict[str, Any]: # noqa: PLR6301
27
+ def info(self) -> dict[str, Any]:
28
28
  """Determine info of this service.
29
29
 
30
30
  Returns:
31
31
  dict[str,Any]: The info of this service.
32
32
  """
33
- return {}
33
+ return {"settings": self._settings.model_dump()}
34
34
 
35
35
  def _determine_api_public_health(self) -> Health:
36
36
  """Determine healthiness and reachability of Aignostics Platform API.
@@ -6,7 +6,15 @@ from pathlib import Path
6
6
  from typing import Annotated, TypeVar
7
7
 
8
8
  import appdirs
9
- from pydantic import Field, PlainSerializer, SecretStr, computed_field, model_validator
9
+ from pydantic import (
10
+ Field,
11
+ FieldSerializationInfo,
12
+ PlainSerializer,
13
+ SecretStr,
14
+ computed_field,
15
+ field_serializer,
16
+ model_validator,
17
+ )
10
18
  from pydantic_settings import BaseSettings, SettingsConfigDict
11
19
 
12
20
  from aignostics.utils import OpaqueSettings, __project_name__, load_settings
@@ -47,22 +55,22 @@ class Settings(OpaqueSettings):
47
55
  client credentials, token storage, and other SDK behaviors.
48
56
 
49
57
  Attributes:
50
- client_id_device (SecretStr): Client ID for device authorization flow.
51
- client_id_interactive (SecretStr): Client ID for interactive authorization flow.
52
58
  api_root (str): Base URL of the Aignostics API.
53
- scope (str): OAuth scopes required by the SDK.
54
- scope_elements (list[str]): OAuth scopes split into individual elements.
55
59
  audience (str): OAuth audience claim.
60
+ authorization_backoff_seconds (int): Backoff time for authorization retries in seconds.
56
61
  authorization_base_url (str): Authorization endpoint for OAuth flows.
57
- token_url (str): Token endpoint for OAuth flows.
58
- redirect_uri (str): Redirect URI for OAuth authorization code flow.
62
+ cache_dir (str): Directory for caching tokens and other data.
63
+ client_id_device (SecretStr): Client ID for device authorization flow.
64
+ client_id_interactive (SecretStr): Client ID for interactive authorization flow.
59
65
  device_url (str): Device authorization endpoint for device flow.
60
66
  jws_json_url (str): URL for JWS key set.
67
+ redirect_uri (str): Redirect URI for OAuth authorization code flow.
61
68
  refresh_token (SecretStr | None): OAuth refresh token if available.
62
- cache_dir (str): Directory for caching tokens and other data.
63
- token_file (Path): Path to the token storage file.
64
69
  request_timeout_seconds (int): Timeout for API requests in seconds.
65
- authorization_backoff_seconds (int): Backoff time for authorization retries in seconds.
70
+ scope (str): OAuth scopes required by the SDK.
71
+ scope_elements (list[str]): OAuth scopes split into individual elements.
72
+ token_file (Path): Path to the token storage file.
73
+ token_url (str): Token endpoint for OAuth flows.
66
74
  """
67
75
 
68
76
  model_config = SettingsConfigDict(
@@ -117,7 +125,13 @@ class Settings(OpaqueSettings):
117
125
  device_url: str
118
126
  jws_json_url: str
119
127
 
120
- refresh_token: SecretStr | None = None
128
+ refresh_token: Annotated[
129
+ SecretStr | None,
130
+ PlainSerializer(
131
+ func=OpaqueSettings.serialize_sensitive_info, return_type=str, when_used="always"
132
+ ), # allow to unhide sensitive info from CLI or if user presents valid token via API
133
+ Field(description="Refresh token for OAuth authentication", min_length=10, max_length=1000, default=None),
134
+ ] = None
121
135
 
122
136
  cache_dir: str = appdirs.user_cache_dir(__project_name__)
123
137
 
@@ -131,6 +145,10 @@ class Settings(OpaqueSettings):
131
145
  """
132
146
  return Path(self.cache_dir) / ".token"
133
147
 
148
+ @field_serializer("token_file")
149
+ def serialize_token_file(self, token_file: Path, _info: FieldSerializationInfo) -> str: # noqa: PLR6301
150
+ return str(token_file.resolve())
151
+
134
152
  request_timeout_seconds: int = 30
135
153
  authorization_backoff_seconds: int = 3
136
154
 
@@ -172,33 +172,6 @@ def launch(
172
172
  sys.exit(1)
173
173
 
174
174
 
175
- @cli.command()
176
- def info() -> None:
177
- """Get info about QuPath installation."""
178
- if not Service().is_qupath_installed():
179
- console.print("QuPath is not installed. Use 'uvx aignostics qupath install' to install it.", style="warning")
180
- sys.exit(2)
181
- try:
182
- console.print_json(data=Service().info())
183
- except Exception as e:
184
- message = f"Failed to get QuPath info: {e!s}."
185
- logger.exception(message)
186
- console.print(message, style="error")
187
- sys.exit(1)
188
-
189
-
190
- @cli.command()
191
- def settings() -> None:
192
- """Show settings configured for Paquo based QuPath integration."""
193
- console.print_json(data=Service().get_paquo_settings())
194
-
195
-
196
- @cli.command()
197
- def defaults() -> None:
198
- """Show default settings of Paquo based QuPath integration."""
199
- console.print_json(data=Service().get_paquo_defaults())
200
-
201
-
202
175
  @cli.command()
203
176
  def uninstall(
204
177
  version: Annotated[
@@ -25,6 +25,7 @@ import requests
25
25
  from packaging.version import Version
26
26
  from pydantic import BaseModel, computed_field
27
27
 
28
+ from aignostics import WSI_SUPPORTED_FILE_EXTENSIONS
28
29
  from aignostics.utils import BaseService, Health, __project_name__, get_logger
29
30
 
30
31
  from ._settings import Settings
@@ -35,7 +36,6 @@ QUPATH_VERSION = "0.6.0-rc5"
35
36
  DOWNLOAD_CHUNK_SIZE = 10 * 1024 * 1024
36
37
  LAUNCH_MAX_WAIT_TIME = 60 # seconds, maximum wait time for QuPath to start
37
38
 
38
- IMAGE_SUFFIXES = {".dcm", ".tiff", ".tif", ".svs"}
39
39
  PROJECT_FILENAME = "project.qpproj"
40
40
  ANNOTATIONS_BATCH_SIZE = 500000
41
41
 
@@ -152,17 +152,22 @@ class Service(BaseService):
152
152
  Returns:
153
153
  dict[str,Any]: The info of this service.
154
154
  """
155
- application_path = Service.find_qupath()
155
+ settings = self._settings.model_dump()
156
+ settings.update({
157
+ "paquo": {
158
+ "current": self.get_paquo_settings(),
159
+ "defaults": self.get_paquo_defaults(),
160
+ },
161
+ })
162
+ app_path = Service.find_qupath()
156
163
  version = Service.get_version()
157
164
  return {
158
- "qupath": {
159
- "application_path": str(application_path) if application_path else "Not found",
165
+ "app": {
166
+ "path": str(app_path) if app_path else None,
160
167
  "version": dict(version) if version else None,
168
+ "expected_version": Service.get_expected_version(),
161
169
  },
162
- "paquo": {
163
- "settings": self.get_paquo_settings(),
164
- "defaults": self.get_paquo_defaults(),
165
- },
170
+ "settings": settings,
166
171
  }
167
172
 
168
173
  def health(self) -> Health:
@@ -1085,7 +1090,7 @@ class Service(BaseService):
1085
1090
  progress.status = AddProgressState.FINDING_IMAGES
1086
1091
  progress_callable(progress)
1087
1092
 
1088
- supported_extensions = IMAGE_SUFFIXES
1093
+ supported_extensions = WSI_SUPPORTED_FILE_EXTENSIONS
1089
1094
 
1090
1095
  def is_supported_image(file_path: Path) -> bool:
1091
1096
  return file_path.is_file() and file_path.suffix.lower() in supported_extensions
@@ -38,6 +38,12 @@ class OpaqueSettings(BaseSettings):
38
38
  return input_value.get_secret_value()
39
39
  return str(input_value)
40
40
 
41
+ @staticmethod
42
+ def serialize_path_resolve(input_value: Path, _info: FieldSerializationInfo) -> str | None:
43
+ if not input_value:
44
+ return None
45
+ return str(input_value.resolve())
46
+
41
47
 
42
48
  def load_settings(settings_class: type[T]) -> T:
43
49
  """
@@ -6,14 +6,13 @@ from typing import Any
6
6
 
7
7
  import requests
8
8
 
9
+ from aignostics import WSI_SUPPORTED_FILE_EXTENSIONS
9
10
  from aignostics.utils import BaseService, Health, get_logger
10
11
 
11
12
  logger = get_logger(__name__)
12
13
 
13
14
  TIMEOUT = 60 # 1 minutes
14
15
 
15
- FILE_TYPES_SUPPORTED = {".dcm", ".tiff", ".tif", ".svs"}
16
-
17
16
 
18
17
  class Service(BaseService):
19
18
  """Service of the application module."""
@@ -47,7 +46,7 @@ class Service(BaseService):
47
46
  PIL.Image.Image: Thumbnail of the image.
48
47
 
49
48
  Raises:
50
- ValueError: If the file type is not supported (.dcm, .tiff, or .tif).
49
+ ValueError: If the file type is not supported.
51
50
  RuntimeError: If there is an error generating the thumbnail.
52
51
  """
53
52
  from ._openslide_handler import OpenSlideHandler # noqa: PLC0415
@@ -56,8 +55,8 @@ class Service(BaseService):
56
55
  message = f"File does not exist: {path}"
57
56
  logger.warning(message)
58
57
  raise ValueError(message)
59
- if path.suffix.lower() not in FILE_TYPES_SUPPORTED:
60
- message = f"Unsupported file type: {path.suffix}. Supported types are .dcm, .tiff, and .tif."
58
+ if path.suffix.lower() not in WSI_SUPPORTED_FILE_EXTENSIONS:
59
+ message = f"Unsupported file type: {path.suffix}. Supported types are {WSI_SUPPORTED_FILE_EXTENSIONS!s}"
61
60
  logger.warning(message)
62
61
  raise ValueError(message)
63
62
  try:
@@ -106,8 +105,8 @@ class Service(BaseService):
106
105
  message = f"File does not exist: {path}"
107
106
  logger.warning(message)
108
107
  raise ValueError(message)
109
- if path.suffix.lower() not in FILE_TYPES_SUPPORTED:
110
- message = f"Unsupported file type: {path.suffix}. Supported types are .dcm, .tiff, and .tif."
108
+ if path.suffix.lower() not in WSI_SUPPORTED_FILE_EXTENSIONS:
109
+ message = f"Unsupported file type: {path.suffix}. Supported types are {WSI_SUPPORTED_FILE_EXTENSIONS}."
111
110
  logger.warning(message)
112
111
  raise ValueError(message)
113
112
  try:
File without changes
File without changes
File without changes
File without changes