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
@@ -7,6 +7,7 @@ HOW TO WRITE TESTS FOR NEW PARAMETER SUBCLASSES :
7
7
  4. Add a `pytest.param` with this helper function to `test_parameters` to make sure none of the default stuff broke
8
8
  5. Write a new test that only uses the helper function to test the new functionality.
9
9
  """
10
+
10
11
  import json
11
12
  from datetime import datetime
12
13
  from datetime import timedelta
@@ -18,22 +19,21 @@ import numpy as np
18
19
  import pytest
19
20
  from astropy.io import fits
20
21
 
22
+ from dkist_processing_common.codecs.array import array_encoder
23
+ from dkist_processing_common.codecs.fits import fits_hdulist_encoder
21
24
  from dkist_processing_common.models.parameters import ParameterArmIdMixin
22
25
  from dkist_processing_common.models.parameters import ParameterBase
23
26
  from dkist_processing_common.models.parameters import ParameterWavelengthMixin
24
27
  from dkist_processing_common.models.tags import Tag
25
28
  from dkist_processing_common.tasks import WorkflowTaskBase
26
- from dkist_processing_common.tasks.mixin.input_dataset import InputDatasetMixin
29
+ from dkist_processing_common.tests.test_input_dataset import input_dataset_frames_part_factory
30
+
31
+ FITS_FILE = "fits.dat"
32
+ NP_FILE = "np.npy"
27
33
 
28
34
 
29
35
  @pytest.fixture
30
- def input_dataset_parameters(tmp_path):
31
- fits_file_path = tmp_path / "fits.dat"
32
- phdu = fits.PrimaryHDU(np.ones((3, 3)) * 3)
33
- ihdu = fits.ImageHDU(np.ones((4, 4)) * 4)
34
- fits.HDUList([phdu, ihdu]).writeto(fits_file_path)
35
- np_file_path = tmp_path / "np.npy"
36
- np.save(np_file_path, np.ones((3, 3)) * 4)
36
+ def input_dataset_parameters():
37
37
  return [
38
38
  {
39
39
  "parameterName": "basic_param",
@@ -106,7 +106,15 @@ def input_dataset_parameters(tmp_path):
106
106
  "parameterValues": [
107
107
  {
108
108
  "parameterValueId": 1,
109
- "parameterValue": json.dumps({"param_path": str(fits_file_path)}),
109
+ "parameterValue": json.dumps(
110
+ {
111
+ "__file__": {
112
+ "bucket": "not_used",
113
+ "objectKey": "not_used",
114
+ "tag": Tag.parameter(FITS_FILE),
115
+ }
116
+ }
117
+ ),
110
118
  }
111
119
  ],
112
120
  },
@@ -115,7 +123,15 @@ def input_dataset_parameters(tmp_path):
115
123
  "parameterValues": [
116
124
  {
117
125
  "parameterValueId": 1,
118
- "parameterValue": json.dumps({"param_path": str(np_file_path)}),
126
+ "parameterValue": json.dumps(
127
+ {
128
+ "__file__": {
129
+ "bucket": "not_used",
130
+ "objectKey": "not_used",
131
+ "tag": Tag.parameter(NP_FILE),
132
+ }
133
+ }
134
+ ),
119
135
  }
120
136
  ],
121
137
  },
@@ -129,16 +145,14 @@ def input_dataset_parts(input_dataset_parameters) -> tuple[Any, str]:
129
145
 
130
146
  @pytest.fixture()
131
147
  def task_class_with_parameters(parameter_class) -> Type[WorkflowTaskBase]:
132
- class TaskWithParameters(WorkflowTaskBase, InputDatasetMixin):
148
+ class TaskWithParameters(WorkflowTaskBase):
133
149
  def __init__(self, recipe_run_id: int, workflow_name: str, workflow_version: str):
134
150
  super().__init__(
135
151
  recipe_run_id=recipe_run_id,
136
152
  workflow_name=workflow_name,
137
153
  workflow_version=workflow_version,
138
154
  )
139
- self.parameters = parameter_class(
140
- input_dataset_parameters=self.input_dataset_parameters
141
- )
155
+ self.parameters = parameter_class(scratch=self.scratch)
142
156
 
143
157
  def run(self) -> None:
144
158
  pass
@@ -149,11 +163,21 @@ def task_class_with_parameters(parameter_class) -> Type[WorkflowTaskBase]:
149
163
  @pytest.fixture()
150
164
  def task_with_parameters(task_with_input_dataset, task_class_with_parameters):
151
165
  task_class = task_class_with_parameters
152
- return task_class(
166
+ with task_class(
153
167
  recipe_run_id=task_with_input_dataset.recipe_run_id,
154
168
  workflow_name=task_with_input_dataset.workflow_name,
155
169
  workflow_version=task_with_input_dataset.workflow_version,
156
- )
170
+ ) as task:
171
+ phdu = fits.PrimaryHDU(np.ones((3, 3)) * 3)
172
+ ihdu = fits.ImageHDU(np.ones((4, 4)) * 4)
173
+ task.write(
174
+ data=fits.HDUList([phdu, ihdu]),
175
+ tags=Tag.parameter(FITS_FILE),
176
+ encoder=fits_hdulist_encoder,
177
+ )
178
+ task.write(data=np.ones((3, 3)) * 4, tags=Tag.parameter(NP_FILE), encoder=array_encoder)
179
+ yield task
180
+ task._purge()
157
181
 
158
182
 
159
183
  class FilledParametersNoObsTime(ParameterBase):
@@ -182,18 +206,18 @@ class FilledParametersWithObsTime(ParameterBase):
182
206
 
183
207
  @property
184
208
  def fits_file_parameter(self):
185
- param_dict = self._find_most_recent_past_value("fits_file_parameter")
186
- return self._load_param_value_from_fits(param_dict)
209
+ param_obj = self._find_most_recent_past_value("fits_file_parameter")
210
+ return self._load_param_value_from_fits(param_obj=param_obj)
187
211
 
188
212
  @property
189
213
  def non_primary_fits_file_parameter(self):
190
- param_dict = self._find_most_recent_past_value("fits_file_parameter")
191
- return self._load_param_value_from_fits(param_dict, hdu=1)
214
+ param_obj = self._find_most_recent_past_value("fits_file_parameter")
215
+ return self._load_param_value_from_fits(param_obj=param_obj, hdu=1)
192
216
 
193
217
  @property
194
218
  def numpy_file_parameter(self):
195
- param_dict = self._find_most_recent_past_value("numpy_file_parameter")
196
- return self._load_param_value_from_numpy_save(param_dict)
219
+ param_obj = self._find_most_recent_past_value("numpy_file_parameter")
220
+ return self._load_param_value_from_numpy_save(param_obj=param_obj)
197
221
 
198
222
 
199
223
  def parameter_class_with_obs_ip_start_time():
@@ -343,3 +367,28 @@ def test_mixins_error_with_no_arg():
343
367
  """
