dkist-processing-common 10.5.4__py3-none-any.whl → 12.1.0rc1__py3-none-any.whl

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 (122) hide show
  1. changelog/280.misc.rst +1 -0
  2. changelog/282.feature.2.rst +2 -0
  3. changelog/282.feature.rst +2 -0
  4. changelog/284.feature.rst +1 -0
  5. changelog/285.feature.rst +2 -0
  6. changelog/285.misc.rst +2 -0
  7. changelog/286.feature.rst +2 -0
  8. changelog/287.misc.rst +1 -0
  9. dkist_processing_common/__init__.py +1 -0
  10. dkist_processing_common/_util/constants.py +1 -0
  11. dkist_processing_common/_util/graphql.py +1 -0
  12. dkist_processing_common/_util/scratch.py +9 -9
  13. dkist_processing_common/_util/tags.py +1 -0
  14. dkist_processing_common/codecs/array.py +20 -0
  15. dkist_processing_common/codecs/asdf.py +9 -3
  16. dkist_processing_common/codecs/basemodel.py +22 -0
  17. dkist_processing_common/codecs/bytes.py +1 -0
  18. dkist_processing_common/codecs/fits.py +37 -9
  19. dkist_processing_common/codecs/iobase.py +1 -0
  20. dkist_processing_common/codecs/json.py +1 -0
  21. dkist_processing_common/codecs/path.py +1 -0
  22. dkist_processing_common/codecs/quality.py +1 -1
  23. dkist_processing_common/codecs/str.py +1 -0
  24. dkist_processing_common/config.py +64 -25
  25. dkist_processing_common/manual.py +6 -8
  26. dkist_processing_common/models/constants.py +373 -37
  27. dkist_processing_common/models/dkist_location.py +27 -0
  28. dkist_processing_common/models/fits_access.py +48 -0
  29. dkist_processing_common/models/flower_pot.py +231 -9
  30. dkist_processing_common/models/fried_parameter.py +41 -0
  31. dkist_processing_common/models/graphql.py +66 -75
  32. dkist_processing_common/models/input_dataset.py +117 -0
  33. dkist_processing_common/models/message.py +1 -1
  34. dkist_processing_common/models/message_queue_binding.py +1 -1
  35. dkist_processing_common/models/metric_code.py +2 -0
  36. dkist_processing_common/models/parameters.py +65 -28
  37. dkist_processing_common/models/quality.py +50 -5
  38. dkist_processing_common/models/tags.py +23 -21
  39. dkist_processing_common/models/task_name.py +3 -2
  40. dkist_processing_common/models/telemetry.py +28 -0
  41. dkist_processing_common/models/wavelength.py +3 -1
  42. dkist_processing_common/parsers/average_bud.py +46 -0
  43. dkist_processing_common/parsers/cs_step.py +13 -12
  44. dkist_processing_common/parsers/dsps_repeat.py +6 -4
  45. dkist_processing_common/parsers/experiment_id_bud.py +12 -4
  46. dkist_processing_common/parsers/id_bud.py +42 -27
  47. dkist_processing_common/parsers/l0_fits_access.py +5 -3
  48. dkist_processing_common/parsers/l1_fits_access.py +51 -23
  49. dkist_processing_common/parsers/lookup_bud.py +125 -0
  50. dkist_processing_common/parsers/near_bud.py +21 -20
  51. dkist_processing_common/parsers/observing_program_id_bud.py +24 -0
  52. dkist_processing_common/parsers/proposal_id_bud.py +13 -5
  53. dkist_processing_common/parsers/quality.py +2 -0
  54. dkist_processing_common/parsers/retarder.py +32 -0
  55. dkist_processing_common/parsers/single_value_single_key_flower.py +6 -1
  56. dkist_processing_common/parsers/task.py +8 -6
  57. dkist_processing_common/parsers/time.py +178 -72
  58. dkist_processing_common/parsers/unique_bud.py +21 -22
  59. dkist_processing_common/parsers/wavelength.py +5 -3
  60. dkist_processing_common/tasks/__init__.py +3 -2
  61. dkist_processing_common/tasks/assemble_movie.py +4 -3
  62. dkist_processing_common/tasks/base.py +59 -60
  63. dkist_processing_common/tasks/l1_output_data.py +54 -53
  64. dkist_processing_common/tasks/mixin/globus.py +24 -27
  65. dkist_processing_common/tasks/mixin/interservice_bus.py +1 -0
  66. dkist_processing_common/tasks/mixin/metadata_store.py +108 -243
  67. dkist_processing_common/tasks/mixin/object_store.py +22 -0
  68. dkist_processing_common/tasks/mixin/quality/__init__.py +1 -0
  69. dkist_processing_common/tasks/mixin/quality/_base.py +8 -1
  70. dkist_processing_common/tasks/mixin/quality/_metrics.py +166 -14
  71. dkist_processing_common/tasks/output_data_base.py +4 -3
  72. dkist_processing_common/tasks/parse_l0_input_data.py +277 -15
  73. dkist_processing_common/tasks/quality_metrics.py +9 -9
  74. dkist_processing_common/tasks/teardown.py +7 -7
  75. dkist_processing_common/tasks/transfer_input_data.py +67 -69
  76. dkist_processing_common/tasks/trial_catalog.py +77 -17
  77. dkist_processing_common/tasks/trial_output_data.py +16 -17
  78. dkist_processing_common/tasks/write_l1.py +102 -72
  79. dkist_processing_common/tests/conftest.py +32 -173
  80. dkist_processing_common/tests/mock_metadata_store.py +271 -0
  81. dkist_processing_common/tests/test_assemble_movie.py +4 -4
  82. dkist_processing_common/tests/test_assemble_quality.py +32 -4
  83. dkist_processing_common/tests/test_base.py +5 -19
  84. dkist_processing_common/tests/test_codecs.py +103 -12
  85. dkist_processing_common/tests/test_constants.py +15 -0
  86. dkist_processing_common/tests/test_dkist_location.py +15 -0
  87. dkist_processing_common/tests/test_fits_access.py +56 -19
  88. dkist_processing_common/tests/test_flower_pot.py +147 -5
  89. dkist_processing_common/tests/test_fried_parameter.py +27 -0
  90. dkist_processing_common/tests/test_input_dataset.py +78 -361
  91. dkist_processing_common/tests/test_interservice_bus.py +1 -0
  92. dkist_processing_common/tests/test_interservice_bus_mixin.py +1 -1
  93. dkist_processing_common/tests/test_manual_processing.py +33 -0
  94. dkist_processing_common/tests/test_output_data_base.py +5 -7
  95. dkist_processing_common/tests/test_parameters.py +71 -22
  96. dkist_processing_common/tests/test_parse_l0_input_data.py +115 -32
  97. dkist_processing_common/tests/test_publish_catalog_messages.py +2 -24
  98. dkist_processing_common/tests/test_quality.py +1 -0
  99. dkist_processing_common/tests/test_quality_mixin.py +255 -23
  100. dkist_processing_common/tests/test_scratch.py +2 -1
  101. dkist_processing_common/tests/test_stems.py +511 -168
  102. dkist_processing_common/tests/test_submit_dataset_metadata.py +3 -7
  103. dkist_processing_common/tests/test_tags.py +1 -0
  104. dkist_processing_common/tests/test_task_name.py +1 -1
  105. dkist_processing_common/tests/test_task_parsing.py +17 -7
  106. dkist_processing_common/tests/test_teardown.py +28 -24
  107. dkist_processing_common/tests/test_transfer_input_data.py +270 -125
  108. dkist_processing_common/tests/test_transfer_l1_output_data.py +2 -3
  109. dkist_processing_common/tests/test_trial_catalog.py +83 -8
  110. dkist_processing_common/tests/test_trial_output_data.py +46 -73
  111. dkist_processing_common/tests/test_workflow_task_base.py +8 -10
  112. dkist_processing_common/tests/test_write_l1.py +298 -76
  113. dkist_processing_common-12.1.0rc1.dist-info/METADATA +265 -0
  114. dkist_processing_common-12.1.0rc1.dist-info/RECORD +134 -0
  115. {dkist_processing_common-10.5.4.dist-info → dkist_processing_common-12.1.0rc1.dist-info}/WHEEL +1 -1
  116. docs/conf.py +1 -0
  117. docs/index.rst +1 -1
  118. docs/landing_page.rst +13 -0
  119. dkist_processing_common/tasks/mixin/input_dataset.py +0 -166
  120. dkist_processing_common-10.5.4.dist-info/METADATA +0 -175
  121. dkist_processing_common-10.5.4.dist-info/RECORD +0 -112
  122. {dkist_processing_common-10.5.4.dist-info → dkist_processing_common-12.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,13 @@
1
1
  import json
2
- from copy import copy
3
- from datetime import datetime
4
- from pathlib import Path
5
2
  from typing import Any
6
3
  from uuid import uuid4
7
4
 
8
5
  import pytest
9
6
 
7
+ from dkist_processing_common.codecs.basemodel import basemodel_decoder
8
+ from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
10
9
  from dkist_processing_common.models.tags import Tag
11
- from dkist_processing_common.tasks.mixin.input_dataset import InputDatasetMixin
12
- from dkist_processing_common.tasks.mixin.input_dataset import InputDatasetObject
13
- from dkist_processing_common.tests.conftest import create_parameter_files
14
- from dkist_processing_common.tests.conftest import InputDatasetTask
10
+ from dkist_processing_common.tests.mock_metadata_store import input_dataset_parameters_part_factory
15
11
 
16
12
 
17
13
  def input_dataset_frames_part_factory(bucket_count: int = 1) -> list[dict]:
@@ -29,136 +25,45 @@ def flatten_frame_parts(frame_parts: list[dict]) -> list[tuple[str, str]]:
29
25
  return result
30
26
 
31
27
 
32
- def input_dataset_parameters_part_factory(
33
- parameter_count: int = 1,
34
- parameter_value_count: int = 1,
35
- has_date: bool = False,
36
- has_file: bool = False,
37
- ) -> list[dict]:
38
- result = [
39
- {
40
- "parameterName": uuid4().hex[:6],
41
- "parameterValues": [
42
- {"parameterValueId": i, "parameterValue": json.dumps(uuid4().hex)}
43
- for i in range(parameter_value_count)
44
- ],
45
- }
46
- for _ in range(parameter_count)
47
- ]
48
- if has_date:
49
- for data in result:
50
- data["parameterValueStartDate"] = datetime(2022, 9, 14).isoformat()[:10]
51
- if has_file:
52
- for data in result:
53
- param_list = data["parameterValues"]
54
- for item in param_list:
55
- item["parameterValue"] = json.dumps(
56
- {
57
- "__file__": {
58
- "bucket": "data",
59
- "objectKey": f"parameters/{data['parameterName']}/{uuid4().hex}.dat",
60
- }
61
- }
62
- )
63
- return result
64
-
65
-
66
28
  @pytest.mark.parametrize(
67
29
  "input_dataset_parts",
68
30
  [
69
- pytest.param((None, Tag.input_dataset_observe_frames()), id="empty"),
70
31
  pytest.param(
71
32
  (input_dataset_frames_part_factory(), Tag.input_dataset_observe_frames()),
72
- id="single_bucket",
33
+ id="observe_single_bucket",
73
34
  ),
74
35
  pytest.param(
75
36
  (input_dataset_frames_part_factory(bucket_count=2), Tag.input_dataset_observe_frames()),
76
- id="multi_bucket",
37
+ id="observe_multi_bucket",
77
38
  ),
78
- ],
79
- )
80
- def test_input_dataset_observe_frames_part_document(
81
- task_with_input_dataset, input_dataset_parts: tuple[Any, str]
82
- ):
83
- """
84
- Given: A task with an input dataset observe frames part document tagged as such
85
- When: Accessing the document via the InputDatasetMixIn
86
- Then: The contents of the file are returned
87
- """
88
- doc_part, _ = input_dataset_parts
89
- task = task_with_input_dataset
90
- assert task.input_dataset_observe_frames_part_document == doc_part
91
-
92
-
93
- @pytest.mark.parametrize(
94
- "input_dataset_parts",
95
- [
96
- pytest.param((None, Tag.input_dataset_calibration_frames()), id="empty"),
97
39
  pytest.param(
98
40
  (input_dataset_frames_part_factory(), Tag.input_dataset_calibration_frames()),
99
- id="single_bucket",
41
+ id="calib_single_bucket",
100
42
  ),
101
43
  pytest.param(
102
44
  (
103
45
  input_dataset_frames_part_factory(bucket_count=2),
104
46
  Tag.input_dataset_calibration_frames(),
105
47
  ),
106
- id="multi_bucket",
107
- ),
108
- ],
109
- )
110
- def test_input_dataset_calibration_frames_part_document(
111
- task_with_input_dataset, input_dataset_parts: tuple[Any, str]
112
- ):
113
- """
114
- Given: A task with an input dataset calibration frames part document tagged as such
115
- When: Accessing the document via the InputDatasetMixIn
116
- Then: The contents of the file are returned
117
- """
118
- doc_part, _ = input_dataset_parts
119
- task = task_with_input_dataset
120
- assert task.input_dataset_calibration_frames_part_document == doc_part
121
-
122
-
123
- @pytest.mark.parametrize(
124
- "input_dataset_parts",
125
- [
126
- pytest.param((None, Tag.input_dataset_parameters()), id="empty"),
127
- pytest.param(
128
- (input_dataset_parameters_part_factory(), Tag.input_dataset_parameters()),
129
- id="single_param_no_date",
130
- ),
131
- pytest.param(
132
- (
133
- input_dataset_parameters_part_factory(parameter_count=2),
134
- Tag.input_dataset_parameters(),
135
- ),
136
- id="multi_param_no_date",
137
- ),
138
- pytest.param(
139
- (input_dataset_parameters_part_factory(has_date=True), Tag.input_dataset_parameters()),
140
- id="single_param_with_date",
141
- ),
142
- pytest.param(
143
- (
144
- input_dataset_parameters_part_factory(parameter_count=2, has_date=True),
145
- Tag.input_dataset_parameters(),
146
- ),
147
- id="multi_param_with_date",
48
+ id="calib_multi_bucket",
148
49
  ),
149
50
  ],
150
51
  )
151
- def test_input_dataset_parameters_part_document(
52
+ def test_input_dataset_frames_part_document(
152
53
  task_with_input_dataset, input_dataset_parts: tuple[Any, str]
153
54
  ):
154
55
  """
155
- Given: A task with an input dataset parameters part document tagged as such
156
- When: Accessing the document via the InputDatasetMixIn
157
- Then: The contents of the file are returned
56
+ Given: A task with an input dataset frames part document already written to file
57
+ When: Reading the file into a validated model
58
+ Then: The correct contents of the file are loaded
158
59
  """
159
- doc_part, _ = input_dataset_parts
60
+ doc_part, tag = input_dataset_parts
160
61
  task = task_with_input_dataset
161
- assert task.input_dataset_parameters_part_document == doc_part
62
+ doc_from_file = next(
63
+ task.read(tags=tag, decoder=basemodel_decoder, model=InputDatasetPartDocumentList)
64
+ )
65
+ frames = [frames.model_dump() for frames in doc_from_file.doc_list]
66
+ assert frames == doc_part
162
67
 
163
68
 
164
69
  @pytest.mark.parametrize(
@@ -174,24 +79,15 @@ def test_input_dataset_parameters_part_document(
174
79
  pytest.param(
175
80
  [
176
81
  (input_dataset_frames_part_factory(), Tag.input_dataset_observe_frames()),
177
- (None, Tag.input_dataset_calibration_frames()),
178
82
  ],
179
83
  id="observe1_cal0_single_bucket",
180
84
  ),
181
85
  pytest.param(
182
86
  [
183
- (None, Tag.input_dataset_observe_frames()),
184
87
  (input_dataset_frames_part_factory(), Tag.input_dataset_calibration_frames()),
185
88
  ],
186
89
  id="observe0_cal1_single_bucket",
187
90
  ),
188
- pytest.param(
189
- [
190
- (None, Tag.input_dataset_observe_frames()),
191
- (None, Tag.input_dataset_calibration_frames()),
192
- ],
193
- id="observe0_cal0_single_bucket",
194
- ),
195
91
  pytest.param(
196
92
  [
197
93
  (
@@ -211,13 +107,11 @@ def test_input_dataset_parameters_part_document(
211
107
  input_dataset_frames_part_factory(bucket_count=2),
212
108
  Tag.input_dataset_observe_frames(),
213
109
  ),
214
- (None, Tag.input_dataset_calibration_frames()),
215
110
  ],
216
111
  id="observe1_cal0_multi_bucket",
217
112
  ),
218
113
  pytest.param(
219
114
  [
220
- (None, Tag.input_dataset_observe_frames()),
221
115
  (
222
116
  input_dataset_frames_part_factory(bucket_count=2),
223
117
  Tag.input_dataset_calibration_frames(),
@@ -225,21 +119,17 @@ def test_input_dataset_parameters_part_document(
225
119
  ],
226
120
  id="observe0_cal1_multi_bucket",
227
121
  ),
228
- pytest.param(
229
- [
230
- (None, Tag.input_dataset_observe_frames()),
231
- (None, Tag.input_dataset_calibration_frames()),
232
- ],
233
- id="observe0_cal0_multi_bucket",
234
- ),
235
122
  ],
236
123
  )
237
- def test_input_dataset_frames(task_with_input_dataset, input_dataset_parts: list[tuple[Any, str]]):
124
+ def test_input_dataset_frames_combination(
125
+ task_with_input_dataset, input_dataset_parts: list[tuple[Any, str]]
126
+ ):
238
127
  """
239
- Given: a task with the InputDatasetMixin
240
- When: getting the frames in the input dataset
241
- Then: it matches the frames used to create the input dataset
128
+ Given: A task with both types of input dataset frame documents written to files
129
+ When: Reading the file and validating into models
130
+ Then: The correct files are returned by the input_dataset_objects method of InputDatasetFrames
242
131
  """
132
+ # Given
243
133
  doc_parts = [part for part, _ in input_dataset_parts]
244
134
  task = task_with_input_dataset
245
135
  expected = []
@@ -247,7 +137,29 @@ def test_input_dataset_frames(task_with_input_dataset, input_dataset_parts: list
247
137
  if part:
248
138
  expected.extend(flatten_frame_parts(part))
249
139
  expected_set = set(expected)
250
- actual = [(frame.bucket, frame.object_key) for frame in task.input_dataset_frames]
140
+ # When
141
+ frames = []
142
+ observe_frames = next(
143
+ task.read(
144
+ tags=Tag.input_dataset_observe_frames(),
145
+ decoder=basemodel_decoder,
146
+ model=InputDatasetPartDocumentList,
147
+ ),
148
+ None,
149
+ )
150
+ frames += observe_frames.doc_list if observe_frames else []
151
+ calibration_frames = next(
152
+ task.read(
153
+ tags=Tag.input_dataset_calibration_frames(),
154
+ decoder=basemodel_decoder,
155
+ model=InputDatasetPartDocumentList,
156
+ ),
157
+ None,
158
+ )
159
+ frames += calibration_frames.doc_list if calibration_frames else []
160
+ # Then
161
+ frames_objects = sum([f.input_dataset_objects for f in frames], [])
162
+ actual = [(frame.bucket, frame.object_key) for frame in frames_objects]
251
163
  actual_set = set(actual)
252
164
  assert len(actual) == len(actual_set)
253
165
  assert actual_set.difference(expected_set) == set()
@@ -256,132 +168,45 @@ def test_input_dataset_frames(task_with_input_dataset, input_dataset_parts: list
256
168
  @pytest.mark.parametrize(
257
169
  "input_dataset_parts",
258
170
  [
259
- pytest.param((None, Tag.input_dataset_parameters()), id="empty"),
260
171
  pytest.param(
261
172
  (input_dataset_parameters_part_factory(), Tag.input_dataset_parameters()),
262
173
  id="single_param_no_date_no_file",
263
174
  ),
264
175
  pytest.param(
265
176
  (input_dataset_parameters_part_factory(has_file=True), Tag.input_dataset_parameters()),
266
- id="single_param_no_date",
177
+ id="single_param_no_date_with_file",
267
178
  ),
268
179
  pytest.param(
269
- (
270
- input_dataset_parameters_part_factory(parameter_count=2, has_file=True),
271
- Tag.input_dataset_parameters(),
272
- ),
273
- id="multi_param_no_date",
274
- ),
275
- pytest.param(
276
- (
277
- input_dataset_parameters_part_factory(parameter_value_count=2, has_file=True),
278
- Tag.input_dataset_parameters(),
279
- ),
280
- id="multi_param_values_no_date",
180
+ (input_dataset_parameters_part_factory(has_date=True), Tag.input_dataset_parameters()),
181
+ id="single_param_with_date_no_file",
281
182
  ),
282
183
  pytest.param(
283
184
  (
284
185
  input_dataset_parameters_part_factory(has_date=True, has_file=True),
285
186
  Tag.input_dataset_parameters(),
286
187
  ),
287
- id="single_param_with_date",
288
- ),
289
- pytest.param(
290
- (
291
- input_dataset_parameters_part_factory(
292
- parameter_count=2, has_date=True, has_file=True
293
- ),
294
- Tag.input_dataset_parameters(),
295
- ),
296
- id="multi_param_with_date",
297
- ),
298
- ],
299
- )
300
- def test_input_dataset_parameters(
301
- task_with_input_dataset, input_dataset_parts: list[tuple[Any, str]]
302
- ):
303
- """
304
- Given: a task with the InputDatasetMixin
305
- When: getting the parameters in the input dataset
306
- Then: the names of the parameters match the keys in the returned dictionary
307
- """
308
- task = task_with_input_dataset
309
- doc_part, _ = input_dataset_parts
310
- doc_part = doc_part or [] # None case parsing of expected values
311
- """
312
- expected_parameters is a dict with the parameter names as the keys
313
- and the values are a list of value dicts for each parameter:
314
- expected_parameters =
315
- { 'parameter_name_1': [param_dict_1, param_dict_2, ...],
316
- 'parameter_name_2': [param_dict_1, param_dict_2, ...],
317
- ...
318
- }
319
- where the param_dicts have the following format:
320
- sample_param_dict =
321
- { "parameterValueId": <param_id>,
322
- "parameterValue": <param_value>,
323
- "parameterValueStartDate": <start_date>
324
- }
325
- """
326
- expected_parameters = dict()
327
- for item in doc_part:
328
- expected_parameters[item["parameterName"]] = item["parameterValues"]
329
- create_parameter_files(task, expected_parameters)
330
- # key is param name, values is list of InputDatasetParameterValue objects
331
- for key, values in task.input_dataset_parameters.items():
332
- assert key in expected_parameters
333
- expected_values = expected_parameters[key]
334
- # Iterate through multiple values if they exist
335
- for value in values:
336
- # Find the matching expected value for this value object
337
- expected_value = [
338
- item
339
- for item in expected_values
340
- if value.parameter_value_id == item["parameterValueId"]
341
- ]
342
- # Make sure there's only one value
343
- assert len(expected_value) == 1
344
- # Now check the value
345
- expected_value = expected_value[0]
346
- assert value.parameter_value == json.loads(
347
- expected_value["parameterValue"], object_hook=task._decode_parameter_value
348
- )
349
- expected_date = expected_value.get("parameterValueStartDate", datetime(1, 1, 1))
350
- assert value.parameter_value_start_date == expected_date
351
-
352
-
353
- @pytest.mark.parametrize(
354
- "input_dataset_parts",
355
- [
356
- pytest.param((None, Tag.input_dataset_parameters()), id="empty"),
357
- pytest.param(
358
- (input_dataset_parameters_part_factory(), Tag.input_dataset_parameters()),
359
- id="single_param_no_date_no_file",
360
- ),
361
- pytest.param(
362
- (input_dataset_parameters_part_factory(has_file=True), Tag.input_dataset_parameters()),
363
- id="single_param_no_date",
188
+ id="single_param_with_date_with_file",
364
189
  ),
365
190
  pytest.param(
366
191
  (
367
- input_dataset_parameters_part_factory(parameter_count=2, has_file=True),
192
+ input_dataset_parameters_part_factory(parameter_count=2),
368
193
  Tag.input_dataset_parameters(),
369
194
  ),
370
- id="multi_param_no_date",
195
+ id="multi_param_no_date_no_file",
371
196
  ),
372
197
  pytest.param(
373
198
  (
374
- input_dataset_parameters_part_factory(parameter_value_count=2, has_file=True),
199
+ input_dataset_parameters_part_factory(parameter_count=2, has_date=True),
375
200
  Tag.input_dataset_parameters(),
376
201
  ),
377
- id="multi_param_values_no_date",
202
+ id="multi_param_with_date_no_file",
378
203
  ),
379
204
  pytest.param(
380
205
  (
381
- input_dataset_parameters_part_factory(has_date=True, has_file=True),
206
+ input_dataset_parameters_part_factory(parameter_count=2, has_file=True),
382
207
  Tag.input_dataset_parameters(),
383
208
  ),
384
- id="single_param_with_date",
209
+ id="multi_param_no_date_with_file",
385
210
  ),
386
211
  pytest.param(
387
212
  (
@@ -390,138 +215,30 @@ def test_input_dataset_parameters(
390
215
  ),
391
216
  Tag.input_dataset_parameters(),
392
217
  ),
393
- id="multi_param_with_date",
394
- ),
395
- ],
396
- )
397
- def test_input_dataset_parameter_objects(
398
- task_with_input_dataset, input_dataset_parts: list[tuple[Any, str]]
399
- ):
400
- """
401
- Given: a task with the InputDatasetMixin
402
- When: getting the parameters objects in the input dataset
403
- Then: the InputDatsetObjects returned by the task method match the objects defined by the input
404
- dataset doc part
405
- """
406
- task = task_with_input_dataset
407
- doc_part, _ = input_dataset_parts
408
- doc_part = doc_part or [] # None case parsing of expected values
409
-
410
- # Create a list of InputDatasetObjects from the input dataset doc part
411
- expected_parameters = list()
412
- for param_item in doc_part:
413
- param_values_list = param_item["parameterValues"]
414
- for param_value_dict in param_values_list:
415
- if "__file__" in param_value_dict["parameterValue"]:
416
- file_dict = json.loads(
417
- param_value_dict["parameterValue"], object_hook=task._decode_parameter_value
418
- )
419
- expected_parameters.append(
420
- InputDatasetObject(
421
- bucket=file_dict["bucket"], object_key=file_dict["objectKey"]
422
- )
423
- )
424
- # Check that each InputDatasetObject returned by the task is in the list of expected parameters
425
- input_dataset_parameter_objects = task.input_dataset_parameter_objects
426
- assert len(input_dataset_parameter_objects) == len(expected_parameters)
427
- for input_dataset_object in input_dataset_parameter_objects:
428
- assert input_dataset_object in expected_parameters
429
-
430
-
431
- @pytest.mark.parametrize(
432
- "input_parameter_dict",
433
- [
434
- {"bucket": "data", "objectKey": "parameters/805c46/714ff939158b4253859cde5e5d6f62c3.dat"},
435
- {
436
- "__file__": {
437
- "bucket": "data",
438
- "objectKey": "parameters/805c46/714ff939158b4253859cde5e5d6f62c3.dat",
439
- }
440
- },
441
- {"key_name_1": "value_1", "key_name_2": "value_2", "key_name_3": "value_3"},
442
- ],
443
- )
444
- def test_convert_parameter_file_to_path(recipe_run_id, input_parameter_dict: dict):
445
- """
446
- Given: a parameter value field to be json decoded
447
- When: passing the parameter value string to the json decoder hook
448
- Then: the hook passes non-file parameter strings without change and modifies file parameter strings
449
- by replacing the __file__ dict in the value string with a bucket field, an objectKey field
450
- and adds a param_path field and an is_file field
451
- """
452
- # Initial test with no tags
453
- with InputDatasetTask(
454
- recipe_run_id=recipe_run_id,
455
- workflow_name="workflow_name",
456
- workflow_version="workflow_version",
457
- ) as task:
458
- # Test with no tags...
459
- input_dict = input_parameter_dict
460
- output_dict = task._decode_parameter_value(input_dict)
461
- if "__file__" not in input_dict:
462
- assert input_dict == output_dict
463
- else:
464
- value_dict = input_dict["__file__"]
465
- assert output_dict["bucket"] == value_dict["bucket"]
466
- assert output_dict["objectKey"] == value_dict["objectKey"]
467
- assert output_dict["is_file"]
468
- assert output_dict["param_path"] is None
469
- # Test with tags
470
- if "__file__" not in input_dict:
471
- output_dict = task._decode_parameter_value(input_dict)
472
- assert input_dict == output_dict
473
- else:
474
- # Create the destination path
475
- param_path = input_dict["__file__"]["objectKey"]
476
- destination_path = task.scratch.absolute_path(param_path)
477
- if not destination_path.parent.exists():
478
- destination_path.parent.mkdir(parents=True, exist_ok=True)
479
- destination_path.write_text(data="")
480
- task.tag(path=destination_path, tags=Tag.parameter(destination_path.name))
481
- output_dict = task._decode_parameter_value(input_dict)
482
- value_dict = input_dict["__file__"]
483
- assert output_dict["bucket"] == value_dict["bucket"]
484
- assert output_dict["objectKey"] == value_dict["objectKey"]
485
- assert output_dict["is_file"]
486
- assert output_dict["param_path"] == destination_path
487
-
488
-
489
- @pytest.mark.parametrize(
490
- "input_dataset_parts",
491
- [
492
- pytest.param(
493
- [
494
- (input_dataset_frames_part_factory(), Tag.input_dataset_observe_frames()),
495
- (input_dataset_frames_part_factory(), Tag.input_dataset_observe_frames()),
496
- ],
497
- id="observe",
498
- ),
499
- pytest.param(
500
- [
501
- (input_dataset_frames_part_factory(), Tag.input_dataset_calibration_frames()),
502
- (input_dataset_frames_part_factory(), Tag.input_dataset_calibration_frames()),
503
- ],
504
- id="calibration",
505
- ),
506
- pytest.param(
507
- [
508
- (input_dataset_frames_part_factory(), Tag.input_dataset_parameters()),
509
- (input_dataset_frames_part_factory(), Tag.input_dataset_parameters()),
510
- ],
511
- id="params",
218
+ id="multi_param_with_date_with_file",
512
219
  ),
513
220
  ],
514
221
  )
515
- def test_multiple_input_dataset_parts(
516
- task_with_input_dataset, input_dataset_parts: list[tuple[Any, str]]
517
- ):
222
+ def test_input_dataset_parameters(task_with_input_dataset, input_dataset_parts: tuple[Any, str]):
518
223
  """
519
- Given: a task with the InputDatasetMixin and multiple tagged input datasets
520
- When: reading the input dataset document
521
- Then: an error is raised
224
+ Given: A task with an input dataset parameters part document written to file
225
+ When: Reading the file and validating into models
226
+ Then: The correct contents of the file, including file parameters, are loaded
522
227
  """
228
+ doc_part, tag = input_dataset_parts
523
229
  task = task_with_input_dataset
524
- with pytest.raises(ValueError):
525
- task.input_dataset_parameters_part_document
526
- task.input_dataset_observe_frames_part_document
527
- task.input_dataset_calibration_frames_part_document
230
+ doc_from_file = next(
231
+ task.read(tags=tag, decoder=basemodel_decoder, model=InputDatasetPartDocumentList)
232
+ )
233
+
234
+ params = [params.model_dump() for params in doc_from_file.doc_list]
235
+ assert params == doc_part
236
+ expected_files = []
237
+ for item in doc_part or []:
238
+ for val in item["parameterValues"]:
239
+ if "__file__" in val["parameterValue"]:
240
+ file_dict = json.loads(val["parameterValue"])
241
+ expected_files.append(file_dict["__file__"])
242
+ file_objects = sum([d.input_dataset_objects for d in doc_from_file.doc_list], [])
243
+ file_objects_dump = [f.model_dump() for f in file_objects]
244
+ assert file_objects_dump == expected_files
@@ -1,4 +1,5 @@
1
1
  """Tests for the interservice bus mixin for a WorkflowDataTaskBase subclass"""
2
+
2
3
  from typing import Type
3
4
  from uuid import uuid4
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Tests for the interservice bus mixin for a WorkflowDataTaskBase subclass"""
2
+
2
3
  import logging
3
4
  from typing import Dict
4
5
  from typing import Type
@@ -15,7 +16,6 @@ from dkist_processing_common.config import common_configurations
15
16
  from dkist_processing_common.models.message_queue_binding import common_message_queue_bindings
16
17
  from dkist_processing_common.tasks import WorkflowTaskBase
17
18
  from dkist_processing_common.tasks.mixin.interservice_bus import InterserviceBusMixin
18
- from dkist_processing_common.tests.conftest import recipe_run_id
19
19
 
20
20
  logger = logging.getLogger(__name__)
21
21
 
@@ -0,0 +1,33 @@
1
+ import pytest
2
+
3
+ from dkist_processing_common.manual import ManualProcessing
4
+ from dkist_processing_common.tasks import WorkflowTaskBase
5
+
6
+
7
+ class ProvenanceTask(WorkflowTaskBase):
8
+ record_provenance = True
9
+
10
+ def run(self): ...
11
+
12
+
13
+ @pytest.fixture(scope="function")
14
+ def manual_processing_run(tmp_path, recipe_run_id):
15
+ with ManualProcessing(
16
+ recipe_run_id=recipe_run_id,
17
+ workflow_path=tmp_path,
18
+ workflow_name="manual",
19
+ workflow_version="manual",
20
+ ) as manual_processing_run:
21
+ yield manual_processing_run
22
+
23
+
24
+ def test_manual_record_provenance(tmp_path, recipe_run_id, manual_processing_run):
25
+ """
26
+ Given: A WorkflowTaskBase subclass with provenance recording turned on
27
+ When: Running the task with the ManualProcessing wrapper
28
+ Then: The provenance record exists on disk
29
+ """
30
+ manual_processing_run.run_task(task=ProvenanceTask)
31
+ directory = tmp_path / str(recipe_run_id)
32
+ provenance_file = directory / "ProvenanceTask_provenance.json"
33
+ assert provenance_file.exists()
@@ -7,18 +7,16 @@ from dkist_processing_common._util.scratch import WorkflowFileSystem
7
7
  from dkist_processing_common.models.tags import Tag
8
8
  from dkist_processing_common.tasks.output_data_base import OutputDataBase
9
9
  from dkist_processing_common.tasks.output_data_base import TransferDataBase
10
- from dkist_processing_common.tests.conftest import FakeGQLClient
11
10
 
12
11
 
13
12
  class OutputDataBaseTask(OutputDataBase):
14
- def run(self) -> None:
15
- ...
13
+ def run(self) -> None: ...
16
14
 
17
15
 
18
16
  @pytest.fixture
19
- def output_data_base_task(recipe_run_id, mocker):
17
+ def output_data_base_task(recipe_run_id, mocker, fake_gql_client):
20
18
  mocker.patch(
21
- "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
19
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=fake_gql_client
22
20
  )
23
21
  proposal_id = "test_proposal_id"
24
22
  with OutputDataBaseTask(
@@ -37,9 +35,9 @@ class TransferDataTask(TransferDataBase):
37
35
 
38
36
 
39
37
  @pytest.fixture
40
- def transfer_data_task(recipe_run_id, tmp_path, mocker):
38
+ def transfer_data_task(recipe_run_id, tmp_path, mocker, fake_gql_client):
41
39
  mocker.patch(
42
- "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
40
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=fake_gql_client
43
41
  )
44
42
  with TransferDataTask(
45
43
  recipe_run_id=recipe_run_id,