aind-data-transfer-service 1.16.0__tar.gz → 1.17.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.

Potentially problematic release.


This version of aind-data-transfer-service might be problematic. Click here for more details.

Files changed (108) hide show
  1. {aind_data_transfer_service-1.16.0/src/aind_data_transfer_service.egg-info → aind_data_transfer_service-1.17.0}/PKG-INFO +2 -2
  2. aind_data_transfer_service-1.17.0/docs/examples/behavior_videos_compression.csv +2 -0
  3. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/Contributing.rst +1 -0
  4. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/UserGuideV1.rst +1 -0
  5. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/UserGuideV2.rst +1 -0
  6. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/pyproject.toml +1 -1
  7. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/__init__.py +1 -1
  8. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/models/internal.py +35 -10
  9. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/server.py +120 -7
  10. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/templates/admin.html +11 -2
  11. aind_data_transfer_service-1.17.0/src/aind_data_transfer_service/templates/job_params.html +405 -0
  12. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0/src/aind_data_transfer_service.egg-info}/PKG-INFO +2 -2
  13. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service.egg-info/SOURCES.txt +2 -0
  14. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service.egg-info/requires.txt +1 -1
  15. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/describe_parameters_response.json +1 -1
  16. aind_data_transfer_service-1.17.0/tests/resources/put_parameter_response.json +17 -0
  17. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_server.py +203 -5
  18. aind_data_transfer_service-1.16.0/src/aind_data_transfer_service/templates/job_params.html +0 -195
  19. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.flake8 +0 -0
  20. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  21. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  22. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/ISSUE_TEMPLATE/user-story.md +0 -0
  23. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/workflows/add_issue_to_project_board.yml +0 -0
  24. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/workflows/publish_dev.yml +0 -0
  25. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/workflows/publish_main.yml +0 -0
  26. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/workflows/run_dev_tests.yml +0 -0
  27. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.github/workflows/run_main_tests.yml +0 -0
  28. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.gitignore +0 -0
  29. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/.readthedocs.yaml +0 -0
  30. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/CODE_OF_CONDUCT.md +0 -0
  31. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/Dockerfile +0 -0
  32. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/LICENSE +0 -0
  33. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/README.md +0 -0
  34. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/Makefile +0 -0
  35. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/diagrams/system_container.png +0 -0
  36. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/diagrams/system_container.puml +0 -0
  37. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/diagrams/system_context.png +0 -0
  38. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/diagrams/system_context.puml +0 -0
  39. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/basic_upload.py +0 -0
  40. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/behavior_videos_compression.py +0 -0
  41. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/custom_codeocean_pipeline_settings.py +0 -0
  42. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/custom_metadata_mapper_settings.py +0 -0
  43. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/example1.csv +0 -0
  44. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/hcr_example.py +0 -0
  45. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/remove_source_folders.py +0 -0
  46. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/skip_s3_check.py +0 -0
  47. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/upload_with_custom_slurm_settings.py +0 -0
  48. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/examples/upload_with_notification.py +0 -0
  49. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/make.bat +0 -0
  50. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/_static/dark-logo.svg +0 -0
  51. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/_static/favicon.ico +0 -0
  52. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/_static/light-logo.svg +0 -0
  53. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/aind_data_transfer_service.configs.rst +0 -0
  54. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/aind_data_transfer_service.hpc.rst +0 -0
  55. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/aind_data_transfer_service.models.rst +0 -0
  56. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/aind_data_transfer_service.rst +0 -0
  57. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/conf.py +0 -0
  58. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/index.rst +0 -0
  59. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/docs/source/modules.rst +0 -0
  60. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/setup.cfg +0 -0
  61. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/setup.py +0 -0
  62. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/configs/__init__.py +0 -0
  63. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/configs/csv_handler.py +0 -0
  64. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/configs/job_configs.py +0 -0
  65. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/configs/job_upload_template.py +0 -0
  66. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/hpc/__init__.py +0 -0
  67. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/hpc/client.py +0 -0
  68. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/hpc/models.py +0 -0
  69. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/log_handler.py +0 -0
  70. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/models/__init__.py +0 -0
  71. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/models/core.py +0 -0
  72. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/templates/index.html +0 -0
  73. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/templates/job_status.html +0 -0
  74. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/templates/job_tasks_table.html +0 -0
  75. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service/templates/task_logs.html +0 -0
  76. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service.egg-info/dependency_links.txt +0 -0
  77. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/src/aind_data_transfer_service.egg-info/top_level.txt +0 -0
  78. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/__init__.py +0 -0
  79. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/airflow_dag_run_response.json +0 -0
  80. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/airflow_dag_runs_response.json +0 -0
  81. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/airflow_task_instances_response.json +0 -0
  82. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/get_parameter_response.json +0 -0
  83. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/get_secrets_response.json +0 -0
  84. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/job_upload_template.xlsx +0 -0
  85. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/legacy_sample.csv +0 -0
  86. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/legacy_sample2.csv +0 -0
  87. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/nested_sample.csv +0 -0
  88. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/new_sample.csv +0 -0
  89. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample.csv +0 -0
  90. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample.xlsx +0 -0
  91. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_alt_modality_case.csv +0 -0
  92. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_empty_rows.csv +0 -0
  93. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_empty_rows.xlsx +0 -0
  94. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_empty_rows_2.csv +0 -0
  95. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_invalid_ext.txt +0 -0
  96. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_malformed.csv +0 -0
  97. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_malformed.xlsx +0 -0
  98. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/resources/sample_malformed_2.csv +0 -0
  99. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_configs.py +0 -0
  100. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_core.py +0 -0
  101. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_csv_handler.py +0 -0
  102. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_hpc_client.py +0 -0
  103. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_hpc_models.py +0 -0
  104. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_internal.py +0 -0
  105. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_job_upload_template.py +0 -0
  106. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_log_handler.py +0 -0
  107. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_server/Dockerfile +0 -0
  108. {aind_data_transfer_service-1.16.0 → aind_data_transfer_service-1.17.0}/tests/test_server/db.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aind-data-transfer-service