344
368
  with pytest.raises(TypeError):
345
369
  parameters = FilledWavelengthParameters(input_dataset_parameters={"foo": []})
370
+
371
+
372
+ @pytest.mark.parametrize(
373
+ ("input_dataset_parts", "parameter_class"),
374
+ [
375
+ pytest.param(
376
+ [
377
+ (input_dataset_frames_part_factory(), Tag.input_dataset_parameters()),
378
+ (input_dataset_frames_part_factory(), Tag.input_dataset_parameters()),
379
+ ],
380
+ parameter_class_with_obs_ip_start_time(),
381
+ id="two_param_docs",
382
+ ),
383
+ ],
384
+ )
385
+ def test_multiple_input_dataset_parameter_parts(
386
+ request, input_dataset_parts: list[tuple[Any, str]], parameter_class
387
+ ):
388
+ """
389
+ Given: a task with multiple tagged input dataset parameter docs
390
+ When: initializing the parameter base
391
+ Then: an error is raised
392
+ """
393
+ with pytest.raises(ValueError, match="more than one parameter file"):
394
+ request.getfixturevalue("task_with_parameters")
@@ -1,4 +1,7 @@
1
1
  """Tests for the parse L0 input data task"""
2
+
3
+ from enum import StrEnum
4
+
2
5
  import numpy as np
3
6
  import pytest
4
7
  from astropy.io import fits
@@ -9,16 +12,20 @@ from dkist_processing_common._util.scratch import WorkflowFileSystem
9
12
  from dkist_processing_common.codecs.fits import fits_hdulist_encoder
10
13
  from dkist_processing_common.models.constants import BudName
11
14
  from dkist_processing_common.models.fits_access import FitsAccessBase
15
+ from dkist_processing_common.models.flower_pot import ListStem
16
+ from dkist_processing_common.models.flower_pot import SetStem
12
17
  from dkist_processing_common.models.flower_pot import SpilledDirt
13
18
  from dkist_processing_common.models.flower_pot import Stem
