aind-data-transfer-service 1.15.0__tar.gz → 1.16.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 (105) hide show
  1. {aind_data_transfer_service-1.15.0/src/aind_data_transfer_service.egg-info → aind_data_transfer_service-1.16.0}/PKG-INFO +3 -3
  2. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/remove_source_folders.py +1 -1
  3. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/pyproject.toml +2 -2
  4. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/__init__.py +1 -1
  5. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/configs/csv_handler.py +57 -6
  6. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/server.py +15 -9
  7. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0/src/aind_data_transfer_service.egg-info}/PKG-INFO +3 -3
  8. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service.egg-info/SOURCES.txt +1 -0
  9. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service.egg-info/requires.txt +2 -2
  10. aind_data_transfer_service-1.16.0/tests/resources/nested_sample.csv +2 -0
  11. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_csv_handler.py +63 -5
  12. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.flake8 +0 -0
  13. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/ISSUE_TEMPLATE/user-story.md +0 -0
  16. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/workflows/add_issue_to_project_board.yml +0 -0
  17. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/workflows/publish_dev.yml +0 -0
  18. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/workflows/publish_main.yml +0 -0
  19. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/workflows/run_dev_tests.yml +0 -0
  20. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.github/workflows/run_main_tests.yml +0 -0
  21. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.gitignore +0 -0
  22. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/.readthedocs.yaml +0 -0
  23. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/CODE_OF_CONDUCT.md +0 -0
  24. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/Dockerfile +0 -0
  25. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/LICENSE +0 -0
  26. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/README.md +0 -0
  27. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/Makefile +0 -0
  28. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/diagrams/system_container.png +0 -0
  29. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/diagrams/system_container.puml +0 -0
  30. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/diagrams/system_context.png +0 -0
  31. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/diagrams/system_context.puml +0 -0
  32. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/basic_upload.py +0 -0
  33. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/behavior_videos_compression.py +0 -0
  34. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/custom_codeocean_pipeline_settings.py +0 -0
  35. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/custom_metadata_mapper_settings.py +0 -0
  36. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/example1.csv +0 -0
  37. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/hcr_example.py +0 -0
  38. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/skip_s3_check.py +0 -0
  39. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/upload_with_custom_slurm_settings.py +0 -0
  40. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/examples/upload_with_notification.py +0 -0
  41. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/make.bat +0 -0
  42. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/Contributing.rst +0 -0
  43. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/UserGuideV1.rst +0 -0
  44. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/UserGuideV2.rst +0 -0
  45. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/_static/dark-logo.svg +0 -0
  46. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/_static/favicon.ico +0 -0
  47. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/_static/light-logo.svg +0 -0
  48. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/aind_data_transfer_service.configs.rst +0 -0
  49. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/aind_data_transfer_service.hpc.rst +0 -0
  50. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/aind_data_transfer_service.models.rst +0 -0
  51. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/aind_data_transfer_service.rst +0 -0
  52. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/conf.py +0 -0
  53. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/index.rst +0 -0
  54. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/docs/source/modules.rst +0 -0
  55. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/setup.cfg +0 -0
  56. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/setup.py +0 -0
  57. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/configs/__init__.py +0 -0
  58. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/configs/job_configs.py +0 -0
  59. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/configs/job_upload_template.py +0 -0
  60. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/hpc/__init__.py +0 -0
  61. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/hpc/client.py +0 -0
  62. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/hpc/models.py +0 -0
  63. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/log_handler.py +0 -0
  64. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/models/__init__.py +0 -0
  65. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/models/core.py +0 -0
  66. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/models/internal.py +0 -0
  67. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/templates/admin.html +0 -0
  68. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/templates/index.html +0 -0
  69. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/templates/job_params.html +0 -0
  70. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/templates/job_status.html +0 -0
  71. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/templates/job_tasks_table.html +0 -0
  72. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service/templates/task_logs.html +0 -0
  73. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service.egg-info/dependency_links.txt +0 -0
  74. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/src/aind_data_transfer_service.egg-info/top_level.txt +0 -0
  75. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/__init__.py +0 -0
  76. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/airflow_dag_run_response.json +0 -0
  77. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/airflow_dag_runs_response.json +0 -0
  78. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/airflow_task_instances_response.json +0 -0
  79. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/describe_parameters_response.json +0 -0
  80. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/get_parameter_response.json +0 -0
  81. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/get_secrets_response.json +0 -0
  82. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/job_upload_template.xlsx +0 -0
  83. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/legacy_sample.csv +0 -0
  84. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/legacy_sample2.csv +0 -0
  85. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/new_sample.csv +0 -0
  86. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample.csv +0 -0
  87. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample.xlsx +0 -0
  88. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_alt_modality_case.csv +0 -0
  89. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_empty_rows.csv +0 -0
  90. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_empty_rows.xlsx +0 -0
  91. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_empty_rows_2.csv +0 -0
  92. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_invalid_ext.txt +0 -0
  93. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_malformed.csv +0 -0
  94. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_malformed.xlsx +0 -0
  95. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/resources/sample_malformed_2.csv +0 -0
  96. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_configs.py +0 -0
  97. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_core.py +0 -0
  98. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_hpc_client.py +0 -0
  99. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_hpc_models.py +0 -0
  100. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_internal.py +0 -0
  101. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_job_upload_template.py +0 -0
  102. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_log_handler.py +0 -0
  103. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_server/Dockerfile +0 -0
  104. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_server/db.json +0 -0
  105. {aind_data_transfer_service-1.15.0 → aind_data_transfer_service-1.16.0}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aind-data-transfer-service