3
- Version: 1.16.0
3
+ Version: 1.17.0
4
4
  Summary: Service that handles requests to upload data to the cloud
5
5
  Author: Allen Institute for Neural Dynamics
6
6
  License: MIT
@@ -25,7 +25,7 @@ Requires-Dist: furo; extra == "docs"
25
25
  Provides-Extra: server
26
26
  Requires-Dist: aind-data-schema<2.0,>=1.0.0; extra == "server"
27
27
  Requires-Dist: aind-data-transfer-models==0.17.0; extra == "server"
28
- Requires-Dist: aind-metadata-mapper==0.23.0; extra == "server"
28
+ Requires-Dist: aind-metadata-mapper>=0.23.0; extra == "server"
29
29
  Requires-Dist: boto3; extra == "server"
30
30
  Requires-Dist: boto3-stubs[ssm]; extra == "server"
31
31
  Requires-Dist: fastapi>=0.115.13; extra == "server"
@@ -0,0 +1,2 @@
1
+ project_name, modality0, modality0.input_source, modality0.compression_requested.compression_enum, subject-id, platform, acq-datetime, job_type
2
+ Ephys Platform, behavior-videos, dir/data_set_1, gamma fix colorspace, 123454, ecephys, 2020-10-10 14:10:10, ecephys
@@ -62,6 +62,7 @@ To run uvicorn locally:
62
62
 
63
63
  .. code:: bash
64
64
 
65
+ # export MSYS_NO_PATHCONV=1 # Uncomment this line if running on Windows
65
66
  export AIND_METADATA_SERVICE_PROJECT_NAMES_URL='http://aind-metadata-service-dev/project_names'
66
67
  export AIND_AIRFLOW_SERVICE_URL='http://aind-airflow-service-dev:8080/api/v1/dags/run_list_of_jobs/dagRuns'
67
68
  # export AIND_AIRFLOW_SERVICE_JOBS_URL='http://aind-airflow-service-dev:8080/api/v1/dags'
@@ -520,6 +520,7 @@ Available job types and their configurations can be viewed at:
520
520
  http://aind-data-transfer-service/job_params
521
521
 
522
522
  To request a new job type, please reach out to Scientific Computing.
523
+ Admins can manage job types directly in the Job Parameters page.
523
524
 
524
525
  Reporting bugs or making feature requests