14
19
  from dkist_processing_common.models.flower_pot import Thorn
15
20
  from dkist_processing_common.models.tags import StemName
16
21
  from dkist_processing_common.models.tags import Tag
22
+ from dkist_processing_common.parsers.lookup_bud import TimeLookupBud
17
23
  from dkist_processing_common.parsers.single_value_single_key_flower import (
18
24
  SingleValueSingleKeyFlower,
19
25
  )
20
26
  from dkist_processing_common.parsers.unique_bud import UniqueBud
21
27
  from dkist_processing_common.tasks.parse_l0_input_data import ParseL0InputDataBase
28
+ from dkist_processing_common.tasks.parse_l0_input_data import default_constant_bud_factory
22
29
 
23
30
 
24
31
  class VispHeaders(Spec122Dataset):
@@ -84,25 +91,44 @@ class VispHeaders(Spec122Dataset):
84
91
  return self.index % self.num_mod
85
92
 
86
93
 
94
+ class ViSPMetadataKey(StrEnum):
95
+ num_mod = "VISP_010"
96
+ modstate = "VISP_011"
97
+ ip_task_type = "DKIST004"
98
+
99
+
87
100
  class ViSPFitsAccess(FitsAccessBase):
88
101
  def __init__(self, hdu, name, auto_squeeze=False):
89
102
  super().__init__(hdu, name, auto_squeeze=auto_squeeze)
90
- self.num_mod: int = self.header["VISP_010"]
91
- self.modstate: int = self.header["VISP_011"]
92
- self.ip_task_type: str = self.header["DKIST004"]
103
+ self.num_mod: int = self.header[ViSPMetadataKey.num_mod]
104
+ self.modstate: int = self.header[ViSPMetadataKey.modstate]
105
+ self.ip_task_type: str = self.header[ViSPMetadataKey.ip_task_type]
93
106
  self.name = name
94
107
 
95
108
 
96
109
  @pytest.fixture(scope="function")
97
110
  def visp_flowers():
98
111
  return [
99
- SingleValueSingleKeyFlower(tag_stem_name=StemName.modstate.value, metadata_key="modstate")
112
+ SingleValueSingleKeyFlower(
113
+ tag_stem_name=StemName.modstate, metadata_key=ViSPMetadataKey.modstate
114
+ )
100
115
  ]
101
116
 
102
117
 
103
118
  @pytest.fixture(scope="function")
104
119
  def visp_buds():
105
- return [UniqueBud(constant_name=BudName.num_modstates.value, metadata_key="num_mod")]
120
+ return [UniqueBud(constant_name=BudName.num_modstates, metadata_key=ViSPMetadataKey.num_mod)]
121
+
122
+
123
+ @pytest.fixture(scope="function")
124
+ def visp_lookup_buds():
125
+ return [
126
+ TimeLookupBud(
127
+ constant_name="LOOKUP_BUD",
128
+ key_metadata_key=ViSPMetadataKey.num_mod,
129
+ value_metadata_key=ViSPMetadataKey.modstate,
130
+ )
131
+ ]
106
132
 
107
133
 
108
134
  @pytest.fixture(scope="function")
@@ -132,7 +158,27 @@ def empty_buds():
132
158
  def getter(self, key):
133
159
  pass # We'll never get here because we spilled the dirt
134
160
 
135
- return [EmptyBud()]
161
+ class EmptyListBud(ListStem):
162
+ def __init__(self):
163
+ super().__init__(stem_name="EMPTY_LIST_BUD")
164
+
165
+ def setter(self, value):
166
+ return SpilledDirt
167
+
168
+ def getter(self):
169
+ pass
170
+
171
+ class EmptySetBud(SetStem):
172
+ def __init__(self):
173
+ super().__init__(stem_name="EMPTY_SET_BUD")
174
+
175
+ def setter(self, value):
176
+ return SpilledDirt
177
+
178
+ def getter(self):
179
+ pass
180
+
181
+ return [EmptyBud(), EmptyListBud(), EmptySetBud()]
136
182
 
137
183
 
138
184
  @pytest.fixture()
@@ -149,8 +195,17 @@ def picky_buds():
149
195
 
150
196
  @pytest.fixture(scope="function")
151
197
  def parse_inputs_task(
152
- tmp_path, visp_flowers, visp_buds, empty_flowers, empty_buds, picky_buds, recipe_run_id
198
+ tmp_path,
199
+ visp_flowers,
200
+ visp_buds,
201
+ visp_lookup_buds,
202
+ empty_flowers,
203
+ empty_buds,
204
+ picky_buds,
205
+ recipe_run_id,
153
206
  ):