3
- Version: 1.15.0
3
+ Version: 1.16.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
@@ -28,10 +28,10 @@ Requires-Dist: aind-data-transfer-models==0.17.0; extra == "server"
28
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
- Requires-Dist: fastapi; extra == "server"
31
+ Requires-Dist: fastapi>=0.115.13; extra == "server"
32
32
  Requires-Dist: httpx; extra == "server"
33
33
  Requires-Dist: jinja2; extra == "server"
34
- Requires-Dist: starlette; extra == "server"
34
+ Requires-Dist: starlette<0.47.0,>=0.40.0; extra == "server"
35
35
  Requires-Dist: starlette_wtf; extra == "server"
36
36
  Requires-Dist: uvicorn[standard]; extra == "server"
37
37
  Requires-Dist: wtforms; extra == "server"
@@ -55,7 +55,7 @@ upload_job_configs_v2 = UploadJobConfigsV2(
55
55
  tasks={
56
56
  "modality_transformation_settings": modality_transformation_settings,
57
57
  "gather_preliminary_metadata": gather_preliminary_metadata,
58
- "remove_source_folders": remove_source_folders
58
+ "remove_source_folders": remove_source_folders,
59
59
  },
60
60
  )
61
61
 
@@ -44,10 +44,10 @@ server = [
44
44
  'aind-metadata-mapper==0.23.0',
45
45
  'boto3',
46
46
  'boto3-stubs[ssm]',
47
- 'fastapi',
47
+ 'fastapi>=0.115.13',
48
48
  'httpx',
49
49
  'jinja2',
50
- 'starlette',
50
+ 'starlette>=0.40.0,<0.47.0',
51
51
  'starlette_wtf',
52
52
  'uvicorn[standard]',
53
53
  'wtforms',
@@ -1,7 +1,7 @@
1
1
  """Init package"""
2
2
  import os
3
3
 
4
- __version__ = "1.15.0"
4
+ __version__ = "1.16.0"
5
5
 
6
6
  # Global constants
7
7
  OPEN_DATA_BUCKET_NAME = os.getenv("OPEN_DATA_BUCKET_NAME", "open")
@@ -1,7 +1,10 @@
1
1
  """Module to handle processing legacy csv files"""
2
2
 
3
3
  import re
4
+ from collections.abc import Mapping
5
+ from copy import deepcopy
4
6
  from datetime import datetime
7
+ from typing import Any, Dict
5
8
 
6
9
  from aind_data_schema_models.modalities import Modality
7
10
  from aind_data_schema_models.platforms import Platform
@@ -13,6 +16,45 @@ DATETIME_PATTERN2 = re.compile(
13
16
  )
14
17
 
15
18
 
19
+ def nested_update(dict_to_update: Dict[str, Any], updates: Mapping):
20
+ """
21
+ Update a nested dictionary in-place.
22
+ Parameters
23
+ ----------
24
+ dict_to_update : Dict[str, Any]
25
+ updates : Mapping
26
+
27
+ """
28
+ for k, v in updates.items():
29
+ if isinstance(v, Mapping):
30
+ dict_to_update[k] = nested_update(dict_to_update.get(k, {}), v)
31
+ else:
32
+ dict_to_update[k] = v
33
+ return dict_to_update
34
+
35
+
36
+ def create_nested_dict(
37
+ dict_to_update: Dict[str, Any], key_string: str, value: Any
38
+ ):
39
+ """
40
+ Updates in-place a nested dictionary with a period delimited key and value.
41
+ Parameters
42
+ ----------
43
+ dict_to_update : Dict[str, Any]
44
+ key_string : str
45
+ value : Any
46
+
47
+ """
48
+ keys = key_string.split(".", 1)
49
+ current_key = keys[0]
50
+ if len(keys) == 1:
51
+ dict_to_update[current_key] = value
52
+ else:
53
+ if current_key not in dict_to_update:
54
+ dict_to_update[current_key] = dict()
55
+ create_nested_dict(dict_to_update[current_key], keys[1], value)
56
+
57
+
16
58
  def map_csv_row_to_job(row: dict) -> UploadJobConfigsV2:
17
59
  """
18
60
  Maps csv row into a UploadJobConfigsV2 model. This attempts to be somewhat
@@ -29,7 +71,6 @@ def map_csv_row_to_job(row: dict) -> UploadJobConfigsV2:
29
71
  modality_configs = dict()
30
72
  job_configs = dict()
31
73
  check_s3_folder_exists_task = None
32
- final_check_s3_folder_exist = None
33
74
  codeocean_tasks = dict()
34
75
  for key, value in row.items():
35
76
  # Strip white spaces and replace dashes with underscores
@@ -42,7 +83,9 @@ def map_csv_row_to_job(row: dict) -> UploadJobConfigsV2:
42
83
  modality_parts = clean_key.split(".")
43
84
  modality_key = modality_parts[0]
44
85
  sub_key = (
45
- "modality" if len(modality_parts) == 1 else modality_parts[1]
86
+ "modality"
87
+ if len(modality_parts) == 1
88
+ else ".".join(modality_parts[1:])
46
89
  )
47
90
  modality_configs.setdefault(modality_key, dict())
48
91
  # Temp backwards compatibility check
@@ -66,13 +109,22 @@ def map_csv_row_to_job(row: dict) -> UploadJobConfigsV2:
66
109
  job_settings=codeocean_pipeline_monitor_settings,
67
110
  )
68
111
  else:
69
- modality_configs[modality_key].update({sub_key: clean_val})
112
+ nested_val = dict()
113
+ create_nested_dict(
114
+ dict_to_update=nested_val,
115
+ key_string=sub_key,
116
+ value=clean_val,
117
+ )
118
+ current_dict = deepcopy(
119
+ modality_configs.get(modality_key, dict())
120
+ )
121
+ nested_update(current_dict, nested_val)
122
+ modality_configs[modality_key] = current_dict
70
123
  elif clean_key == "force_cloud_sync" and clean_val.upper() in [
71
124
  "TRUE",
72
125
  "T",
73
126
  ]:
74
127
  check_s3_folder_exists_task = {"skip_task": True}
75
- final_check_s3_folder_exist = {"skip_task": True}
76
128
  else:
77
129
  job_configs[clean_key] = clean_val
78
130
  # Rename codeocean config keys with correct modality
@@ -93,8 +145,7 @@ def map_csv_row_to_job(row: dict) -> UploadJobConfigsV2:
93
145
  )
94
146
  tasks = {
95
147
  "gather_preliminary_metadata": metadata_task,
96
- "check_s3_folder_exists_task": check_s3_folder_exists_task,
97
- "final_check_s3_folder_exist": final_check_s3_folder_exist,
148
+ "check_s3_folder_exists": check_s3_folder_exists_task,
98
149
  "modality_transformation_settings": modality_tasks,
99
150
  "codeocean_pipeline_settings": None
100
151
  if codeocean_tasks == dict()
@@ -29,7 +29,9 @@ from starlette.middleware.sessions import SessionMiddleware
29
29
  from starlette.responses import RedirectResponse
30
30
  from starlette.routing import Route
31
31
 
32
- from aind_data_transfer_service import OPEN_DATA_BUCKET_NAME
32
+ from aind_data_transfer_service import (
33
+ OPEN_DATA_BUCKET_NAME,
34
+ )
33
35
  from aind_data_transfer_service import (
34
36
  __version__ as aind_data_transfer_service_version,
35
37
  )
@@ -37,14 +39,18 @@ from aind_data_transfer_service.configs.csv_handler import map_csv_row_to_job
37
39
  from aind_data_transfer_service.configs.job_configs import (
38
40
  BasicUploadJobConfigs as LegacyBasicUploadJobConfigs,
39
41
  )
40
- from aind_data_transfer_service.configs.job_configs import HpcJobConfigs
42
+ from aind_data_transfer_service.configs.job_configs import (
43
+ HpcJobConfigs,
44
+ )
41
45
  from aind_data_transfer_service.configs.job_upload_template import (
42
46
  JobUploadTemplate,
43
47
  )
44
48
  from aind_data_transfer_service.hpc.client import HpcClient, HpcClientConfigs
45
49
  from aind_data_transfer_service.hpc.models import HpcJobSubmitSettings
46
50
  from aind_data_transfer_service.log_handler import LoggingConfigs, get_logger
47
- from aind_data_transfer_service.models.core import SubmitJobRequestV2
51
+ from aind_data_transfer_service.models.core import (
52
+ SubmitJobRequestV2,
53
+ )
48
54
  from aind_data_transfer_service.models.core import (
49
55
  validation_context as validation_context_v2,
50
56
  )
@@ -929,10 +935,10 @@ async def get_task_logs(request: Request):
929
935
  async def index(request: Request):
930
936
  """GET|POST /: form handler"""
931
937
  return templates.TemplateResponse(
938
+ request=request,
932
939
  name="index.html",
933
940
  context=(
934
941
  {
935
- "request": request,
936
942
  "project_names_url": project_names_url,
937
943
  }
938
944
  ),
@@ -945,10 +951,10 @@ async def job_tasks_table(request: Request):
945
951
  response_tasks_json = json.loads(response_tasks.body)
946
952
  data = response_tasks_json.get("data")
947
953
  return templates.TemplateResponse(
954
+ request=request,
948
955
  name="job_tasks_table.html",
949
956
  context=(
950
957
  {
951
- "request": request,
952
958
  "status_code": response_tasks.status_code,
953
959
  "message": response_tasks_json.get("message"),
954
960
  "errors": data.get("errors", []),
@@ -965,10 +971,10 @@ async def task_logs(request: Request):
965
971
  response_tasks_json = json.loads(response_tasks.body)
966
972
  data = response_tasks_json.get("data")
967
973
  return templates.TemplateResponse(
974
+ request=request,
968
975
  name="task_logs.html",
969
976
  context=(
970
977
  {
971
- "request": request,
972
978
  "status_code": response_tasks.status_code,
973
979
  "message": response_tasks_json.get("message"),
974
980
  "errors": data.get("errors", []),
@@ -982,10 +988,10 @@ async def jobs(request: Request):
982
988
  """Get Job Status page with pagination"""
983
989
  dag_ids = AirflowDagRunsRequestParameters.model_fields["dag_ids"].default
984
990
  return templates.TemplateResponse(
991
+ request=request,
985
992
  name="job_status.html",
986
993
  context=(
987
994
  {
988
- "request": request,
989
995
  "project_names_url": project_names_url,
990
996
  "dag_ids": dag_ids,
991
997
  }
@@ -996,10 +1002,10 @@ async def jobs(request: Request):
996
1002
  async def job_params(request: Request):
997
1003
  """Get Job Parameters page"""
998
1004
  return templates.TemplateResponse(
1005
+ request=request,
999
1006
  name="job_params.html",
1000
1007
  context=(
1001
1008
  {
1002
- "request": request,
1003
1009
  "project_names_url": os.getenv(
1004
1010
  "AIND_METADATA_SERVICE_PROJECT_NAMES_URL"
1005
1011
  ),
@@ -1122,10 +1128,10 @@ async def admin(request: Request):
1122
1128
  user = {"name": "local user"}
1123
1129
  if user:
1124
1130
  return templates.TemplateResponse(
1131
+ request=request,
1125
1132
  name="admin.html",
1126
1133
  context=(
1127
1134
  {
1128
- "request": request,
1129
1135
  "project_names_url": project_names_url,
1130
1136
  "user_name": user.get("name", "unknown"),
1131
1137
  "user_email": user.get("email", "unknown"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aind-data-transfer-service
3
- Version: 1.15.0
3
+ Version: 1.16.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
@@ -28,10 +28,10 @@ Requires-Dist: aind-data-transfer-models==0.17.0; extra == "server"
28
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
- Requires-Dist: fastapi; extra == "server"
31
+ Requires-Dist: fastapi>=0.115.13; extra == "server"
32
32
  Requires-Dist: httpx; extra == "server"
33
33
  Requires-Dist: jinja2; extra == "server"
34
- Requires-Dist: starlette; extra == "server"
34
+ Requires-Dist: starlette<0.47.0,>=0.40.0; extra == "server"
35
35
  Requires-Dist: starlette_wtf; extra == "server"
36
36
  Requires-Dist: uvicorn[standard]; extra == "server"
37
37
  Requires-Dist: wtforms; extra == "server"
@@ -87,6 +87,7 @@ tests/resources/get_secrets_response.json
87
87
  tests/resources/job_upload_template.xlsx
88
88
  tests/resources/legacy_sample.csv
89
89
  tests/resources/legacy_sample2.csv
90
+ tests/resources/nested_sample.csv
90
91
  tests/resources/new_sample.csv
91
92
  tests/resources/sample.csv
92
93
  tests/resources/sample.xlsx
@@ -21,10 +21,10 @@ aind-data-transfer-models==0.17.0
21
21
  aind-metadata-mapper==0.23.0
22
22
  boto3
23
23
  boto3-stubs[ssm]
24
- fastapi
24
+ fastapi>=0.115.13
25
25
  httpx
26
26
  jinja2
27
- starlette
27
+ starlette<0.47.0,>=0.40.0
28
28
  starlette_wtf
29
29
  uvicorn[standard]
30
30
  wtforms
@@ -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, ecephys, dir/data_set_1, gamma fix colorspace, 123454, ecephys, 2020-10-10 14:10:10, ecephys
@@ -9,11 +9,16 @@ from pathlib import Path
9
9
  from aind_data_schema_models.modalities import Modality
10
10
  from aind_data_schema_models.platforms import Platform
11
11
 
12
- from aind_data_transfer_service.configs.csv_handler import map_csv_row_to_job
12
+ from aind_data_transfer_service.configs.csv_handler import (
13
+ create_nested_dict,
14
+ map_csv_row_to_job,
15
+ nested_update,
16
+ )
13
17
  from aind_data_transfer_service.models.core import Task, UploadJobConfigsV2
14
18
 
15
19
  RESOURCES_DIR = Path(os.path.dirname(os.path.realpath(__file__))) / "resources"
16
20
  SAMPLE_FILE = RESOURCES_DIR / "new_sample.csv"
21
+ NESTED_SAMPLE_FILE = RESOURCES_DIR / "nested_sample.csv"
17
22
  LEGACY_FILE = RESOURCES_DIR / "legacy_sample.csv"
18
23
  LEGACY_FILE_2 = RESOURCES_DIR / "legacy_sample2.csv"
19
24
 
@@ -21,6 +26,26 @@ LEGACY_FILE_2 = RESOURCES_DIR / "legacy_sample2.csv"
21
26
  class TestCsvHandler(unittest.TestCase):
22
27
  """Tests methods in csv_handler module"""
23
28
 
29
+ def test_create_nested_dict(self):
30
+ """Tests create_nested_dict method"""
31
+
32
+ key = "abc.def.ghi"
33
+ value = "my_val"
34
+ nested_dict = dict()
35
+ create_nested_dict(
36
+ dict_to_update=nested_dict, key_string=key, value=value
37
+ )
38
+ expected_dict = {"abc": {"def": {"ghi": "my_val"}}}
39
+ self.assertEqual(expected_dict, nested_dict)
40
+
41
+ def test_nested_update(self):
42
+ """Tests nested_update method."""
43
+ current_dict = {"abc": {"def": {"ghi": "my_val"}}}
44
+ updates = {"abc": {"def": {"jkl": 123}}}
45
+ nested_update(current_dict, updates)
46
+ expected_dict = {"abc": {"def": {"ghi": "my_val", "jkl": 123}}}
47
+ self.assertEqual(expected_dict, current_dict)
48
+
24
49
  def test_map_csv_row_to_job(self):
25
50
  """Tests map_csv_row_to_job method"""
26
51
 
@@ -87,6 +112,42 @@ class TestCsvHandler(unittest.TestCase):
87
112
  ]
88
113
  self.assertEqual(expected_jobs, jobs)
89
114
 
115
+ def test_map_nested_csv_row_to_job(self):
116
+ """Tests map_csv_row_to_job method with nested keys"""
117
+
118
+ jobs = []
119
+ with open(NESTED_SAMPLE_FILE, newline="") as csvfile:
120
+ reader = csv.DictReader(csvfile, skipinitialspace=True)
121
+ for row in reader:
122
+ jobs.append(map_csv_row_to_job(row))
123
+ expected_jobs = [
124
+ UploadJobConfigsV2(
125
+ job_type="ecephys",
126
+ s3_bucket="default",
127
+ project_name="Ephys Platform",
128
+ platform=Platform.ECEPHYS,
129
+ modalities=[Modality.ECEPHYS],
130
+ subject_id="123454",
131
+ acq_datetime=datetime(2020, 10, 10, 14, 10, 10),
132
+ tasks={
133
+ "modality_transformation_settings": {
134
+ "ecephys": Task(
135
+ skip_task=False,
136
+ job_settings={
137
+ "input_source": "dir/data_set_1",
138
+ "compression_requested": {
139
+ "compression_enum": "gamma fix colorspace"
140
+ },
141
+ },
142
+ )
143
+ }
144
+ },
145
+ s3_prefix="ecephys_123454_2020-10-10_14-10-10",
146
+ )
147
+ ]
148
+
149
+ self.assertEqual(expected_jobs, jobs)
150
+
90
151
  def test_map_legacy_csv_row_to_job(self):
91
152
  """Tests map_csv_row_to_job method"""
92
153
 
@@ -105,10 +166,7 @@ class TestCsvHandler(unittest.TestCase):
105
166
  subject_id="123454",
106
167
  acq_datetime=datetime(2020, 10, 10, 14, 10, 10),
107
168
  tasks={
108
- "check_s3_folder_exists_task": Task(
109
- skip_task=True,
110
- ),
111
- "final_check_s3_folder_exist": Task(
169
+ "check_s3_folder_exists": Task(
112
170
  skip_task=True,
113
171
  ),
114
172
  "modality_transformation_settings": {