525
526
  -----------------------------------------
@@ -186,6 +186,7 @@ Available job types and their configurations can be viewed at:
186
186
  http://aind-data-transfer-service/job_params
187
187
 
188
188
  To request a new job type, please reach out to Scientific Computing.
189
+ Admins can manage job types directly in the Job Parameters page.
189
190
 
190
191
  Reporting bugs or making feature requests
191
192
  -----------------------------------------
@@ -41,7 +41,7 @@ docs = [
41
41
  server = [
42
42
  'aind-data-schema>=1.0.0,<2.0',
43
43
  'aind-data-transfer-models==0.17.0',
44
- 'aind-metadata-mapper==0.23.0',
44
+ 'aind-metadata-mapper>=0.23.0',
45
45
  'boto3',
46
46
  'boto3-stubs[ssm]',
47
47
  'fastapi>=0.115.13',
@@ -1,7 +1,7 @@
1
1
  """Init package"""
2
2
  import os
3
3
 
4
- __version__ = "1.16.0"
4
+ __version__ = "1.17.0"
5
5
 
6
6
  # Global constants
7
7
  OPEN_DATA_BUCKET_NAME = os.getenv("OPEN_DATA_BUCKET_NAME", "open")
@@ -3,8 +3,9 @@
3
3
  import ast
4
4
  import os
5
5
  from datetime import datetime, timedelta, timezone
6
- from typing import List, Optional, Union
6
+ from typing import ClassVar, List, Optional, Union
7
7
 
8
+ from aind_data_schema_models.modalities import Modality
8
9
  from mypy_boto3_ssm.type_defs import ParameterMetadataTypeDef
9
10
  from pydantic import AwareDatetime, BaseModel, Field, field_validator
10
11
  from starlette.datastructures import QueryParams
@@ -223,11 +224,30 @@ class JobTasks(BaseModel):
223
224
  class JobParamInfo(BaseModel):
224
225
  """Model for job parameter info from AWS Parameter Store"""
225
226
 
227
+ _MODALITIES_LIST: ClassVar[list[str]] = list(
228
+ Modality.abbreviation_map.keys()
229
+ )
230
+ _MODALITY_TASKS: ClassVar[list[str]] = [
231
+ "modality_transformation_settings",
232
+ "codeocean_pipeline_settings",
233
+ ]
234
+
226
235
  name: Optional[str]
227
236
  last_modified: Optional[datetime]
228
- job_type: str
229
- task_id: str
237
+ job_type: str = Field(..., pattern=r"^[^\s/]+$")
238
+ task_id: str = Field(..., pattern=r"^[^\s/]+$")
230
239
  modality: Optional[str]
240
+ version: Optional[str] = Field(..., pattern=r"^(v1|v2)?$")
241
+
242
+ @field_validator("modality", mode="after")
243
+ def validate_modality(cls, v):
244
+ """Check that modality is one of aind-data-schema modalities"""
245
+ if v is not None and v not in JobParamInfo._MODALITIES_LIST:
246
+ raise ValueError(
247
+ "Invalid modality: modality must be one of "
248
+ f"{JobParamInfo._MODALITIES_LIST}"
249
+ )
250
+ return v
231
251
 
232
252
  @classmethod
233
253
  def from_aws_describe_parameter(
@@ -236,6 +256,7 @@ class JobParamInfo(BaseModel):
236
256
  job_type: str,
237
257
  task_id: str,
238
258
  modality: Optional[str],
259
+ version: Optional[str],
239
260
  ):
240
261
  """Map the parameter to the model"""
241
262
  return cls(
@@ -244,13 +265,14 @@ class JobParamInfo(BaseModel):
244
265
  job_type=job_type,
245
266
  task_id=task_id,
246
267
  modality=modality,
268
+ version=version,
247
269
  )
248
270
 
249
271
  @staticmethod
250
272
  def get_parameter_prefix(version: Optional[str] = None) -> str:
251
273
  """Get the prefix for job_type parameters"""
252
274
  prefix = os.getenv("AIND_AIRFLOW_PARAM_PREFIX")
253
- if version is None:
275
+ if version is None or version == "v1":
254
276
  return prefix
255
277
  return f"{prefix}/{version}"
256
278
 
@@ -262,16 +284,19 @@ class JobParamInfo(BaseModel):
262
284
  "(?P<job_type>[^/]+)/tasks/(?P<task_id>[^/]+)"
263
285
  "(?:/(?P<modality>[^/]+))?"
264
286
  )
265
- if version is None:
287
+ if version is None or version == "v1":
266
288
  return f"{prefix}/{regex}"
267
289
  return f"{prefix}/{version}/{regex}"
268
290
 
269
291
  @staticmethod
270
292
  def get_parameter_name(
271
- job_type: str, task_id: str, version: Optional[str] = None
293
+ job_type: str,
294
+ task_id: str,
295
+ modality: Optional[str],
296
+ version: Optional[str] = None,
272
297
  ) -> str:
273
298
  """Create the parameter name from job_type and task_id"""
274
- prefix = os.getenv("AIND_AIRFLOW_PARAM_PREFIX")
275
- if version is None:
276
- return f"{prefix}/{job_type}/tasks/{task_id}"
277
- return f"{prefix}/{version}/{job_type}/tasks/{task_id}"
299
+ prefix = JobParamInfo.get_parameter_prefix(version)
300
+ if modality:
301
+ return f"{prefix}/{job_type}/tasks/{task_id}/{modality}"
302
+ return f"{prefix}/{job_type}/tasks/{task_id}"
@@ -7,7 +7,7 @@ import os
7
7
  import re
8
8
  from asyncio import gather, sleep
9
9
  from pathlib import PurePosixPath
10
- from typing import List, Optional, Union
10
+ from typing import Any, List, Optional, Union
11
11
 
12
12
  import boto3
13
13
  import requests
@@ -156,6 +156,7 @@ def get_parameter_infos(version: Optional[str] = None) -> List[JobParamInfo]:
156
156
  job_type=match.group("job_type"),
157
157
  task_id=match.group("task_id"),
158
158
  modality=match.group("modality"),
159
+ version=version,
159
160
  )
