dkist-processing-common 11.2.1rc3__py3-none-any.whl → 11.3.1rc1__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.
- changelog/265.feature.1.rst +3 -0
- changelog/265.feature.rst +4 -0
- changelog/265.misc.rst +2 -0
- dkist_processing_common/__init__.py +1 -0
- dkist_processing_common/_util/constants.py +1 -0
- dkist_processing_common/_util/graphql.py +1 -0
- dkist_processing_common/_util/scratch.py +1 -1
- dkist_processing_common/_util/tags.py +1 -0
- dkist_processing_common/codecs/array.py +1 -0
- dkist_processing_common/codecs/asdf.py +1 -0
- dkist_processing_common/codecs/basemodel.py +1 -0
- dkist_processing_common/codecs/bytes.py +1 -0
- dkist_processing_common/codecs/fits.py +1 -0
- dkist_processing_common/codecs/iobase.py +1 -0
- dkist_processing_common/codecs/json.py +1 -0
- dkist_processing_common/codecs/path.py +1 -0
- dkist_processing_common/codecs/quality.py +1 -1
- dkist_processing_common/codecs/str.py +1 -0
- dkist_processing_common/config.py +1 -0
- dkist_processing_common/manual.py +1 -1
- dkist_processing_common/models/constants.py +43 -28
- dkist_processing_common/models/dkist_location.py +1 -1
- dkist_processing_common/models/fits_access.py +57 -0
- dkist_processing_common/models/flower_pot.py +1 -0
- dkist_processing_common/models/graphql.py +2 -1
- dkist_processing_common/models/input_dataset.py +3 -2
- dkist_processing_common/models/message.py +1 -1
- dkist_processing_common/models/message_queue_binding.py +1 -1
- dkist_processing_common/models/metric_code.py +1 -0
- dkist_processing_common/models/parameters.py +1 -1
- dkist_processing_common/models/quality.py +1 -0
- dkist_processing_common/models/tags.py +3 -1
- dkist_processing_common/models/task_name.py +3 -2
- dkist_processing_common/models/wavelength.py +2 -1
- dkist_processing_common/parsers/cs_step.py +3 -2
- dkist_processing_common/parsers/dsps_repeat.py +6 -4
- dkist_processing_common/parsers/experiment_id_bud.py +6 -2
- dkist_processing_common/parsers/id_bud.py +7 -3
- dkist_processing_common/parsers/l0_fits_access.py +5 -3
- dkist_processing_common/parsers/l1_fits_access.py +23 -21
- dkist_processing_common/parsers/near_bud.py +6 -2
- dkist_processing_common/parsers/proposal_id_bud.py +4 -2
- dkist_processing_common/parsers/quality.py +1 -0
- dkist_processing_common/parsers/retarder.py +5 -3
- dkist_processing_common/parsers/single_value_single_key_flower.py +7 -1
- dkist_processing_common/parsers/task.py +8 -6
- dkist_processing_common/parsers/time.py +20 -15
- dkist_processing_common/parsers/unique_bud.py +6 -2
- dkist_processing_common/parsers/wavelength.py +5 -3
- dkist_processing_common/tasks/__init__.py +3 -2
- dkist_processing_common/tasks/assemble_movie.py +1 -0
- dkist_processing_common/tasks/base.py +1 -0
- dkist_processing_common/tasks/l1_output_data.py +1 -1
- dkist_processing_common/tasks/mixin/globus.py +1 -1
- dkist_processing_common/tasks/mixin/interservice_bus.py +1 -0
- dkist_processing_common/tasks/mixin/metadata_store.py +1 -1
- dkist_processing_common/tasks/mixin/object_store.py +1 -0
- dkist_processing_common/tasks/mixin/quality/__init__.py +1 -0
- dkist_processing_common/tasks/mixin/quality/_base.py +1 -0
- dkist_processing_common/tasks/mixin/quality/_metrics.py +1 -0
- dkist_processing_common/tasks/output_data_base.py +1 -0
- dkist_processing_common/tasks/parse_l0_input_data.py +6 -4
- dkist_processing_common/tasks/quality_metrics.py +1 -1
- dkist_processing_common/tasks/teardown.py +1 -1
- dkist_processing_common/tasks/transfer_input_data.py +1 -1
- dkist_processing_common/tasks/trial_catalog.py +3 -2
- dkist_processing_common/tasks/trial_output_data.py +1 -0
- dkist_processing_common/tasks/write_l1.py +19 -8
- dkist_processing_common/tests/conftest.py +1 -0
- dkist_processing_common/tests/mock_metadata_store.py +2 -3
- dkist_processing_common/tests/test_assemble_movie.py +0 -1
- dkist_processing_common/tests/test_codecs.py +2 -2
- dkist_processing_common/tests/test_constants.py +15 -0
- dkist_processing_common/tests/test_fits_access.py +62 -7
- dkist_processing_common/tests/test_interservice_bus.py +1 -0
- dkist_processing_common/tests/test_interservice_bus_mixin.py +1 -0
- dkist_processing_common/tests/test_manual_processing.py +1 -2
- dkist_processing_common/tests/test_output_data_base.py +1 -2
- dkist_processing_common/tests/test_parameters.py +1 -1
- dkist_processing_common/tests/test_parse_l0_input_data.py +23 -24
- dkist_processing_common/tests/test_quality.py +1 -0
- dkist_processing_common/tests/test_scratch.py +2 -1
- dkist_processing_common/tests/test_stems.py +31 -22
- dkist_processing_common/tests/test_tags.py +1 -0
- dkist_processing_common/tests/test_task_parsing.py +17 -7
- dkist_processing_common/tests/test_teardown.py +1 -1
- dkist_processing_common/tests/test_transfer_input_data.py +2 -3
- dkist_processing_common/tests/test_trial_catalog.py +1 -0
- dkist_processing_common/tests/test_trial_output_data.py +1 -1
- dkist_processing_common/tests/test_workflow_task_base.py +1 -2
- dkist_processing_common/tests/test_write_l1.py +8 -10
- {dkist_processing_common-11.2.1rc3.dist-info → dkist_processing_common-11.3.1rc1.dist-info}/METADATA +2 -2
- dkist_processing_common-11.3.1rc1.dist-info/RECORD +125 -0
- docs/conf.py +1 -0
- changelog/262.misc.rst +0 -1
- dkist_processing_common-11.2.1rc3.dist-info/RECORD +0 -123
- {dkist_processing_common-11.2.1rc3.dist-info → dkist_processing_common-11.3.1rc1.dist-info}/WHEEL +0 -0
- {dkist_processing_common-11.2.1rc3.dist-info → dkist_processing_common-11.3.1rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Task(s) for writing level 1 data as 214 compliant fits files."""
|
|
2
|
+
|
|
2
3
|
import importlib
|
|
3
4
|
import logging
|
|
4
5
|
import uuid
|
|
@@ -213,11 +214,6 @@ class WriteL1Frame(WorkflowTaskBase, MetadataStoreMixin, ABC):
|
|
|
213
214
|
header["OBS_HDU"] = 1
|
|
214
215
|
header["FILENAME"] = self.l1_filename(header=header, stokes=stokes)
|
|
215
216
|
header["STOKES"] = stokes
|
|
216
|
-
# Cadence keywords
|
|
217
|
-
header["CADENCE"] = self.constants.average_cadence
|
|
218
|
-
header["CADMIN"] = self.constants.minimum_cadence
|
|
219
|
-
header["CADMAX"] = self.constants.maximum_cadence
|
|
220
|
-
header["CADVAR"] = self.constants.variance_cadence
|
|
221
217
|
# Keywords to support reprocessing
|
|
222
218
|
if parameters := self.metadata_store_input_dataset_parameters:
|
|
223
219
|
header["IDSPARID"] = parameters.inputDatasetPartId
|
|
@@ -232,6 +228,19 @@ class WriteL1Frame(WorkflowTaskBase, MetadataStoreMixin, ABC):
|
|
|
232
228
|
header["PRODUCT"] = self.compute_product_id(header["IDSOBSID"], header["PROCTYPE"])
|
|
233
229
|
return header
|
|
234
230
|
|
|
231
|
+
def add_timing_headers(self, header: fits.Header) -> fits.Header:
|
|
232
|
+
"""
|
|
233
|
+
Add timing headers to the FITS header.
|
|
234
|
+
|
|
235
|
+
This method adds or updates headers related to frame timings.
|
|
236
|
+
"""
|
|
237
|
+
# Cadence keywords
|
|
238
|
+
header["CADENCE"] = self.constants.average_cadence
|
|
239
|
+
header["CADMIN"] = self.constants.minimum_cadence
|
|
240
|
+
header["CADMAX"] = self.constants.maximum_cadence
|
|
241
|
+
header["CADVAR"] = self.constants.variance_cadence
|
|
242
|
+
return header
|
|
243
|
+
|
|
235
244
|
def add_spectral_line_headers(
|
|
236
245
|
self,
|
|
237
246
|
header: fits.Header,
|
|
@@ -370,6 +379,8 @@ class WriteL1Frame(WorkflowTaskBase, MetadataStoreMixin, ABC):
|
|
|
370
379
|
header = self.add_doc_headers(header=header)
|
|
371
380
|
# Add the dataset headers (abstract - implement in instrument task)
|
|
372
381
|
header = self.add_dataset_headers(header=header, stokes=stokes_param)
|
|
382
|
+
# Add the timing headers
|
|
383
|
+
header = self.add_timing_headers(header=header)
|
|
373
384
|
# Add the spectral line headers
|
|
374
385
|
header = self.add_spectral_line_headers(header=header)
|
|
375
386
|
# Remove any headers not contained in spec 214
|
|
@@ -414,9 +425,9 @@ class WriteL1Frame(WorkflowTaskBase, MetadataStoreMixin, ABC):
|
|
|
414
425
|
inst_name = self.constants.instrument.lower()
|
|
415
426
|
calvers = self.version_from_module_name()
|
|
416
427
|
header["CALVERS"] = calvers
|
|
417
|
-
header[
|
|
418
|
-
"
|
|
419
|
-
|
|
428
|
+
header["CAL_URL"] = (
|
|
429
|
+
f"{self.docs_base_url}/projects/{inst_name}/en/v{calvers}/{self.workflow_name}.html"
|
|
430
|
+
)
|
|
420
431
|
return header
|
|
421
432
|
|
|
422
433
|
def version_from_module_name(self) -> str:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Support functions and constants for customized FakeGQLClient
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
import json
|
|
5
6
|
from abc import ABC
|
|
6
7
|
from abc import abstractmethod
|
|
@@ -22,7 +23,6 @@ from dkist_processing_common.models.graphql import RecipeRunProvenanceResponse
|
|
|
22
23
|
from dkist_processing_common.models.graphql import RecipeRunResponse
|
|
23
24
|
from dkist_processing_common.models.graphql import RecipeRunStatusResponse
|
|
24
25
|
|
|
25
|
-
|
|
26
26
|
TILE_SIZE = 64
|
|
27
27
|
|
|
28
28
|
default_observe_frames_doc = [
|
|
@@ -223,8 +223,7 @@ def fake_gql_client_factory(response_mapping_override: ResponseMapping | None =
|
|
|
223
223
|
raise ValueError(f"Mocked response not found for {query_base=}, {query_response_cls=}")
|
|
224
224
|
|
|
225
225
|
@staticmethod
|
|
226
|
-
def execute_gql_mutation(**kwargs):
|
|
227
|
-
...
|
|
226
|
+
def execute_gql_mutation(**kwargs): ...
|
|
228
227
|
|
|
229
228
|
return FakeGQLClientClass
|
|
230
229
|
|
|
@@ -20,8 +20,8 @@ from astropy.io.fits import HDUList
|
|
|
20
20
|
from astropy.io.fits import Header
|
|
21
21
|
from astropy.io.fits import PrimaryHDU
|
|
22
22
|
from pydantic import BaseModel
|
|
23
|
-
from pydantic import create_model
|
|
24
23
|
from pydantic import Field
|
|
24
|
+
from pydantic import create_model
|
|
25
25
|
|
|
26
26
|
from dkist_processing_common.codecs.asdf import asdf_decoder
|
|
27
27
|
from dkist_processing_common.codecs.asdf import asdf_encoder
|
|
@@ -39,10 +39,10 @@ from dkist_processing_common.codecs.iobase import iobase_decoder
|
|
|
39
39
|
from dkist_processing_common.codecs.iobase import iobase_encoder
|
|
40
40
|
from dkist_processing_common.codecs.json import json_decoder
|
|
41
41
|
from dkist_processing_common.codecs.json import json_encoder
|
|
42
|
+
from dkist_processing_common.codecs.quality import QualityDataEncoder
|
|
42
43
|
from dkist_processing_common.codecs.quality import quality_data_decoder
|
|
43
44
|
from dkist_processing_common.codecs.quality import quality_data_encoder
|
|
44
45
|
from dkist_processing_common.codecs.quality import quality_data_hook
|
|
45
|
-
from dkist_processing_common.codecs.quality import QualityDataEncoder
|
|
46
46
|
from dkist_processing_common.codecs.str import str_decoder
|
|
47
47
|
from dkist_processing_common.codecs.str import str_encoder
|
|
48
48
|
from dkist_processing_common.models.fits_access import FitsAccessBase
|
|
@@ -40,6 +40,21 @@ class ConstantsFinal(ConstantsBase):
|
|
|
40
40
|
return self._db_dict["KEY 1"] ** 2 # Just to show that you can do whatever you want
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def test_bud_names_in_constant_base():
|
|
44
|
+
"""
|
|
45
|
+
Given: a set of constants in the BudNames sting enumeration
|
|
46
|
+
When: ConstantBase class defines a set of properties
|
|
47
|
+
Then: the sets are the same (except for constants that are not in the redis database)
|
|
48
|
+
"""
|
|
49
|
+
all_bud_names = {b.name for b in BudName}
|
|
50
|
+
all_properties_in_constants_base = {
|
|
51
|
+
k for k, v in ConstantsBase.__dict__.items() if isinstance(v, property)
|
|
52
|
+
}
|
|
53
|
+
constants_not_in_redis = {"dataset_id", "stokes_params"}
|
|
54
|
+
all_buds_defined_in_constant_base = all_properties_in_constants_base - constants_not_in_redis
|
|
55
|
+
assert all_bud_names == all_buds_defined_in_constant_base
|
|
56
|
+
|
|
57
|
+
|
|
43
58
|
def test_constants_db_as_dict(test_constants_db, test_dict):
|
|
44
59
|
"""
|
|
45
60
|
Given: a ConstantsDb object and a python dictionary
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
import pytest
|
|
3
5
|
from astropy.io import fits
|
|
4
6
|
|
|
7
|
+
from dkist_processing_common.models.fits_access import NOT_FOUND_MESSAGE
|
|
5
8
|
from dkist_processing_common.models.fits_access import FitsAccessBase
|
|
9
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
6
10
|
from dkist_processing_common.parsers.l0_fits_access import L0FitsAccess
|
|
7
11
|
from dkist_processing_common.parsers.l1_fits_access import L1FitsAccess
|
|
8
12
|
|
|
@@ -93,17 +97,32 @@ def hdu_with_no_data(complete_common_header):
|
|
|
93
97
|
|
|
94
98
|
|
|
95
99
|
@pytest.fixture()
|
|
96
|
-
def hdu_with_incomplete_common_header(
|
|
100
|
+
def hdu_with_incomplete_common_header(complete_common_header):
|
|
97
101
|
"""
|
|
98
102
|
An HDU with data and a header missing one of the expected common by-frame keywords
|
|
99
103
|
"""
|
|
104
|
+
incomplete_header = complete_common_header
|
|
105
|
+
incomplete_header.pop("ELEV_ANG")
|
|
106
|
+
incomplete_header.pop("TAZIMUTH")
|
|
100
107
|
data = np.arange(9).reshape(3, 3)
|
|
101
|
-
hdu = fits.PrimaryHDU(data)
|
|
102
|
-
hdu.header["TELEVATN"] = 6.28
|
|
103
|
-
hdu.header["TAZIMUTH"] = 3.14
|
|
108
|
+
hdu = fits.PrimaryHDU(data, header=incomplete_header)
|
|
104
109
|
return hdu
|
|
105
110
|
|
|
106
111
|
|
|
112
|
+
class MetadataKeyWithOptionalKeys(StrEnum):
|
|
113
|
+
optional1 = "ELEV_ANG"
|
|
114
|
+
optional2 = "TAZIMUTH"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class FitsAccessWithOptionalKeys(FitsAccessBase):
|
|
118
|
+
def __init__(self, hdu, name):
|
|
119
|
+
super().__init__(hdu, name)
|
|
120
|
+
self._set_metadata_key_value(MetadataKeyWithOptionalKeys.optional1, optional=True)
|
|
121
|
+
self._set_metadata_key_value(
|
|
122
|
+
MetadataKeyWithOptionalKeys.optional2, optional=True, default="SO_RAD"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
107
126
|
@pytest.fixture()
|
|
108
127
|
def fits_file_path(tmp_path, complete_common_header):
|
|
109
128
|
file_path = tmp_path / "foo.fits"
|
|
@@ -127,12 +146,37 @@ def fits_file_path_with_data_in_imagehdu(tmp_path, complete_common_header):
|
|
|
127
146
|
return file_path
|
|
128
147
|
|
|
129
148
|
|
|
149
|
+
class MetadataKeyWithNaxisKeys(StrEnum):
|
|
150
|
+
naxis = "NAXIS"
|
|
151
|
+
naxis1 = "NAXIS1"
|
|
152
|
+
naxis2 = "NAXIS2"
|
|
153
|
+
|
|
154
|
+
|
|
130
155
|
class FitsAccessWithNaxisKeys(FitsAccessBase):
|
|
131
156
|
def __init__(self, hdu, name):
|
|
132
157
|
super().__init__(hdu, name)
|
|
133
|
-
self.naxis
|
|
134
|
-
self.naxis1
|
|
135
|
-
self.naxis2
|
|
158
|
+
self._set_metadata_key_value(MetadataKeyWithNaxisKeys.naxis)
|
|
159
|
+
self._set_metadata_key_value(MetadataKeyWithNaxisKeys.naxis1)
|
|
160
|
+
self._set_metadata_key_value(MetadataKeyWithNaxisKeys.naxis2)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_metadata_keys_in_access_bases(hdu_with_no_data):
|
|
164
|
+
"""
|
|
165
|
+
Given: a set of metadata key names in the MetadataKey sting enumeration
|
|
166
|
+
When: the FITS access classes define a set of attributes/properties
|
|
167
|
+
Then: the sets are the same
|
|
168
|
+
"""
|
|
169
|
+
all_metadata_key_names = {mk.name for mk in MetadataKey}
|
|
170
|
+
hdu = hdu_with_no_data
|
|
171
|
+
l1_properties = {k for k, v in L1FitsAccess.__dict__.items() if isinstance(v, property)}
|
|
172
|
+
l0_properties = {k for k, v in L0FitsAccess.__dict__.items() if isinstance(v, property)}
|
|
173
|
+
all_fits_access_properties = l1_properties | l0_properties
|
|
174
|
+
l1_dict_keys = {k for k, v in L1FitsAccess(hdu=hdu).__dict__.items()}
|
|
175
|
+
l0_dict_keys = {k for k, v in L0FitsAccess(hdu=hdu).__dict__.items()}
|
|
176
|
+
all_fits_access_dict_keys = l1_dict_keys | l0_dict_keys
|
|
177
|
+
instance_dict_keys = {"_hdu", "name", "auto_squeeze"}
|
|
178
|
+
keys_defined_in_fits_access = all_fits_access_dict_keys | all_fits_access_properties
|
|
179
|
+
assert all_metadata_key_names == keys_defined_in_fits_access - instance_dict_keys
|
|
136
180
|
|
|
137
181
|
|
|
138
182
|
def test_from_single_hdu(hdu_with_complete_common_header):
|
|
@@ -211,6 +255,17 @@ def test_no_header_value(hdu_with_incomplete_common_header):
|
|
|
211
255
|
_ = L0FitsAccess(hdu_with_incomplete_common_header)
|
|
212
256
|
|
|
213
257
|
|
|
258
|
+
def test_default_header_values(hdu_with_incomplete_common_header):
|
|
259
|
+
"""
|
|
260
|
+
Given: an HDU with a header with missing common by-frame keywords
|
|
261
|
+
When: processing the HDU with a FITS access class that sets these keywords as optional
|
|
262
|
+
Then: the correct default values are set
|
|
263
|
+
"""
|
|
264
|
+
fits_obj = FitsAccessWithOptionalKeys(hdu_with_incomplete_common_header, name="foo")
|
|
265
|
+
assert fits_obj.optional1 == MetadataKeyWithOptionalKeys.optional1 + NOT_FOUND_MESSAGE
|
|
266
|
+
assert fits_obj.optional2 == "SO_RAD"
|
|
267
|
+
|
|
268
|
+
|
|
214
269
|
def test_as_subclass(hdu_with_complete_common_header):
|
|
215
270
|
"""
|
|
216
271
|
Given: an instrument-specific fits_obj class that subclasses L0FitsAccess
|
|
@@ -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
|
|
@@ -27,7 +28,6 @@ from dkist_processing_common.models.tags import Tag
|
|
|
27
28
|
from dkist_processing_common.tasks import WorkflowTaskBase
|
|
28
29
|
from dkist_processing_common.tests.test_input_dataset import input_dataset_frames_part_factory
|
|
29
30
|
|
|
30
|
-
|
|
31
31
|
FITS_FILE = "fits.dat"
|
|
32
32
|
NP_FILE = "np.npy"
|
|
33
33
|
|
|
@@ -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
|
|
@@ -19,6 +22,7 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
|
19
22
|
)
|
|
20
23
|
from dkist_processing_common.parsers.unique_bud import UniqueBud
|
|
21
24
|
from dkist_processing_common.tasks.parse_l0_input_data import ParseL0InputDataBase
|
|
25
|
+
from dkist_processing_common.tasks.parse_l0_input_data import default_constant_bud_factory
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
class VispHeaders(Spec122Dataset):
|
|
@@ -84,25 +88,33 @@ class VispHeaders(Spec122Dataset):
|
|
|
84
88
|
return self.index % self.num_mod
|
|
85
89
|
|
|
86
90
|
|
|
91
|
+
class ViSPMetadataKey(StrEnum):
|
|
92
|
+
num_mod = "VISP_010"
|
|
93
|
+
modstate = "VISP_011"
|
|
94
|
+
ip_task_type = "DKIST004"
|
|
95
|
+
|
|
96
|
+
|
|
87
97
|
class ViSPFitsAccess(FitsAccessBase):
|
|
88
98
|
def __init__(self, hdu, name, auto_squeeze=False):
|
|
89
99
|
super().__init__(hdu, name, auto_squeeze=auto_squeeze)
|
|
90
|
-
self.num_mod
|
|
91
|
-
self.modstate
|
|
92
|
-
self.ip_task_type
|
|
100
|
+
self._set_metadata_key_value(ViSPMetadataKey.num_mod)
|
|
101
|
+
self._set_metadata_key_value(ViSPMetadataKey.modstate)
|
|
102
|
+
self._set_metadata_key_value(ViSPMetadataKey.ip_task_type)
|
|
93
103
|
self.name = name
|
|
94
104
|
|
|
95
105
|
|
|
96
106
|
@pytest.fixture(scope="function")
|
|
97
107
|
def visp_flowers():
|
|
98
108
|
return [
|
|
99
|
-
SingleValueSingleKeyFlower(
|
|
109
|
+
SingleValueSingleKeyFlower(
|
|
110
|
+
tag_stem_name=StemName.modstate, metadata_key=ViSPMetadataKey.modstate
|
|
111
|
+
)
|
|
100
112
|
]
|
|
101
113
|
|
|
102
114
|
|
|
103
115
|
@pytest.fixture(scope="function")
|
|
104
116
|
def visp_buds():
|
|
105
|
-
return [UniqueBud(constant_name=BudName.num_modstates
|
|
117
|
+
return [UniqueBud(constant_name=BudName.num_modstates, metadata_key=ViSPMetadataKey.num_mod)]
|
|
106
118
|
|
|
107
119
|
|
|
108
120
|
@pytest.fixture(scope="function")
|
|
@@ -232,9 +244,9 @@ def test_make_flowerpots(parse_inputs_task):
|
|
|
232
244
|
|
|
233
245
|
assert len(tag_pot.stems) == 2
|
|
234
246
|
assert len(constant_pot.stems) == 3
|
|
235
|
-
assert tag_pot.stems[0].stem_name == StemName.modstate
|
|
247
|
+
assert tag_pot.stems[0].stem_name == StemName.modstate
|
|
236
248
|
assert tag_pot.stems[1].stem_name == "EMPTY_FLOWER"
|
|
237
|
-
assert constant_pot.stems[0].stem_name == BudName.num_modstates
|
|
249
|
+
assert constant_pot.stems[0].stem_name == BudName.num_modstates
|
|
238
250
|
assert constant_pot.stems[1].stem_name == "EMPTY_BUD"
|
|
239
251
|
assert constant_pot.stems[2].stem_name == "PICKY_BUD"
|
|
240
252
|
|
|
@@ -249,23 +261,10 @@ def test_subclass_flowers(visp_parse_inputs_task, max_cs_step_time_sec):
|
|
|
249
261
|
|
|
250
262
|
assert len(tag_pot.stems) == 1
|
|
251
263
|
assert len(constant_pot.stems) == 12
|
|
252
|
-
|
|
253
|
-
assert sorted([f.stem_name for f in
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
)
|
|
264
|
+
all_flower_names = [StemName.modstate]
|
|
265
|
+
assert sorted([f.stem_name for f in tag_pot.stems]) == sorted(all_flower_names)
|
|
266
|
+
all_bud_names = [b.stem_name for b in default_constant_bud_factory()] + [BudName.num_modstates]
|
|
267
|
+
assert sorted([f.stem_name for f in constant_pot.stems]) == sorted(all_bud_names)
|
|
269
268
|
|
|
270
269
|
|
|
271
270
|
def test_constants_correct(parse_inputs_task):
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Tests for the workflow file system wrapper
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Callable
|
|
6
7
|
from uuid import uuid4
|
|
7
8
|
|
|
8
9
|
import pytest
|
|
9
10
|
|
|
10
|
-
from dkist_processing_common._util.scratch import _flatten_list
|
|
11
11
|
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
12
|
+
from dkist_processing_common._util.scratch import _flatten_list
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@pytest.fixture(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
1
2
|
from itertools import chain
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
@@ -21,9 +22,9 @@ from dkist_processing_common.parsers.retarder import RetarderNameBud
|
|
|
21
22
|
from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
22
23
|
SingleValueSingleKeyFlower,
|
|
23
24
|
)
|
|
24
|
-
from dkist_processing_common.parsers.task import parse_header_ip_task_with_gains
|
|
25
25
|
from dkist_processing_common.parsers.task import PolcalTaskFlower
|
|
26
26
|
from dkist_processing_common.parsers.task import TaskTypeFlower
|
|
27
|
+
from dkist_processing_common.parsers.task import parse_header_ip_task_with_gains
|
|
27
28
|
from dkist_processing_common.parsers.time import AverageCadenceBud
|
|
28
29
|
from dkist_processing_common.parsers.time import ExposureTimeFlower
|
|
29
30
|
from dkist_processing_common.parsers.time import MaximumCadenceBud
|
|
@@ -39,30 +40,38 @@ from dkist_processing_common.parsers.unique_bud import UniqueBud
|
|
|
39
40
|
from dkist_processing_common.parsers.wavelength import ObserveWavelengthBud
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
class FitsReaderMetadataKey(StrEnum):
|
|
44
|
+
thing_id = "id_key"
|
|
45
|
+
constant_thing = "constant"
|
|
46
|
+
near_thing = "near"
|
|
47
|
+
proposal_id = "ID___013"
|
|
48
|
+
experiment_id = "ID___012"
|
|
49
|
+
ip_task_type = "DKIST004"
|
|
50
|
+
ip_start_time = "DKIST011"
|
|
51
|
+
fpa_exposure_time_ms = "XPOSURE"
|
|
52
|
+
sensor_readout_exposure_time_ms = "TEXPOSUR"
|
|
53
|
+
num_raw_frames_per_fpa = "NSUMEXP"
|
|
54
|
+
num_dsps_repeats = "DSPSREPS"
|
|
55
|
+
current_dsps_repeat = "DSPSNUM"
|
|
56
|
+
time_obs = "DATE-OBS"
|
|
57
|
+
gos_level3_status = "GOSLVL3"
|
|
58
|
+
gos_level3_lamp_status = "GOSLAMP"
|
|
59
|
+
gos_level0_status = "GOSLVL0"
|
|
60
|
+
gos_retarder_status = "GOSRET"
|
|
61
|
+
gos_polarizer_status = "GOSPOL"
|
|
62
|
+
wavelength = "LINEWAV"
|
|
63
|
+
roundable_time = "RTIME"
|
|
64
|
+
|
|
65
|
+
|
|
42
66
|
class FitsReader(FitsAccessBase):
|
|
43
67
|
def __init__(self, hdu, name):
|
|
44
68
|
super().__init__(hdu, name)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
self.
|
|
69
|
+
for key in FitsReaderMetadataKey:
|
|
70
|
+
self._set_metadata_key_value(key=key, optional=True)
|
|
71
|
+
self._set_metadata_key_value(
|
|
72
|
+
FitsReaderMetadataKey.roundable_time, optional=True, default=0.0
|
|
73
|
+
)
|
|
48
74
|
self.name = name
|
|
49
|
-
self.proposal_id: str = self.header.get("ID___013")
|
|
50
|
-
self.experiment_id: str = self.header.get("ID___012")
|
|
51
|
-
self.ip_task_type: str = self.header.get("DKIST004")
|
|
52
|
-
self.ip_start_time: str = self.header.get("DKIST011")
|
|
53
|
-
self.fpa_exposure_time_ms: float = self.header.get("XPOSURE")
|
|
54
|
-
self.sensor_readout_exposure_time_ms: float = self.header.get("TEXPOSUR")
|
|
55
|
-
self.num_raw_frames_per_fpa: int = self.header.get("NSUMEXP")
|
|
56
|
-
self.num_dsps_repeats: int = self.header.get("DSPSREPS")
|
|
57
|
-
self.current_dsps_repeat: int = self.header.get("DSPSNUM")
|
|
58
|
-
self.time_obs: str = self.header.get("DATE-OBS")
|
|
59
|
-
self.gos_level3_status: str = self.header.get("GOSLVL3")
|
|
60
|
-
self.gos_level3_lamp_status: str = self.header.get("GOSLAMP")
|
|
61
|
-
self.gos_level0_status: str = self.header.get("GOSLVL0")
|
|
62
|
-
self.gos_retarder_status: str = self.header.get("GOSRET")
|
|
63
|
-
self.gos_polarizer_status: str = self.header.get("GOSPOL")
|
|
64
|
-
self.wavelength: str = self.header.get("LINEWAV")
|
|
65
|
-
self.roundable_time: float = self.header.get("RTIME", 0.0)
|
|
66
75
|
|
|
67
76
|
|
|
68
77
|
@pytest.fixture()
|
|
@@ -926,4 +935,4 @@ def test_retarder_name_bud_error(bad_polcal_header_objs):
|
|
|
926
935
|
_ = bud.bud
|
|
927
936
|
|
|
928
937
|
|
|
929
|
-
# TODO: test new
|
|
938
|
+
# TODO: test new stem types that have been added to parse_l0_input_data
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
from astropy.io import fits
|
|
3
5
|
|
|
@@ -9,6 +11,15 @@ from dkist_processing_common.parsers.task import parse_polcal_task_type
|
|
|
9
11
|
from dkist_processing_common.parsers.task import passthrough_header_ip_task
|
|
10
12
|
|
|
11
13
|
|
|
14
|
+
class DummyMetadataKey(StrEnum):
|
|
15
|
+
ip_task_type = "IPTASK"
|
|
16
|
+
gos_level3_status = "GOSLVL3"
|
|
17
|
+
gos_level3_lamp_status = "GOSLAMP"
|
|
18
|
+
gos_level0_status = "GOSLVL0"
|
|
19
|
+
gos_retarder_status = "GOSRET"
|
|
20
|
+
gos_polarizer_status = "GOSPOL"
|
|
21
|
+
|
|
22
|
+
|
|
12
23
|
class DummyFitsAccess(FitsAccessBase):
|
|
13
24
|
def __init__(
|
|
14
25
|
self,
|
|
@@ -17,13 +28,12 @@ class DummyFitsAccess(FitsAccessBase):
|
|
|
17
28
|
auto_squeeze: bool = False, # Because L1 data should always have the right form, right?
|
|
18
29
|
):
|
|
19
30
|
super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
|
|
20
|
-
|
|
21
|
-
self.
|
|
22
|
-
self.
|
|
23
|
-
self.
|
|
24
|
-
self.
|
|
25
|
-
self.
|
|
26
|
-
self.gos_polarizer_status: str = self.header["GOSPOL"]
|
|
31
|
+
self._set_metadata_key_value(DummyMetadataKey.ip_task_type)
|
|
32
|
+
self._set_metadata_key_value(DummyMetadataKey.gos_level3_status)
|
|
33
|
+
self._set_metadata_key_value(DummyMetadataKey.gos_level3_lamp_status)
|
|
34
|
+
self._set_metadata_key_value(DummyMetadataKey.gos_level0_status)
|
|
35
|
+
self._set_metadata_key_value(DummyMetadataKey.gos_retarder_status)
|
|
36
|
+
self._set_metadata_key_value(DummyMetadataKey.gos_polarizer_status)
|
|
27
37
|
|
|
28
38
|
|
|
29
39
|
@pytest.fixture
|
|
@@ -7,9 +7,9 @@ from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
|
7
7
|
from dkist_processing_common.codecs.str import str_encoder
|
|
8
8
|
from dkist_processing_common.models.tags import Tag
|
|
9
9
|
from dkist_processing_common.tasks.teardown import Teardown
|
|
10
|
+
from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
|
|
10
11
|
from dkist_processing_common.tests.mock_metadata_store import fake_gql_client_factory
|
|
11
12
|
from dkist_processing_common.tests.mock_metadata_store import make_default_recipe_run_response
|
|
12
|
-
from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class TeardownTest(Teardown):
|
|
@@ -10,11 +10,11 @@ from dkist_processing_common.models.input_dataset import InputDatasetPartDocumen
|
|
|
10
10
|
from dkist_processing_common.models.tags import Tag
|
|
11
11
|
from dkist_processing_common.tasks import WorkflowTaskBase
|
|
12
12
|
from dkist_processing_common.tasks.transfer_input_data import TransferL0Data
|
|
13
|
+
from dkist_processing_common.tests.mock_metadata_store import InputDatasetRecipeRunResponseMapping
|
|
13
14
|
from dkist_processing_common.tests.mock_metadata_store import default_calibration_frames_doc
|
|
14
15
|
from dkist_processing_common.tests.mock_metadata_store import default_observe_frames_doc
|
|
15
16
|
from dkist_processing_common.tests.mock_metadata_store import default_parameters_doc
|
|
16
17
|
from dkist_processing_common.tests.mock_metadata_store import fake_gql_client_factory
|
|
17
|
-
from dkist_processing_common.tests.mock_metadata_store import InputDatasetRecipeRunResponseMapping
|
|
18
18
|
from dkist_processing_common.tests.mock_metadata_store import (
|
|
19
19
|
make_default_input_dataset_recipe_run_response,
|
|
20
20
|
)
|
|
@@ -60,8 +60,7 @@ def create_input_frames(
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class TransferL0DataTask(TransferL0Data):
|
|
63
|
-
def run(self) -> None:
|
|
64
|
-
...
|
|
63
|
+
def run(self) -> None: ...
|
|
65
64
|
|
|
66
65
|
|
|
67
66
|
@pytest.fixture
|
|
@@ -10,9 +10,9 @@ from dkist_processing_common.models.graphql import RecipeRunConfiguration
|
|
|
10
10
|
from dkist_processing_common.models.tags import Tag
|
|
11
11
|
from dkist_processing_common.tasks.mixin.globus import GlobusTransferItem
|
|
12
12
|
from dkist_processing_common.tasks.trial_output_data import TransferTrialData
|
|
13
|
+
from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
|
|
13
14
|
from dkist_processing_common.tests.mock_metadata_store import fake_gql_client_factory
|
|
14
15
|
from dkist_processing_common.tests.mock_metadata_store import make_default_recipe_run_response
|
|
15
|
-
from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@pytest.fixture
|
|
@@ -190,8 +190,7 @@ def test_dataset_id(workflow_data_task):
|
|
|
190
190
|
class ProvenanceTask(WorkflowTaskBase):
|
|
191
191
|
record_provenance = True
|
|
192
192
|
|
|
193
|
-
def run(self):
|
|
194
|
-
...
|
|
193
|
+
def run(self): ...
|
|
195
194
|
|
|
196
195
|
# Because I couldn't figure out how to mock the mixin
|
|
197
196
|
def metadata_store_record_provenance(self, is_task_manual: bool, library_versions: str):
|
|
@@ -21,14 +21,14 @@ from dkist_processing_common.models.graphql import RecipeRunProvenanceResponse
|
|
|
21
21
|
from dkist_processing_common.models.tags import Tag
|
|
22
22
|
from dkist_processing_common.models.wavelength import WavelengthRange
|
|
23
23
|
from dkist_processing_common.tasks.write_l1 import WriteL1Frame
|
|
24
|
-
from dkist_processing_common.tests.mock_metadata_store import
|
|
24
|
+
from dkist_processing_common.tests.mock_metadata_store import TILE_SIZE
|
|
25
25
|
from dkist_processing_common.tests.mock_metadata_store import InputDatasetRecipeRunResponseMapping
|
|
26
|
+
from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
|
|
27
|
+
from dkist_processing_common.tests.mock_metadata_store import fake_gql_client_factory
|
|
26
28
|
from dkist_processing_common.tests.mock_metadata_store import (
|
|
27
29
|
make_default_input_dataset_recipe_run_response,
|
|
28
30
|
)
|
|
29
31
|
from dkist_processing_common.tests.mock_metadata_store import make_default_recipe_run_response
|
|
30
|
-
from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
|
|
31
|
-
from dkist_processing_common.tests.mock_metadata_store import TILE_SIZE
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@pytest.fixture
|
|
@@ -273,13 +273,11 @@ def write_l1_task_with_empty_waveband(recipe_run_id, tmp_path, request):
|
|
|
273
273
|
],
|
|
274
274
|
)
|
|
275
275
|
def write_l1_task_no_data(request, recipe_run_id, tmp_path, complete_common_header):
|
|
276
|
-
with (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
) as task
|
|
282
|
-
):
|
|
276
|
+
with CompleteWriteL1Frame(
|
|
277
|
+
recipe_run_id=recipe_run_id,
|
|
278
|
+
workflow_name="workflow_name",
|
|
279
|
+
workflow_version="workflow_version",
|
|
280
|
+
) as task:
|
|
283
281
|
task.scratch = WorkflowFileSystem(recipe_run_id=recipe_run_id, scratch_base_path=tmp_path)
|
|
284
282
|
header = complete_common_header
|
|
285
283
|
header.pop("AO_LOCK", None)
|