207
+ """Override parse task class and make data for testing."""
208
+
154
209
  class TaskClass(ParseL0InputDataBase):
155
210
  @property
156
211
  def tag_flowers(self):
@@ -158,7 +213,7 @@ def parse_inputs_task(
158
213
 
159
214
  @property
160
215
  def constant_buds(self):
161
- return visp_buds + empty_buds + picky_buds
216
+ return visp_buds + visp_lookup_buds + empty_buds + picky_buds
162
217
 
163
218
  @property
164
219
  def fits_parsing_class(self):
@@ -198,6 +253,8 @@ def parse_inputs_task(
198
253
 
199
254
  @pytest.fixture()
200
255
  def visp_parse_inputs_task(tmp_path, visp_flowers, visp_buds, recipe_run_id):
256
+ """Extend parse task class, but don't make data for testing."""
257
+
201
258
  class TaskClass(ParseL0InputDataBase):
202
259
  @property
203
260
  def tag_flowers(self):
@@ -231,12 +288,15 @@ def test_make_flowerpots(parse_inputs_task):
231
288
  tag_pot, constant_pot = parse_inputs_task.make_flower_pots()
232
289
 
233
290
  assert len(tag_pot.stems) == 2
234
- assert len(constant_pot.stems) == 3
235
- assert tag_pot.stems[0].stem_name == StemName.modstate.value
291
+ assert len(constant_pot.stems) == 6
292
+ assert tag_pot.stems[0].stem_name == StemName.modstate
236
293
  assert tag_pot.stems[1].stem_name == "EMPTY_FLOWER"
237
- assert constant_pot.stems[0].stem_name == BudName.num_modstates.value
238
- assert constant_pot.stems[1].stem_name == "EMPTY_BUD"
239
- assert constant_pot.stems[2].stem_name == "PICKY_BUD"
294
+ assert constant_pot.stems[0].stem_name == BudName.num_modstates
295
+ assert constant_pot.stems[1].stem_name == "LOOKUP_BUD"
296
+ assert constant_pot.stems[2].stem_name == "EMPTY_BUD"
297
+ assert constant_pot.stems[3].stem_name == "EMPTY_LIST_BUD"
298
+ assert constant_pot.stems[4].stem_name == "EMPTY_SET_BUD"
299
+ assert constant_pot.stems[5].stem_name == "PICKY_BUD"
240
300
 
241
301
 
242
302
  def test_subclass_flowers(visp_parse_inputs_task, max_cs_step_time_sec):
@@ -248,36 +308,59 @@ def test_subclass_flowers(visp_parse_inputs_task, max_cs_step_time_sec):
248
308
  tag_pot, constant_pot = visp_parse_inputs_task.make_flower_pots()
249
309
 
250
310
  assert len(tag_pot.stems) == 1
251
- assert len(constant_pot.stems) == 12
252
- assert sorted([f.stem_name for f in tag_pot.stems]) == sorted([StemName.modstate.value])
253
- assert sorted([f.stem_name for f in constant_pot.stems]) == sorted(
254
- [
255
- BudName.instrument.value,
256
- BudName.num_modstates.value,
257
- BudName.proposal_id.value,
258
- BudName.contributing_proposal_ids.value,
259
- BudName.average_cadence.value,
260
- BudName.maximum_cadence.value,
261
- BudName.minimum_cadence.value,
262
- BudName.variance_cadence.value,
263
- BudName.dark_exposure_times.value,
264
- BudName.dark_readout_exp_times.value,
265
- BudName.experiment_id.value,
266
- BudName.contributing_experiment_ids.value,
267
- ]
268
- )
311
+ assert len(constant_pot.stems) == 61
312
+ all_flower_names = [StemName.modstate]
313
+ assert sorted([f.stem_name for f in tag_pot.stems]) == sorted(all_flower_names)
314
+ all_bud_names = [b.stem_name for b in default_constant_bud_factory()] + [BudName.num_modstates]
315
+ assert sorted([f.stem_name for f in constant_pot.stems]) == sorted(all_bud_names)
316
+
317
+
318
+ def test_dataset_extra_bud_factory(visp_parse_inputs_task, max_cs_step_time_sec):
319
+ """
320
+ Given: ParseInputData child class with custom stems
321
+ When: Making the constant pot
322
+ Then: The multi-task dataset extra buds are created
323
+ """
324
+ _, constant_pot = visp_parse_inputs_task.make_flower_pots()
325
+ stem_names = [f.stem_name.value for f in constant_pot.stems]
326
+ bud_name_base = [
327
+ "DATE_BEGIN",
328
+ "OBSERVING_PROGRAM_EXECUTION_IDS",
329
+ "NUM_RAW_FRAMES_PER_FPA",
330
+ "TELESCOPE_TRACKING_MODE",
331
+ "COUDE_TABLE_TRACKING_MODE",
332
+ "TELESCOPE_SCANNING_MODE",
333
+ "AVERAGE_LIGHT_LEVEL",
334
+ "AVERAGE_TELESCOPE_ELEVATION",
335
+ "AVERAGE_COUDE_TABLE_ANGLE",
336
+ "AVERAGE_TELESCOPE_AZIMUTH",
337
+ "GOS_LEVEL3_STATUS",
338
+ "GOS_LEVEL3_LAMP_STATUS",
339
+ "GOS_POLARIZER_STATUS",
340
+ "GOS_POLARIZER_ANGLE",
341
+ "GOS_RETARDER_STATUS",
342
+ "GOS_RETARDER_ANGLE",
343
+ "GOS_LEVEL0_STATUS",
344
+ ]
345
+ for base in bud_name_base:
346
+ assert "SOLAR_GAIN_" + base in stem_names
347
+ # telescope mode keys are not constant for dark frames
348
+ assert ("DARK_" + base in stem_names) ^ ("MODE" in base)
349
+ # gos keys are not constant for polcal frames
350
+ assert ("POLCAL_" + base in stem_names) ^ ("GOS" in base)
269
351
 
270
352
 
271
353
  def test_constants_correct(parse_inputs_task):
272
354
  """
273
355
  Given: ParseInputData task with a populated constant FlowerPot
274
356
  When: Updating pipeline constants
275
- Then: Pipeline constants are correctly populated
357
+ Then: A pipeline constant is correctly populated and the values return correctly
276
358
  """
277
359
  _, constant_pot = parse_inputs_task.make_flower_pots()
278
360
  parse_inputs_task.update_constants(constant_pot)
279
361
  assert dict(parse_inputs_task.constants._db_dict) == {
280
362
  BudName.num_modstates.value: parse_inputs_task._num_mod,
363
+ "LOOKUP_BUD": {str(parse_inputs_task._num_mod): [0, 1]},
281
364
  }
282
365
 
283
366
 
@@ -4,13 +4,12 @@ import pytest
4
4
 
5
5
  from dkist_processing_common.models.message import CreateQualityReportMessage
6
6
  from dkist_processing_common.tasks.l1_output_data import PublishCatalogAndQualityMessages
7
- from dkist_processing_common.tests.conftest import FakeGQLClient
8
7
 
9
8
 
10
9
  @pytest.fixture
11
- def publish_catalog_and_quality_messages_task(recipe_run_id, mocker):
10
+ def publish_catalog_and_quality_messages_task(recipe_run_id, mocker, fake_gql_client):
12
11
  mocker.patch(
13
- "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
12
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=fake_gql_client
14
13
  )
15
14
  with PublishCatalogAndQualityMessages(
16
15
  recipe_run_id=recipe_run_id,
@@ -58,24 +57,3 @@ def test_object_messages(publish_catalog_and_quality_messages_task):
58
57
  assert message.body.conversationId == str(task.recipe_run_id)
59
58
  assert message.body.objectType == object_type
60
59
  assert message.body.groupId == task.constants.dataset_id
61
-
62
-
63
- def test_quality_report_message(publish_catalog_and_quality_messages_task):
64
- """
65
- :Given: a PublishCatalogAndQualityMessages task
66
- :When: creating quality report message
67
- :Then: the attributes are correctly populated
68
- """
69
- # Given
70
- task, proposal_id = publish_catalog_and_quality_messages_task
71
- # When
72
- message = task.quality_report_message
73
- # Then
74
- assert isinstance(message, CreateQualityReportMessage)
75
- assert message.body.bucket == task.destination_bucket
76
- # objectName exists and can be evaluated as a valid path
77
- assert message.body.objectName
78
- _ = Path(message.body.objectName)
79
- assert message.body.datasetId == task.constants.dataset_id
80
- assert message.body.conversationId == str(task.recipe_run_id)
81
- assert message.body.incrementDatasetCatalogReceiptCount is True
@@ -1,4 +1,5 @@
1
1
  """Tests for the quality tasks."""
2
+
2
3
  import json
3
4
  from typing import Iterable
4
5