160
161
  params.append(param_info)
161
162
  else:
@@ -173,6 +174,19 @@ def get_parameter_value(param_name: str) -> dict:
173
174
  return param_value
174
175
 
175
176
 
177
+ def put_parameter_value(param_name: str, param_value: dict) -> Any:
178
+ """Set a parameter value in AWS param store based on parameter name"""
179
+ param_value_str = json.dumps(param_value)
180
+ ssm_client = boto3.client("ssm")
181
+ result = ssm_client.put_parameter(
182
+ Name=param_name,
183
+ Value=param_value_str,
184
+ Type="String",
185
+ Overwrite=True,
186
+ )
187
+ return result
188
+
189
+
176
190
  async def get_airflow_jobs(
177
191
  params: AirflowDagRunsRequestParameters, get_confs: bool = False
178
192
  ) -> tuple[int, Union[List[JobStatus], List[dict]]]:
@@ -1001,16 +1015,20 @@ async def jobs(request: Request):
1001
1015
 
1002
1016
  async def job_params(request: Request):
1003
1017
  """Get Job Parameters page"""
1018
+ user = request.session.get("user")
1004
1019
  return templates.TemplateResponse(
1005
1020
  request=request,
1006
1021
  name="job_params.html",
1007
1022
  context=(
1008
1023
  {
1024
+ "user_signed_in": user is not None,
1009
1025
  "project_names_url": os.getenv(
1010
1026
  "AIND_METADATA_SERVICE_PROJECT_NAMES_URL"
1011
1027
  ),
1012
1028
  "versions": ["v1", "v2"],
1013
1029
  "default_version": "v1",
1030
+ "modalities": JobParamInfo._MODALITIES_LIST,
1031
+ "modality_tasks": JobParamInfo._MODALITY_TASKS,
1014
1032
  }
1015
1033
  ),
1016
1034
  )
@@ -1074,7 +1092,10 @@ def get_parameter_v2(request: Request):
1074
1092
  # path params are auto validated
1075
1093
  job_type = request.path_params.get("job_type")
1076
1094
  task_id = request.path_params.get("task_id")
1077
- param_name = JobParamInfo.get_parameter_name(job_type, task_id, "v2")
1095
+ modality = request.path_params.get("modality")
1096
+ param_name = JobParamInfo.get_parameter_name(
1097
+ job_type=job_type, task_id=task_id, modality=modality, version="v2"
1098
+ )
1078
1099
  try:
1079
1100
  param_value = get_parameter_value(param_name)
1080
1101
  return JSONResponse(
@@ -1095,12 +1116,79 @@ def get_parameter_v2(request: Request):
1095
1116
  )
1096
1117
 
1097
1118
 
1119
+ async def put_parameter(request: Request):
1120
+ """Set v1/v2 parameter in AWS param store based on job_type and task_id"""
1121
+ # User must be signed in
1122
+ user = request.session.get("user")
1123
+ if not user:
1124
+ return JSONResponse(
1125
+ content={
1126
+ "message": "User not authenticated",
1127
+ "data": {"error": "User not authenticated"},
1128
+ },
1129
+ status_code=401,
1130
+ )
1131
+ try:
1132
+ # path params
1133
+ param_info = JobParamInfo(
1134
+ name=None,
1135
+ last_modified=None,
1136
+ job_type=request.path_params.get("job_type"),
1137
+ task_id=request.path_params.get("task_id"),
1138
+ modality=request.path_params.get("modality"),
1139
+ version=request.path_params.get("version"),
1140
+ )
1141
+ param_name = JobParamInfo.get_parameter_name(
1142
+ job_type=param_info.job_type,
1143
+ task_id=param_info.task_id,
1144
+ modality=param_info.modality,
1145
+ version=param_info.version,
1146
+ )
1147
+ # update param store
1148
+ logger.info(
1149
+ f"Received request from {user} to set parameter {param_name}"
1150
+ )
1151
+ param_value = await request.json()
1152
+ logger.info(f"Setting parameter {param_name} to {param_value}")
1153
+ result = put_parameter_value(
1154
+ param_name=param_name, param_value=param_value
1155
+ )
1156
+ logger.info(result)
1157
+ return JSONResponse(
1158
+ content={
1159
+ "message": f"Set parameter for {param_name}",
1160
+ "data": param_value,
1161
+ },
1162
+ status_code=200,
1163
+ )
1164
+ except ValidationError as error:
1165
+ return JSONResponse(
1166
+ content={
1167
+ "message": "Invalid parameter",
1168
+ "data": {"errors": json.loads(error.json())},
1169
+ },
1170
+ status_code=400,
1171
+ )
1172
+ except Exception as e:
1173
+ logger.exception(f"Error setting parameter {param_name}: {e}")
1174
+ return JSONResponse(
1175
+ content={
1176
+ "message": f"Error setting parameter {param_name}",
1177
+ "data": {"error": f"{e.__class__.__name__}{e.args}"},
1178
+ },
1179
+ status_code=500,
1180
+ )
1181
+
1182
+
1098
1183
  def get_parameter(request: Request):
1099
1184
  """Get parameter from AWS parameter store based on job_type and task_id"""
1100
1185
  # path params are auto validated
1101
1186
  job_type = request.path_params.get("job_type")
1102
1187
  task_id = request.path_params.get("task_id")
1103
- param_name = JobParamInfo.get_parameter_name(job_type, task_id)
1188
+ modality = request.path_params.get("modality")
1189
+ param_name = JobParamInfo.get_parameter_name(
1190
+ job_type=job_type, task_id=task_id, modality=modality
1191
+ )
1104
1192
  try:
1105
1193
  param_value = get_parameter_value(param_name)
1106
1194
  return JSONResponse(
@@ -1124,8 +1212,6 @@ def get_parameter(request: Request):
1124
1212
  async def admin(request: Request):
1125
1213
  """Get admin page if authenticated, else redirect to login."""
1126
1214
  user = request.session.get("user")
1127
- if os.getenv("ENV_NAME") == "local":
1128
- user = {"name": "local user"}
1129
1215
  if user:
1130
1216
  return templates.TemplateResponse(
1131
1217
  request=request,
@@ -1143,6 +1229,9 @@ async def admin(request: Request):
1143
1229
 
1144
1230
  async def login(request: Request):
1145
1231
  """Redirect to Azure login page"""
1232
+ if os.getenv("ENV_NAME") == "local":
1233
+ request.session["user"] = {"name": "local user"}
1234
+ return RedirectResponse(url="/admin")
1146
1235
  oauth = set_oauth()
1147
1236
  redirect_uri = request.url_for("auth")
1148
1237
  response = await oauth.azure.authorize_redirect(request, redirect_uri)
@@ -1193,7 +1282,13 @@ routes = [
1193
1282
  Route("/api/v1/get_task_logs", endpoint=get_task_logs, methods=["GET"]),
1194
1283
  Route("/api/v1/parameters", endpoint=list_parameters, methods=["GET"]),
1195
1284
  Route(
1196
- "/api/v1/parameters/job_types/{job_type:str}/tasks/{task_id:path}",
1285
+ "/api/v1/parameters/job_types/{job_type:str}/tasks/{task_id:str}",
1286
+ endpoint=get_parameter,
1287
+ methods=["GET"],
1288
+ ),
1289
+ Route(
1290
+ "/api/v1/parameters/job_types/{job_type:str}/tasks/{task_id:str}"
1291
+ "/{modality:str}",
1197
1292
  endpoint=get_parameter,
1198
1293
  methods=["GET"],
1199
1294
  ),
@@ -1204,10 +1299,28 @@ routes = [
1204
1299
  Route("/api/v2/submit_jobs", endpoint=submit_jobs_v2, methods=["POST"]),
1205
1300
  Route("/api/v2/parameters", endpoint=list_parameters_v2, methods=["GET"]),
1206
1301
  Route(
1207
- "/api/v2/parameters/job_types/{job_type:str}/tasks/{task_id:path}",
1302
+ "/api/v2/parameters/job_types/{job_type:str}/tasks/{task_id:str}",
1303
+ endpoint=get_parameter_v2,
1304
+ methods=["GET"],
1305
+ ),
1306
+ Route(
1307
+ "/api/v2/parameters/job_types/{job_type:str}/tasks/{task_id:str}"
1308
+ "/{modality:str}",
1208
1309
  endpoint=get_parameter_v2,
1209
1310
  methods=["GET"],
1210
1311
  ),
1312
+ Route(
1313
+ "/api/{version:str}/parameters/job_types/{job_type:str}"
1314
+ "/tasks/{task_id:str}",
1315
+ endpoint=put_parameter,
1316
+ methods=["PUT"],
1317
+ ),
1318
+ Route(
1319
+ "/api/{version:str}/parameters/job_types/{job_type:str}"
1320
+ "/tasks/{task_id:str}/{modality:str}",
1321
+ endpoint=put_parameter,
1322
+ methods=["PUT"],
1323
+ ),
1211
1324
  Route("/jobs", endpoint=jobs, methods=["GET"]),
1212
1325
  Route("/job_tasks_table", endpoint=job_tasks_table, methods=["GET"]),
1213
1326
  Route("/task_logs", endpoint=task_logs, methods=["GET"]),
@@ -24,13 +24,22 @@
24
24
  <a title="List of project names" href="{{ project_names_url }}" target="_blank">Project Names</a> |
25
25
  <a title="For more information click here" href="https://aind-data-transfer-service.readthedocs.io"
26
26
  target="_blank">Help</a> |
27
- <a href="/admin">Admin</a> |
28
- <a href="/logout">Log out</a>
27
+ <a href="/admin">Admin</a>
28
+ <a href="/logout" class="float-end">Log out</a>
29
29
  </nav>
30
30
  <div>
31
31
  <h3>Admin</h3>
32
32
  <div>Hello {{user_name}}, welcome to the admin page</div>
33
33
  <div>Email: {{user_email}}</div>
34
+ <h4 class="mt-4">Job Type Parameters</h4>
35
+ <div class="mb-2">
36
+ <div class="alert alert-info mb-0 p-4" role="alert">
37
+ To manage job type parameters, go to the <a href="/job_params" class="alert-link">Job Parameters</a> page.
38
+ Select an existing parameter, or click the
39
+ <button type="button" class="btn btn-success btn-sm" disabled><i class="bi bi-plus-circle"></i> Add New Parameter</button>
40
+ button.
41
+ </div>
42
+ </div>
34
43
  </div>
35
44
  </body>
36
45
  </html>