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.
- changelog/280.misc.rst +1 -0
- changelog/282.feature.2.rst +2 -0
- changelog/282.feature.rst +2 -0
- changelog/284.feature.rst +1 -0
- changelog/285.feature.rst +2 -0
- changelog/285.misc.rst +2 -0
- changelog/286.feature.rst +2 -0
- changelog/287.misc.rst +1 -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 +9 -9
- dkist_processing_common/_util/tags.py +1 -0
- dkist_processing_common/codecs/array.py +20 -0
- dkist_processing_common/codecs/asdf.py +9 -3
- dkist_processing_common/codecs/basemodel.py +22 -0
- dkist_processing_common/codecs/bytes.py +1 -0
- dkist_processing_common/codecs/fits.py +37 -9
- 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 +64 -25
- dkist_processing_common/manual.py +6 -8
- dkist_processing_common/models/constants.py +373 -37
- dkist_processing_common/models/dkist_location.py +27 -0
- dkist_processing_common/models/fits_access.py +48 -0
- dkist_processing_common/models/flower_pot.py +231 -9
- dkist_processing_common/models/fried_parameter.py +41 -0
- dkist_processing_common/models/graphql.py +66 -75
- dkist_processing_common/models/input_dataset.py +117 -0
- 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 +2 -0
- dkist_processing_common/models/parameters.py +65 -28
- dkist_processing_common/models/quality.py +50 -5
- dkist_processing_common/models/tags.py +23 -21
- dkist_processing_common/models/task_name.py +3 -2
- dkist_processing_common/models/telemetry.py +28 -0
- dkist_processing_common/models/wavelength.py +3 -1
- dkist_processing_common/parsers/average_bud.py +46 -0
- dkist_processing_common/parsers/cs_step.py +13 -12
- dkist_processing_common/parsers/dsps_repeat.py +6 -4
- dkist_processing_common/parsers/experiment_id_bud.py +12 -4
- dkist_processing_common/parsers/id_bud.py +42 -27
- dkist_processing_common/parsers/l0_fits_access.py +5 -3
- dkist_processing_common/parsers/l1_fits_access.py +51 -23
- dkist_processing_common/parsers/lookup_bud.py +125 -0
- dkist_processing_common/parsers/near_bud.py +21 -20
- dkist_processing_common/parsers/observing_program_id_bud.py +24 -0
- dkist_processing_common/parsers/proposal_id_bud.py +13 -5
- dkist_processing_common/parsers/quality.py +2 -0
- dkist_processing_common/parsers/retarder.py +32 -0
- dkist_processing_common/parsers/single_value_single_key_flower.py +6 -1
- dkist_processing_common/parsers/task.py +8 -6
- dkist_processing_common/parsers/time.py +178 -72
- dkist_processing_common/parsers/unique_bud.py +21 -22
- dkist_processing_common/parsers/wavelength.py +5 -3
- dkist_processing_common/tasks/__init__.py +3 -2
- dkist_processing_common/tasks/assemble_movie.py +4 -3
- dkist_processing_common/tasks/base.py +59 -60
- dkist_processing_common/tasks/l1_output_data.py +54 -53
- dkist_processing_common/tasks/mixin/globus.py +24 -27
- dkist_processing_common/tasks/mixin/interservice_bus.py +1 -0
- dkist_processing_common/tasks/mixin/metadata_store.py +108 -243
- dkist_processing_common/tasks/mixin/object_store.py +22 -0
- dkist_processing_common/tasks/mixin/quality/__init__.py +1 -0
- dkist_processing_common/tasks/mixin/quality/_base.py +8 -1
- dkist_processing_common/tasks/mixin/quality/_metrics.py +166 -14
- dkist_processing_common/tasks/output_data_base.py +4 -3
- dkist_processing_common/tasks/parse_l0_input_data.py +277 -15
- dkist_processing_common/tasks/quality_metrics.py +9 -9
- dkist_processing_common/tasks/teardown.py +7 -7
- dkist_processing_common/tasks/transfer_input_data.py +67 -69
- dkist_processing_common/tasks/trial_catalog.py +77 -17
- dkist_processing_common/tasks/trial_output_data.py +16 -17
- dkist_processing_common/tasks/write_l1.py +102 -72
- dkist_processing_common/tests/conftest.py +32 -173
- dkist_processing_common/tests/mock_metadata_store.py +271 -0
- dkist_processing_common/tests/test_assemble_movie.py +4 -4
- dkist_processing_common/tests/test_assemble_quality.py +32 -4
- dkist_processing_common/tests/test_base.py +5 -19
- dkist_processing_common/tests/test_codecs.py +103 -12
- dkist_processing_common/tests/test_constants.py +15 -0
- dkist_processing_common/tests/test_dkist_location.py +15 -0
- dkist_processing_common/tests/test_fits_access.py +56 -19
- dkist_processing_common/tests/test_flower_pot.py +147 -5
- dkist_processing_common/tests/test_fried_parameter.py +27 -0
- dkist_processing_common/tests/test_input_dataset.py +78 -361
- dkist_processing_common/tests/test_interservice_bus.py +1 -0
- dkist_processing_common/tests/test_interservice_bus_mixin.py +1 -1
- dkist_processing_common/tests/test_manual_processing.py +33 -0
- dkist_processing_common/tests/test_output_data_base.py +5 -7
- dkist_processing_common/tests/test_parameters.py +71 -22
- dkist_processing_common/tests/test_parse_l0_input_data.py +115 -32
- dkist_processing_common/tests/test_publish_catalog_messages.py +2 -24
- dkist_processing_common/tests/test_quality.py +1 -0
- dkist_processing_common/tests/test_quality_mixin.py +255 -23
- dkist_processing_common/tests/test_scratch.py +2 -1
- dkist_processing_common/tests/test_stems.py +511 -168
- dkist_processing_common/tests/test_submit_dataset_metadata.py +3 -7
- dkist_processing_common/tests/test_tags.py +1 -0
- dkist_processing_common/tests/test_task_name.py +1 -1
- dkist_processing_common/tests/test_task_parsing.py +17 -7
- dkist_processing_common/tests/test_teardown.py +28 -24
- dkist_processing_common/tests/test_transfer_input_data.py +270 -125
- dkist_processing_common/tests/test_transfer_l1_output_data.py +2 -3
- dkist_processing_common/tests/test_trial_catalog.py +83 -8
- dkist_processing_common/tests/test_trial_output_data.py +46 -73
- dkist_processing_common/tests/test_workflow_task_base.py +8 -10
- dkist_processing_common/tests/test_write_l1.py +298 -76
- dkist_processing_common-12.1.0rc1.dist-info/METADATA +265 -0
- dkist_processing_common-12.1.0rc1.dist-info/RECORD +134 -0
- {dkist_processing_common-10.5.4.dist-info → dkist_processing_common-12.1.0rc1.dist-info}/WHEEL +1 -1
- docs/conf.py +1 -0
- docs/index.rst +1 -1
- docs/landing_page.rst +13 -0
- dkist_processing_common/tasks/mixin/input_dataset.py +0 -166
- dkist_processing_common-10.5.4.dist-info/METADATA +0 -175
- dkist_processing_common-10.5.4.dist-info/RECORD +0 -112
- {dkist_processing_common-10.5.4.dist-info → dkist_processing_common-12.1.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,63 +1,115 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from itertools import chain
|
|
4
|
+
|
|
1
5
|
import pytest
|
|
2
6
|
from astropy.io import fits
|
|
3
7
|
|
|
4
8
|
from dkist_processing_common.models.constants import BudName
|
|
5
9
|
from dkist_processing_common.models.fits_access import FitsAccessBase
|
|
10
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
6
11
|
from dkist_processing_common.models.tags import StemName
|
|
7
12
|
from dkist_processing_common.models.task_name import TaskName
|
|
13
|
+
from dkist_processing_common.parsers.average_bud import TaskAverageBud
|
|
8
14
|
from dkist_processing_common.parsers.cs_step import CSStepFlower
|
|
9
15
|
from dkist_processing_common.parsers.cs_step import NumCSStepBud
|
|
10
16
|
from dkist_processing_common.parsers.dsps_repeat import DspsRepeatNumberFlower
|
|
11
17
|
from dkist_processing_common.parsers.dsps_repeat import TotalDspsRepeatsBud
|
|
12
18
|
from dkist_processing_common.parsers.experiment_id_bud import ContributingExperimentIdsBud
|
|
13
19
|
from dkist_processing_common.parsers.experiment_id_bud import ExperimentIdBud
|
|
20
|
+
from dkist_processing_common.parsers.id_bud import TaskContributingIdsBud
|
|
21
|
+
from dkist_processing_common.parsers.lookup_bud import TaskTimeLookupBud
|
|
22
|
+
from dkist_processing_common.parsers.lookup_bud import TimeLookupBud
|
|
14
23
|
from dkist_processing_common.parsers.near_bud import NearFloatBud
|
|
15
24
|
from dkist_processing_common.parsers.near_bud import TaskNearFloatBud
|
|
25
|
+
from dkist_processing_common.parsers.observing_program_id_bud import (
|
|
26
|
+
TaskContributingObservingProgramExecutionIdsBud,
|
|
27
|
+
)
|
|
16
28
|
from dkist_processing_common.parsers.proposal_id_bud import ContributingProposalIdsBud
|
|
17
29
|
from dkist_processing_common.parsers.proposal_id_bud import ProposalIdBud
|
|
30
|
+
from dkist_processing_common.parsers.retarder import RetarderNameBud
|
|
18
31
|
from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
19
32
|
SingleValueSingleKeyFlower,
|
|
20
33
|
)
|
|
21
|
-
from dkist_processing_common.parsers.task import parse_header_ip_task_with_gains
|
|
22
34
|
from dkist_processing_common.parsers.task import PolcalTaskFlower
|
|
23
35
|
from dkist_processing_common.parsers.task import TaskTypeFlower
|
|
36
|
+
from dkist_processing_common.parsers.task import parse_header_ip_task_with_gains
|
|
24
37
|
from dkist_processing_common.parsers.time import AverageCadenceBud
|
|
25
38
|
from dkist_processing_common.parsers.time import ExposureTimeFlower
|
|
26
39
|
from dkist_processing_common.parsers.time import MaximumCadenceBud
|
|
27
40
|
from dkist_processing_common.parsers.time import MinimumCadenceBud
|
|
28
41
|
from dkist_processing_common.parsers.time import ObsIpStartTimeBud
|
|
29
42
|
from dkist_processing_common.parsers.time import ReadoutExpTimeFlower
|
|
43
|
+
from dkist_processing_common.parsers.time import TaskDateBeginBud
|
|
44
|
+
from dkist_processing_common.parsers.time import TaskDatetimeBudBase
|
|
30
45
|
from dkist_processing_common.parsers.time import TaskExposureTimesBud
|
|
31
46
|
from dkist_processing_common.parsers.time import TaskReadoutExpTimesBud
|
|
47
|
+
from dkist_processing_common.parsers.time import TaskRoundTimeBudBase
|
|
32
48
|
from dkist_processing_common.parsers.time import VarianceCadenceBud
|
|
33
49
|
from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
|
|
34
50
|
from dkist_processing_common.parsers.unique_bud import UniqueBud
|
|
35
51
|
from dkist_processing_common.parsers.wavelength import ObserveWavelengthBud
|
|
36
52
|
|
|
37
53
|
|
|
54
|
+
class FitsReaderMetadataKey(StrEnum):
|
|
55
|
+
thing_id = "id_key"
|
|
56
|
+
constant_thing = "constant"
|
|
57
|
+
near_thing = "near"
|
|
58
|
+
proposal_id = "ID___013"
|
|
59
|
+
experiment_id = "ID___012"
|
|
60
|
+
observing_program_execution_id = "ID___008"
|
|
61
|
+
ip_task_type = "DKIST004"
|
|
62
|
+
ip_start_time = "DKIST011"
|
|
63
|
+
fpa_exposure_time_ms = "XPOSURE"
|
|
64
|
+
sensor_readout_exposure_time_ms = "TEXPOSUR"
|
|
65
|
+
num_raw_frames_per_fpa = "NSUMEXP"
|
|
66
|
+
num_dsps_repeats = "DSPSREPS"
|
|
67
|
+
current_dsps_repeat = "DSPSNUM"
|
|
68
|
+
time_obs = "DATE-OBS"
|
|
69
|
+
gos_level3_status = "GOSLVL3"
|
|
70
|
+
gos_level3_lamp_status = "GOSLAMP"
|
|
71
|
+
gos_level0_status = "GOSLVL0"
|
|
72
|
+
gos_retarder_status = "GOSRET"
|
|
73
|
+
gos_polarizer_status = "GOSPOL"
|
|
74
|
+
wavelength = "LINEWAV"
|
|
75
|
+
roundable_time = "RTIME"
|
|
76
|
+
|
|
77
|
+
|
|
38
78
|
class FitsReader(FitsAccessBase):
|
|
39
79
|
def __init__(self, hdu, name):
|
|
40
80
|
super().__init__(hdu, name)
|
|
41
|
-
self.thing_id: int = self.header.get(
|
|
42
|
-
self.constant_thing:
|
|
43
|
-
self.near_thing: float = self.header.get(
|
|
81
|
+
self.thing_id: int = self.header.get(FitsReaderMetadataKey.thing_id)
|
|
82
|
+
self.constant_thing: int = self.header.get(FitsReaderMetadataKey.constant_thing)
|
|
83
|
+
self.near_thing: float = self.header.get(FitsReaderMetadataKey.near_thing)
|
|
44
84
|
self.name = name
|
|
45
|
-
self.proposal_id: str = self.header.get(
|
|
46
|
-
self.experiment_id: str = self.header.get(
|
|
47
|
-
self.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
self.
|
|
51
|
-
self.
|
|
52
|
-
self.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
self.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
self.
|
|
59
|
-
|
|
60
|
-
|
|
85
|
+
self.proposal_id: str = self.header.get(FitsReaderMetadataKey.proposal_id)
|
|
86
|
+
self.experiment_id: str = self.header.get(FitsReaderMetadataKey.experiment_id)
|
|
87
|
+
self.observing_program_execution_id: str = self.header.get(
|
|
88
|
+
FitsReaderMetadataKey.observing_program_execution_id
|
|
89
|
+
)
|
|
90
|
+
self.ip_task_type: str = self.header.get(FitsReaderMetadataKey.ip_task_type)
|
|
91
|
+
self.ip_start_time: str = self.header.get(FitsReaderMetadataKey.ip_start_time)
|
|
92
|
+
self.fpa_exposure_time_ms: float = self.header.get(
|
|
93
|
+
FitsReaderMetadataKey.fpa_exposure_time_ms
|
|
94
|
+
)
|
|
95
|
+
self.sensor_readout_exposure_time_ms: float = self.header.get(
|
|
96
|
+
FitsReaderMetadataKey.sensor_readout_exposure_time_ms
|
|
97
|
+
)
|
|
98
|
+
self.num_raw_frames_per_fpa: int = self.header.get(
|
|
99
|
+
FitsReaderMetadataKey.num_raw_frames_per_fpa
|
|
100
|
+
)
|
|
101
|
+
self.num_dsps_repeats: int = self.header.get(FitsReaderMetadataKey.num_dsps_repeats)
|
|
102
|
+
self.current_dsps_repeat: int = self.header.get(FitsReaderMetadataKey.current_dsps_repeat)
|
|
103
|
+
self.time_obs: str = self.header.get(FitsReaderMetadataKey.time_obs)
|
|
104
|
+
self.gos_level3_status: str = self.header.get(FitsReaderMetadataKey.gos_level3_status)
|
|
105
|
+
self.gos_level3_lamp_status: str = self.header.get(
|
|
106
|
+
FitsReaderMetadataKey.gos_level3_lamp_status
|
|
107
|
+
)
|
|
108
|
+
self.gos_level0_status: str = self.header.get(FitsReaderMetadataKey.gos_level0_status)
|
|
109
|
+
self.gos_retarder_status: str = self.header.get(FitsReaderMetadataKey.gos_retarder_status)
|
|
110
|
+
self.gos_polarizer_status: str = self.header.get(FitsReaderMetadataKey.gos_polarizer_status)
|
|
111
|
+
self.wavelength: str = self.header.get(FitsReaderMetadataKey.wavelength)
|
|
112
|
+
self.roundable_time: float = self.header.get(FitsReaderMetadataKey.roundable_time, 0.0)
|
|
61
113
|
|
|
62
114
|
|
|
63
115
|
@pytest.fixture()
|
|
@@ -71,6 +123,7 @@ def basic_header_objs():
|
|
|
71
123
|
"DKIST004": "observe",
|
|
72
124
|
"ID___012": "experiment_id_1",
|
|
73
125
|
"ID___013": "proposal_id_1",
|
|
126
|
+
"ID___008": "observing_program_execution_id_1",
|
|
74
127
|
"XPOSURE": 0.0013000123,
|
|
75
128
|
"TEXPOSUR": 10.0,
|
|
76
129
|
"NSUMEXP": 3,
|
|
@@ -89,6 +142,7 @@ def basic_header_objs():
|
|
|
89
142
|
"DKIST004": "observe",
|
|
90
143
|
"ID___012": "experiment_id_1",
|
|
91
144
|
"ID___013": "proposal_id_1",
|
|
145
|
+
"ID___008": "observing_program_execution_id_2",
|
|
92
146
|
"XPOSURE": 0.0013000987,
|
|
93
147
|
"TEXPOSUR": 10.0,
|
|
94
148
|
"NSUMEXP": 3,
|
|
@@ -97,6 +151,7 @@ def basic_header_objs():
|
|
|
97
151
|
"DATE-OBS": "2022-06-17T22:00:01.000",
|
|
98
152
|
"DKIST011": "2023-09-28T10:23.000",
|
|
99
153
|
"LINEWAV": 666.0,
|
|
154
|
+
"GOSRET": "incorrect",
|
|
100
155
|
}
|
|
101
156
|
),
|
|
102
157
|
"thing2": fits.header.Header(
|
|
@@ -107,6 +162,7 @@ def basic_header_objs():
|
|
|
107
162
|
"DKIST004": "dark",
|
|
108
163
|
"ID___012": "experiment_id_2",
|
|
109
164
|
"ID___013": "proposal_id_2",
|
|
165
|
+
"ID___008": "observing_program_execution_id_2",
|
|
110
166
|
"XPOSURE": 12.345,
|
|
111
167
|
"TEXPOSUR": 1.123456789,
|
|
112
168
|
"NSUMEXP": 1,
|
|
@@ -115,6 +171,8 @@ def basic_header_objs():
|
|
|
115
171
|
"DATE-OBS": "2022-06-17T22:00:02.000",
|
|
116
172
|
"DKIST011": "1903-01-01T12:00.000",
|
|
117
173
|
"LINEWAV": 0.0,
|
|
174
|
+
"GOSRET": "wrong",
|
|
175
|
+
"RTIME": 2.3400000009999,
|
|
118
176
|
}
|
|
119
177
|
),
|
|
120
178
|
"thing3": fits.header.Header(
|
|
@@ -125,6 +183,7 @@ def basic_header_objs():
|
|
|
125
183
|
"DKIST004": "observe",
|
|
126
184
|
"ID___012": "experiment_id_1",
|
|
127
185
|
"ID___013": "proposal_id_1",
|
|
186
|
+
"ID___008": "observing_program_execution_id_1",
|
|
128
187
|
"XPOSURE": 100.0,
|
|
129
188
|
"TEXPOSUR": 11.0,
|
|
130
189
|
"NSUMEXP": 4,
|
|
@@ -133,6 +192,28 @@ def basic_header_objs():
|
|
|
133
192
|
"DATE-OBS": "2022-06-17T22:00:03.000",
|
|
134
193
|
"DKIST011": "2023-09-28T10:23.000",
|
|
135
194
|
"LINEWAV": 666.0,
|
|
195
|
+
"GOSRET": "clear",
|
|
196
|
+
},
|
|
197
|
+
),
|
|
198
|
+
"thing4": fits.header.Header(
|
|
199
|
+
{
|
|
200
|
+
"DKIST004": "gain",
|
|
201
|
+
"ID___013": "proposal_id_1",
|
|
202
|
+
"ID___008": "observing_program_execution_id_1",
|
|
203
|
+
"id_key": 0,
|
|
204
|
+
"constant": 6.28,
|
|
205
|
+
"near": 1.23,
|
|
206
|
+
"ID___012": "experiment_id_1",
|
|
207
|
+
"XPOSURE": 100.0,
|
|
208
|
+
"TEXPOSUR": 11.0,
|
|
209
|
+
"NSUMEXP": 5,
|
|
210
|
+
"DSPSNUM": 2,
|
|
211
|
+
"DSPSREPS": 2,
|
|
212
|
+
"DATE-OBS": "2022-06-17T22:00:03.000",
|
|
213
|
+
"DKIST011": "2023-09-28T10:23.000",
|
|
214
|
+
"LINEWAV": 666.0,
|
|
215
|
+
"GOSRET": "clear",
|
|
216
|
+
"RTIME": 2.340000004444,
|
|
136
217
|
}
|
|
137
218
|
),
|
|
138
219
|
}
|
|
@@ -150,7 +231,12 @@ def task_with_gains_header_objs():
|
|
|
150
231
|
|
|
151
232
|
|
|
152
233
|
@pytest.fixture
|
|
153
|
-
def
|
|
234
|
+
def retarder_name():
|
|
235
|
+
return "Foo Bar"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@pytest.fixture
|
|
239
|
+
def task_with_polcal_header_objs(retarder_name):
|
|
154
240
|
header_dict = {
|
|
155
241
|
"polcal_dark": fits.header.Header(
|
|
156
242
|
{"DKIST004": "polcal", "GOSLVL0": "DarkShutter", "GOSPOL": "clear", "GOSRET": "clear"}
|
|
@@ -158,7 +244,9 @@ def task_with_polcal_header_objs():
|
|
|
158
244
|
"polcal_gain": fits.header.Header(
|
|
159
245
|
{"DKIST004": "polcal", "GOSLVL0": "FieldStop", "GOSPOL": "clear", "GOSRET": "clear"}
|
|
160
246
|
),
|
|
161
|
-
"just_polcal": fits.header.Header(
|
|
247
|
+
"just_polcal": fits.header.Header(
|
|
248
|
+
{"DKIST004": "polcal", "GOSLVL0": "something", "GOSRET": retarder_name}
|
|
249
|
+
),
|
|
162
250
|
}
|
|
163
251
|
return (FitsReader.from_header(header, name=path) for path, header in header_dict.items())
|
|
164
252
|
|
|
@@ -190,28 +278,61 @@ def bad_header_objs():
|
|
|
190
278
|
"LINEWAV": 1.0,
|
|
191
279
|
}
|
|
192
280
|
),
|
|
281
|
+
"thing2": fits.header.Header(
|
|
282
|
+
{
|
|
283
|
+
"id_key": 1,
|
|
284
|
+
"constant": 2.78,
|
|
285
|
+
"near": 1.76,
|
|
286
|
+
"DKIST004": "gain",
|
|
287
|
+
"DSPSREPS": 2,
|
|
288
|
+
"DSPSNUM": 2,
|
|
289
|
+
"DATE-OBS": "2022-06-17T22:00:03.000",
|
|
290
|
+
"LINEWAV": 1.0,
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
"thing4": fits.header.Header(
|
|
294
|
+
{
|
|
295
|
+
"id_key": 1,
|
|
296
|
+
"constant": 6.66,
|
|
297
|
+
"near": 1.76,
|
|
298
|
+
"DKIST004": "dark",
|
|
299
|
+
"DSPSREPS": 2,
|
|
300
|
+
"DSPSNUM": 2,
|
|
301
|
+
"DATE-OBS": "2022-06-17T22:00:03.000",
|
|
302
|
+
"LINEWAV": 1.0,
|
|
303
|
+
}
|
|
304
|
+
),
|
|
193
305
|
}
|
|
194
306
|
return (FitsReader.from_header(header, name=path) for path, header in bad_headers.items())
|
|
195
307
|
|
|
196
308
|
|
|
309
|
+
@pytest.fixture
|
|
310
|
+
def bad_polcal_header_objs():
|
|
311
|
+
# I.e., GOSRET has multiple values
|
|
312
|
+
header_dict = {
|
|
313
|
+
"thing1": fits.header.Header({"DKIST004": "polcal", "GOSRET": "clear"}),
|
|
314
|
+
"thing2": fits.header.Header({"DKIST004": "polcal", "GOSRET": "RET1"}),
|
|
315
|
+
"thing3": fits.header.Header({"DKIST004": "polcal", "GOSRET": "RET2"}),
|
|
316
|
+
}
|
|
317
|
+
return (FitsReader.from_header(header, name=path) for path, header in header_dict.items())
|
|
318
|
+
|
|
319
|
+
|
|
197
320
|
def test_unique_bud(basic_header_objs):
|
|
198
321
|
"""
|
|
199
322
|
Given: A set of headers with a constant value header key
|
|
200
323
|
When: Ingesting headers with a UniqueBud and asking for the value
|
|
201
324
|
Then: The Bud's value is the header constant value
|
|
202
325
|
"""
|
|
203
|
-
|
|
326
|
+
bud_obj = UniqueBud(
|
|
204
327
|
constant_name="constant",
|
|
205
328
|
metadata_key="constant_thing",
|
|
206
329
|
)
|
|
207
|
-
assert
|
|
330
|
+
assert bud_obj.stem_name == "constant"
|
|
208
331
|
for fo in basic_header_objs:
|
|
209
332
|
key = fo.name
|
|
210
|
-
|
|
333
|
+
bud_obj.update(key, fo)
|
|
211
334
|
|
|
212
|
-
|
|
213
|
-
assert len(petal) == 1
|
|
214
|
-
assert petal[0].value == 6.28
|
|
335
|
+
assert bud_obj.bud.value == 6.28
|
|
215
336
|
|
|
216
337
|
|
|
217
338
|
def test_unique_bud_non_unique_inputs(bad_header_objs):
|
|
@@ -220,54 +341,66 @@ def test_unique_bud_non_unique_inputs(bad_header_objs):
|
|
|
220
341
|
When: Ingesting headers with a UniqueBud and asking for the value
|
|
221
342
|
Then: An error is raised
|
|
222
343
|
"""
|
|
223
|
-
|
|
344
|
+
bud_obj = UniqueBud(
|
|
224
345
|
constant_name="constant",
|
|
225
346
|
metadata_key="constant_thing",
|
|
226
347
|
)
|
|
227
|
-
assert
|
|
348
|
+
assert bud_obj.stem_name == "constant"
|
|
228
349
|
for fo in bad_header_objs:
|
|
229
350
|
key = fo.name
|
|
230
|
-
|
|
351
|
+
bud_obj.update(key, fo)
|
|
231
352
|
|
|
232
|
-
with pytest.raises(ValueError):
|
|
233
|
-
|
|
353
|
+
with pytest.raises(ValueError, match="Multiple constant values found! Values:"):
|
|
354
|
+
_ = bud_obj.bud
|
|
234
355
|
|
|
235
356
|
|
|
236
|
-
|
|
357
|
+
@pytest.mark.parametrize(
|
|
358
|
+
"ip_task_type",
|
|
359
|
+
[
|
|
360
|
+
pytest.param("observe", id="single_task_type"),
|
|
361
|
+
pytest.param(["observe", "gain"], id="task_type_list"),
|
|
362
|
+
],
|
|
363
|
+
)
|
|
364
|
+
def test_task_unique_bud(basic_header_objs, ip_task_type):
|
|
237
365
|
"""
|
|
238
366
|
Given: A set of headers with a constant value header key
|
|
239
367
|
When: Ingesting headers with a TaskUniqueBud and asking for the value
|
|
240
368
|
Then: The bud's value is the header constant value
|
|
241
369
|
"""
|
|
242
|
-
|
|
243
|
-
constant_name="proposal", metadata_key="proposal_id", ip_task_type
|
|
370
|
+
bud_obj = TaskUniqueBud(
|
|
371
|
+
constant_name="proposal", metadata_key="proposal_id", ip_task_types=ip_task_type
|
|
244
372
|
)
|
|
245
|
-
assert
|
|
373
|
+
assert bud_obj.stem_name == "proposal"
|
|
246
374
|
for fo in basic_header_objs:
|
|
247
375
|
key = fo.name
|
|
248
|
-
|
|
376
|
+
bud_obj.update(key, fo)
|
|
249
377
|
|
|
250
|
-
|
|
251
|
-
assert len(petal) == 1
|
|
252
|
-
assert petal[0].value == "proposal_id_1"
|
|
378
|
+
assert bud_obj.bud.value == "proposal_id_1"
|
|
253
379
|
|
|
254
380
|
|
|
255
|
-
|
|
381
|
+
@pytest.mark.parametrize(
|
|
382
|
+
"ip_task_type",
|
|
383
|
+
[
|
|
384
|
+
pytest.param("observe", id="single_task_type"),
|
|
385
|
+
pytest.param(["dark", "gain"], id="task_type_list"),
|
|
386
|
+
],
|
|
387
|
+
)
|
|
388
|
+
def test_task_unique_bud_non_unique_inputs(bad_header_objs, ip_task_type):
|
|
256
389
|
"""
|
|
257
390
|
Given: A set of headers with a non-constant header key that is expected to be constant
|
|
258
391
|
When: Ingesting headers with a UniqueBud and asking for the value
|
|
259
392
|
Then: An error is raised
|
|
260
393
|
"""
|
|
261
394
|
bud = TaskUniqueBud(
|
|
262
|
-
constant_name="constant", metadata_key="constant_thing", ip_task_type
|
|
395
|
+
constant_name="constant", metadata_key="constant_thing", ip_task_types=ip_task_type
|
|
263
396
|
)
|
|
264
397
|
assert bud.stem_name == "constant"
|
|
265
398
|
for fo in bad_header_objs:
|
|
266
399
|
key = fo.name
|
|
267
400
|
bud.update(key, fo)
|
|
268
401
|
|
|
269
|
-
with pytest.raises(ValueError):
|
|
270
|
-
|
|
402
|
+
with pytest.raises(ValueError, match="Multiple constant values found! Values:"):
|
|
403
|
+
_ = bud.bud
|
|
271
404
|
|
|
272
405
|
|
|
273
406
|
def test_single_value_single_key_flower(basic_header_objs):
|
|
@@ -285,13 +418,69 @@ def test_single_value_single_key_flower(basic_header_objs):
|
|
|
285
418
|
petals = sorted(list(flower.petals), key=lambda x: x.value)
|
|
286
419
|
assert len(petals) == 3
|
|
287
420
|
assert petals[0].value == 0
|
|
288
|
-
assert petals[0].keys == ["thing0", "thing3"]
|
|
421
|
+
assert petals[0].keys == ["thing0", "thing3", "thing4"]
|
|
289
422
|
assert petals[1].value == 1
|
|
290
423
|
assert petals[1].keys == ["thing1"]
|
|
291
424
|
assert petals[2].value == 2
|
|
292
425
|
assert petals[2].keys == ["thing2"]
|
|
293
426
|
|
|
294
427
|
|
|
428
|
+
@pytest.mark.parametrize(
|
|
429
|
+
"ip_task_type, expected_value",
|
|
430
|
+
[
|
|
431
|
+
pytest.param("dark", (1655503202.0,), id="single_task_type"),
|
|
432
|
+
pytest.param(["dark", "gain"], (1655503202.0, 1655503203.0), id="task_type_list"),
|
|
433
|
+
pytest.param(
|
|
434
|
+
["dark", "gain", "observe"],
|
|
435
|
+
(1655503200.0, 1655503201.0, 1655503202.0, 1655503203.0, 1655503203.0),
|
|
436
|
+
id="task_type_list2",
|
|
437
|
+
),
|
|
438
|
+
],
|
|
439
|
+
)
|
|
440
|
+
def test_task_datetime_base_bud(basic_header_objs, ip_task_type, expected_value):
|
|
441
|
+
"""
|
|
442
|
+
Given: A set of headers with a datetime value that does not need to be rounded
|
|
443
|
+
When: Ingesting headers with a `TaskDatetimeBudBase` bud and asking for the value
|
|
444
|
+
Then: The bud's value is the list of datetimes in seconds
|
|
445
|
+
"""
|
|
446
|
+
bud_obj = TaskDatetimeBudBase(
|
|
447
|
+
stem_name="datetimes",
|
|
448
|
+
metadata_key=FitsReaderMetadataKey.time_obs,
|
|
449
|
+
ip_task_types=ip_task_type,
|
|
450
|
+
)
|
|
451
|
+
assert bud_obj.stem_name == "datetimes"
|
|
452
|
+
for fo in basic_header_objs:
|
|
453
|
+
key = fo.name
|
|
454
|
+
bud_obj.update(key, fo)
|
|
455
|
+
|
|
456
|
+
assert bud_obj.bud.value == expected_value
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
@pytest.mark.parametrize(
|
|
460
|
+
"ip_task_type, expected_value",
|
|
461
|
+
[
|
|
462
|
+
pytest.param("dark", (2.34,), id="single_task_type"),
|
|
463
|
+
pytest.param(["dark", "gain"], (2.34,), id="task_type_list"),
|
|
464
|
+
pytest.param(["dark", "gain", "observe"], (0.0, 2.34), id="task_type_list2"),
|
|
465
|
+
],
|
|
466
|
+
)
|
|
467
|
+
def test_task_round_time_base_bud(basic_header_objs, ip_task_type, expected_value):
|
|
468
|
+
"""
|
|
469
|
+
Given: A set of headers with a time value that needs to be rounded
|
|
470
|
+
When: Ingesting headers with a `TaskRoundTimeBudBase` bud and asking for the value
|
|
471
|
+
Then: The bud's value is the header constant value
|
|
472
|
+
"""
|
|
473
|
+
bud_obj = TaskRoundTimeBudBase(
|
|
474
|
+
stem_name="rounded_time", metadata_key="roundable_time", ip_task_types=ip_task_type
|
|
475
|
+
)
|
|
476
|
+
assert bud_obj.stem_name == "rounded_time"
|
|
477
|
+
for fo in basic_header_objs:
|
|
478
|
+
key = fo.name
|
|
479
|
+
bud_obj.update(key, fo)
|
|
480
|
+
|
|
481
|
+
assert bud_obj.bud.value == expected_value
|
|
482
|
+
|
|
483
|
+
|
|
295
484
|
def test_cs_step_flower(grouped_cal_sequence_headers, non_polcal_headers, max_cs_step_time_sec):
|
|
296
485
|
"""
|
|
297
486
|
Given: A set of PolCal headers, non-PolCal headers, and the CSStepFlower
|
|
@@ -320,17 +509,15 @@ def test_num_cs_step_bud(grouped_cal_sequence_headers, non_polcal_headers, max_c
|
|
|
320
509
|
When: Updating the NumCSStepBud with all headers
|
|
321
510
|
Then: The bud reports the correct number of CS Steps (thus ignoring the non-PolCal frames)
|
|
322
511
|
"""
|
|
323
|
-
|
|
512
|
+
num_cs_bud_obj = NumCSStepBud(max_cs_step_time_sec=max_cs_step_time_sec)
|
|
324
513
|
for step, headers in grouped_cal_sequence_headers.items():
|
|
325
514
|
for h in headers:
|
|
326
|
-
|
|
515
|
+
num_cs_bud_obj.update(step, h)
|
|
327
516
|
|
|
328
517
|
for h in non_polcal_headers:
|
|
329
|
-
|
|
518
|
+
num_cs_bud_obj.update("foo", h)
|
|
330
519
|
|
|
331
|
-
bud
|
|
332
|
-
assert len(bud) == 1
|
|
333
|
-
assert bud[0].value == len(grouped_cal_sequence_headers.keys())
|
|
520
|
+
assert num_cs_bud_obj.bud.value == len(grouped_cal_sequence_headers.keys())
|
|
334
521
|
|
|
335
522
|
|
|
336
523
|
def test_proposal_id_bud(basic_header_objs):
|
|
@@ -339,15 +526,13 @@ def test_proposal_id_bud(basic_header_objs):
|
|
|
339
526
|
When: Ingesting the headers with a ProposalIdBud
|
|
340
527
|
Then: The Bud's petal has the correct value
|
|
341
528
|
"""
|
|
342
|
-
|
|
343
|
-
assert
|
|
529
|
+
bud_obj = ProposalIdBud()
|
|
530
|
+
assert bud_obj.stem_name == BudName.proposal_id.value
|
|
344
531
|
for fo in basic_header_objs:
|
|
345
532
|
key = fo.name
|
|
346
|
-
|
|
533
|
+
bud_obj.update(key, fo)
|
|
347
534
|
|
|
348
|
-
|
|
349
|
-
assert len(petal) == 1
|
|
350
|
-
assert petal[0].value == "proposal_id_1"
|
|
535
|
+
assert bud_obj.bud.value == "proposal_id_1"
|
|
351
536
|
|
|
352
537
|
|
|
353
538
|
def test_contributing_proposal_ids_bud(basic_header_objs):
|
|
@@ -356,15 +541,13 @@ def test_contributing_proposal_ids_bud(basic_header_objs):
|
|
|
356
541
|
When: Ingesting the headers with a ContributingProposalIdsBud
|
|
357
542
|
Then: The Bud's petal is the tuple of all input proposal IDs
|
|
358
543
|
"""
|
|
359
|
-
|
|
360
|
-
assert
|
|
544
|
+
bud_obj = ContributingProposalIdsBud()
|
|
545
|
+
assert bud_obj.stem_name == BudName.contributing_proposal_ids.value
|
|
361
546
|
for fo in basic_header_objs:
|
|
362
547
|
key = fo.name
|
|
363
|
-
|
|
548
|
+
bud_obj.update(key, fo)
|
|
364
549
|
|
|
365
|
-
|
|
366
|
-
assert len(petal) == 1
|
|
367
|
-
assert sorted(list(petal[0].value)) == ["proposal_id_1", "proposal_id_2"]
|
|
550
|
+
assert sorted(list(bud_obj.bud.value)) == ["proposal_id_1", "proposal_id_2"]
|
|
368
551
|
|
|
369
552
|
|
|
370
553
|
def test_experiment_id_bud(basic_header_objs):
|
|
@@ -373,15 +556,13 @@ def test_experiment_id_bud(basic_header_objs):
|
|
|
373
556
|
When: Ingesting the headers with a ExperimentIdBud
|
|
374
557
|
Then: The Bud's petal has the correct value
|
|
375
558
|
"""
|
|
376
|
-
|
|
377
|
-
assert
|
|
559
|
+
bud_obj = ExperimentIdBud()
|
|
560
|
+
assert bud_obj.stem_name == BudName.experiment_id.value
|
|
378
561
|
for fo in basic_header_objs:
|
|
379
562
|
key = fo.name
|
|
380
|
-
|
|
563
|
+
bud_obj.update(key, fo)
|
|
381
564
|
|
|
382
|
-
|
|
383
|
-
assert len(petal) == 1
|
|
384
|
-
assert petal[0].value == "experiment_id_1"
|
|
565
|
+
assert bud_obj.bud.value == "experiment_id_1"
|
|
385
566
|
|
|
386
567
|
|
|
387
568
|
def test_contributing_experiment_ids_bud(basic_header_objs):
|
|
@@ -390,15 +571,53 @@ def test_contributing_experiment_ids_bud(basic_header_objs):
|
|
|
390
571
|
When: Ingesting the headers with a ContributingExperimentIdsBud
|
|
391
572
|
Then: The Bud's petal is the tuple of all input experiment IDs
|
|
392
573
|
"""
|
|
393
|
-
|
|
394
|
-
assert
|
|
574
|
+
bud_obj = ContributingExperimentIdsBud()
|
|
575
|
+
assert bud_obj.stem_name == BudName.contributing_experiment_ids.value
|
|
395
576
|
for fo in basic_header_objs:
|
|
396
577
|
key = fo.name
|
|
397
|
-
|
|
578
|
+
bud_obj.update(key, fo)
|
|
398
579
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
580
|
+
assert sorted(list(bud_obj.bud.value)) == ["experiment_id_1", "experiment_id_2"]
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def test_task_contributing_ids_bud(basic_header_objs):
|
|
584
|
+
"""
|
|
585
|
+
Given: A set of headers with experiment ID values for different tasks
|
|
586
|
+
When: Ingesting the headers with a TaskContributingIdsBud for the dark task
|
|
587
|
+
Then: The Bud's petal is just the experiment ID for the dark task
|
|
588
|
+
"""
|
|
589
|
+
bud_obj = TaskContributingIdsBud(
|
|
590
|
+
constant_name=BudName.experiment_id,
|
|
591
|
+
metadata_key=MetadataKey.experiment_id,
|
|
592
|
+
ip_task_types=TaskName.dark,
|
|
593
|
+
)
|
|
594
|
+
assert bud_obj.stem_name == BudName.experiment_id.value
|
|
595
|
+
for fo in basic_header_objs:
|
|
596
|
+
key = fo.name
|
|
597
|
+
bud_obj.update(key, fo)
|
|
598
|
+
|
|
599
|
+
assert sorted(list(bud_obj.bud.value)) == ["experiment_id_2"]
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def test_task_contributing_observing_program_execution_ids_bud(basic_header_objs):
|
|
603
|
+
"""
|
|
604
|
+
Given: A set of headers with observing program execution ID values for different tasks
|
|
605
|
+
When: Ingesting the headers with a TaskContributingObservingProgramExecutionIdsBud for a task type
|
|
606
|
+
Then: The Bud's petal is the observing program execution IDs for the that task type
|
|
607
|
+
"""
|
|
608
|
+
bud_obj = TaskContributingObservingProgramExecutionIdsBud(
|
|
609
|
+
constant_name="NOT_A_REAL_BUD",
|
|
610
|
+
ip_task_types=TaskName.observe,
|
|
611
|
+
)
|
|
612
|
+
assert bud_obj.stem_name == "NOT_A_REAL_BUD"
|
|
613
|
+
for fo in basic_header_objs:
|
|
614
|
+
key = fo.name
|
|
615
|
+
bud_obj.update(key, fo)
|
|
616
|
+
|
|
617
|
+
assert sorted(list(bud_obj.bud.value)) == [
|
|
618
|
+
"observing_program_execution_id_1",
|
|
619
|
+
"observing_program_execution_id_2",
|
|
620
|
+
]
|
|
402
621
|
|
|
403
622
|
|
|
404
623
|
def test_exp_time_flower(basic_header_objs):
|
|
@@ -420,7 +639,7 @@ def test_exp_time_flower(basic_header_objs):
|
|
|
420
639
|
assert petals[1].value == 12.345
|
|
421
640
|
assert petals[1].keys == ["thing2"]
|
|
422
641
|
assert petals[2].value == 100.0
|
|
423
|
-
assert petals[2].keys == ["thing3"]
|
|
642
|
+
assert petals[2].keys == ["thing3", "thing4"]
|
|
424
643
|
|
|
425
644
|
|
|
426
645
|
def test_readout_exp_time_flower(basic_header_objs):
|
|
@@ -442,7 +661,7 @@ def test_readout_exp_time_flower(basic_header_objs):
|
|
|
442
661
|
assert petals[1].value == 10.0
|
|
443
662
|
assert petals[1].keys == ["thing0", "thing1"]
|
|
444
663
|
assert petals[2].value == 11.0
|
|
445
|
-
assert petals[2].keys == ["thing3"]
|
|
664
|
+
assert petals[2].keys == ["thing3", "thing4"]
|
|
446
665
|
|
|
447
666
|
|
|
448
667
|
def test_task_type_flower(task_with_gains_header_objs):
|
|
@@ -493,15 +712,13 @@ def test_obs_ip_start_time_bud(basic_header_objs):
|
|
|
493
712
|
When: Ingesting with a ObsIpStartTimeBud
|
|
494
713
|
Then: The correct value from *only* the observe IP is returned
|
|
495
714
|
"""
|
|
496
|
-
|
|
497
|
-
assert
|
|
715
|
+
bud_obj = ObsIpStartTimeBud()
|
|
716
|
+
assert bud_obj.stem_name == BudName.obs_ip_start_time.value
|
|
498
717
|
for fo in basic_header_objs:
|
|
499
718
|
key = fo.name
|
|
500
|
-
|
|
719
|
+
bud_obj.update(key, fo)
|
|
501
720
|
|
|
502
|
-
|
|
503
|
-
assert len(petals) == 1
|
|
504
|
-
assert petals[0].value == "2023-09-28T10:23.000"
|
|
721
|
+
assert bud_obj.bud.value == "2023-09-28T10:23.000"
|
|
505
722
|
|
|
506
723
|
|
|
507
724
|
def test_fpa_exp_times_bud(basic_header_objs):
|
|
@@ -510,23 +727,19 @@ def test_fpa_exp_times_bud(basic_header_objs):
|
|
|
510
727
|
When: Ingesting with a TaskExposureTimesBud
|
|
511
728
|
Then: All (rounded) exposure times are accounted for in the resulting tuple
|
|
512
729
|
"""
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
assert
|
|
730
|
+
dark_bud_obj = TaskExposureTimesBud(stem_name=BudName.dark_exposure_times, ip_task_types="DARK")
|
|
731
|
+
obs_bud_obj = TaskExposureTimesBud(stem_name="obs_exp_times", ip_task_types="OBSERVE")
|
|
732
|
+
assert dark_bud_obj.stem_name == BudName.dark_exposure_times.value
|
|
516
733
|
for fo in basic_header_objs:
|
|
517
734
|
key = fo.name
|
|
518
|
-
|
|
519
|
-
|
|
735
|
+
dark_bud_obj.update(key, fo)
|
|
736
|
+
obs_bud_obj.update(key, fo)
|
|
520
737
|
|
|
521
|
-
|
|
522
|
-
assert
|
|
523
|
-
assert type(dark_petal[0].value) is tuple
|
|
524
|
-
assert tuple(sorted(dark_petal[0].value)) == (12.345,)
|
|
738
|
+
assert type(dark_bud_obj.bud.value) is tuple
|
|
739
|
+
assert tuple(sorted(dark_bud_obj.bud.value)) == (12.345,)
|
|
525
740
|
|
|
526
|
-
|
|
527
|
-
assert
|
|
528
|
-
assert type(obs_petal[0].value) is tuple
|
|
529
|
-
assert tuple(sorted(obs_petal[0].value)) == (0.0013, 100.0)
|
|
741
|
+
assert type(obs_bud_obj.bud.value) is tuple
|
|
742
|
+
assert tuple(sorted(obs_bud_obj.bud.value)) == (0.0013, 100.0)
|
|
530
743
|
|
|
531
744
|
|
|
532
745
|
def test_readout_exp_times_bud(basic_header_objs):
|
|
@@ -535,35 +748,36 @@ def test_readout_exp_times_bud(basic_header_objs):
|
|
|
535
748
|
When: Ingesting with a TaskReadoutExpTimesBud
|
|
536
749
|
Then: All (rounded) exposure times are accounted for in the resulting tuple
|
|
537
750
|
"""
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
751
|
+
dark_bud_obj = TaskReadoutExpTimesBud(
|
|
752
|
+
stem_name=BudName.dark_exposure_times, ip_task_types="DARK"
|
|
753
|
+
)
|
|
754
|
+
obs_bud_obj = TaskReadoutExpTimesBud(stem_name="obs_exp_times", ip_task_types="OBSERVE")
|
|
755
|
+
assert dark_bud_obj.stem_name == BudName.dark_exposure_times.value
|
|
541
756
|
for fo in basic_header_objs:
|
|
542
757
|
key = fo.name
|
|
543
|
-
|
|
544
|
-
|
|
758
|
+
dark_bud_obj.update(key, fo)
|
|
759
|
+
obs_bud_obj.update(key, fo)
|
|
545
760
|
|
|
546
|
-
|
|
547
|
-
assert
|
|
548
|
-
assert type(dark_petal[0].value) is tuple
|
|
549
|
-
assert tuple(sorted(dark_petal[0].value)) == (1.123457,)
|
|
761
|
+
assert type(dark_bud_obj.bud.value) is tuple
|
|
762
|
+
assert tuple(sorted(dark_bud_obj.bud.value)) == (1.123457,)
|
|
550
763
|
|
|
551
|
-
|
|
552
|
-
assert
|
|
553
|
-
assert type(obs_petal[0].value) is tuple
|
|
554
|
-
assert tuple(sorted(obs_petal[0].value)) == (10.0, 11.0)
|
|
764
|
+
assert type(obs_bud_obj.bud.value) is tuple
|
|
765
|
+
assert tuple(sorted(obs_bud_obj.bud.value)) == (10.0, 11.0)
|
|
555
766
|
|
|
556
767
|
|
|
557
768
|
def test_dsps_bud(basic_header_objs):
|
|
558
|
-
|
|
559
|
-
|
|
769
|
+
"""
|
|
770
|
+
Given: A set of filepaths and associated headers with DSPSREPS keywords
|
|
771
|
+
When: Ingesting with a TotalDspsRepeatsBud
|
|
772
|
+
Then: The total number of DSPS repeates is parsed correctly
|
|
773
|
+
"""
|
|
774
|
+
bud_obj = TotalDspsRepeatsBud()
|
|
775
|
+
assert bud_obj.stem_name == BudName.num_dsps_repeats.value
|
|
560
776
|
for fo in basic_header_objs:
|
|
561
777
|
key = fo.name
|
|
562
|
-
|
|
778
|
+
bud_obj.update(key, fo)
|
|
563
779
|
|
|
564
|
-
|
|
565
|
-
assert len(petal) == 1
|
|
566
|
-
assert petal[0].value == 2
|
|
780
|
+
assert bud_obj.bud.value == 2
|
|
567
781
|
|
|
568
782
|
|
|
569
783
|
def test_dsps_flower(basic_header_objs):
|
|
@@ -592,17 +806,14 @@ def test_average_cadence_bud(basic_header_objs):
|
|
|
592
806
|
When: Ingesting with the AverageCadenceBud
|
|
593
807
|
Then: The correct values are returned
|
|
594
808
|
"""
|
|
595
|
-
|
|
596
|
-
assert
|
|
809
|
+
bud_obj = AverageCadenceBud()
|
|
810
|
+
assert bud_obj.stem_name == BudName.average_cadence.value
|
|
597
811
|
for fo in basic_header_objs:
|
|
598
812
|
key = fo.name
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
petal = list(bud.petals)
|
|
602
|
-
assert len(petal) == 1
|
|
813
|
+
bud_obj.update(key, fo)
|
|
603
814
|
|
|
604
815
|
# Because there are 3 observe frames in `basic_header_objs` spaced 1, and 2 seconds apart.
|
|
605
|
-
assert
|
|
816
|
+
assert bud_obj.bud.value == 1.5
|
|
606
817
|
|
|
607
818
|
|
|
608
819
|
def test_max_cadence_bud(basic_header_objs):
|
|
@@ -611,17 +822,14 @@ def test_max_cadence_bud(basic_header_objs):
|
|
|
611
822
|
When: Ingesting with the MaxCadenceBud
|
|
612
823
|
Then: The correct values are returned
|
|
613
824
|
"""
|
|
614
|
-
|
|
615
|
-
assert
|
|
825
|
+
bud_obj = MaximumCadenceBud()
|
|
826
|
+
assert bud_obj.stem_name == BudName.maximum_cadence.value
|
|
616
827
|
for fo in basic_header_objs:
|
|
617
828
|
key = fo.name
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
petal = list(bud.petals)
|
|
621
|
-
assert len(petal) == 1
|
|
829
|
+
bud_obj.update(key, fo)
|
|
622
830
|
|
|
623
831
|
# Because there are 3 observe frames in `basic_header_objs` spaced 1, and 2 seconds apart.
|
|
624
|
-
assert
|
|
832
|
+
assert bud_obj.bud.value == 2
|
|
625
833
|
|
|
626
834
|
|
|
627
835
|
def test_minimum_cadence_bud(basic_header_objs):
|
|
@@ -630,17 +838,14 @@ def test_minimum_cadence_bud(basic_header_objs):
|
|
|
630
838
|
When: Ingesting with the MinimumCadenceBud
|
|
631
839
|
Then: The correct values are returned
|
|
632
840
|
"""
|
|
633
|
-
|
|
634
|
-
assert
|
|
841
|
+
bud_obj = MinimumCadenceBud()
|
|
842
|
+
assert bud_obj.stem_name == BudName.minimum_cadence.value
|
|
635
843
|
for fo in basic_header_objs:
|
|
636
844
|
key = fo.name
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
petal = list(bud.petals)
|
|
640
|
-
assert len(petal) == 1
|
|
845
|
+
bud_obj.update(key, fo)
|
|
641
846
|
|
|
642
847
|
# Because there are 3 observe frames in `basic_header_objs` spaced 1, and 2 seconds apart.
|
|
643
|
-
assert
|
|
848
|
+
assert bud_obj.bud.value == 1
|
|
644
849
|
|
|
645
850
|
|
|
646
851
|
def test_variance_cadence_bud(basic_header_objs):
|
|
@@ -649,17 +854,29 @@ def test_variance_cadence_bud(basic_header_objs):
|
|
|
649
854
|
When: Ingesting with the VarianceCadenceBud
|
|
650
855
|
Then: The correct values are returned
|
|
651
856
|
"""
|
|
652
|
-
|
|
653
|
-
assert
|
|
857
|
+
bud_obj = VarianceCadenceBud()
|
|
858
|
+
assert bud_obj.stem_name == BudName.variance_cadence.value
|
|
654
859
|
for fo in basic_header_objs:
|
|
655
860
|
key = fo.name
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
petal = list(bud.petals)
|
|
659
|
-
assert len(petal) == 1
|
|
861
|
+
bud_obj.update(key, fo)
|
|
660
862
|
|
|
661
863
|
# Because there are 3 observe frames in `basic_header_objs` spaced 1, and 2 seconds apart.
|
|
662
|
-
assert
|
|
864
|
+
assert bud_obj.bud.value == 0.25
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def test_task_date_begin_bud(basic_header_objs):
|
|
868
|
+
"""
|
|
869
|
+
Given: A set of filepaths and associated headers with time_obs metadata keys
|
|
870
|
+
When: Ingesting with the TaskDateBeginBud
|
|
871
|
+
Then: The correct value is returned
|
|
872
|
+
"""
|
|
873
|
+
bud_obj = TaskDateBeginBud(constant_name=BudName.dark_date_begin, ip_task_types=TaskName.dark)
|
|
874
|
+
assert bud_obj.stem_name == BudName.dark_date_begin.value
|
|
875
|
+
for fo in basic_header_objs:
|
|
876
|
+
key = fo.name
|
|
877
|
+
bud_obj.update(key, fo)
|
|
878
|
+
|
|
879
|
+
assert bud_obj.bud.value == "2022-06-17T22:00:02.000000"
|
|
663
880
|
|
|
664
881
|
|
|
665
882
|
def test_observe_wavelength_bud(basic_header_objs):
|
|
@@ -668,15 +885,13 @@ def test_observe_wavelength_bud(basic_header_objs):
|
|
|
668
885
|
When: Ingesting the headers with the ObserveWavelengthBud
|
|
669
886
|
Then: The petal contains the wavelength header value of the observe frames
|
|
670
887
|
"""
|
|
671
|
-
|
|
672
|
-
assert
|
|
888
|
+
bud_obj = ObserveWavelengthBud()
|
|
889
|
+
assert bud_obj.stem_name == BudName.wavelength.value
|
|
673
890
|
for fo in basic_header_objs:
|
|
674
891
|
key = fo.name
|
|
675
|
-
|
|
892
|
+
bud_obj.update(key, fo)
|
|
676
893
|
|
|
677
|
-
|
|
678
|
-
assert len(petal) == 1
|
|
679
|
-
assert petal[0].value == 666.0
|
|
894
|
+
assert bud_obj.bud.value == 666.0
|
|
680
895
|
|
|
681
896
|
|
|
682
897
|
def test_near_bud(basic_header_objs):
|
|
@@ -685,19 +900,17 @@ def test_near_bud(basic_header_objs):
|
|
|
685
900
|
When: Ingesting headers with a NearBud and asking for the value
|
|
686
901
|
Then: The Bud's value is the average of the header values
|
|
687
902
|
"""
|
|
688
|
-
|
|
903
|
+
bud_obj = NearFloatBud(
|
|
689
904
|
constant_name="near",
|
|
690
905
|
metadata_key="near_thing",
|
|
691
906
|
tolerance=0.5,
|
|
692
907
|
)
|
|
693
|
-
assert
|
|
908
|
+
assert bud_obj.stem_name == "near"
|
|
694
909
|
for fo in basic_header_objs:
|
|
695
910
|
key = fo.name
|
|
696
|
-
|
|
911
|
+
bud_obj.update(key, fo)
|
|
697
912
|
|
|
698
|
-
|
|
699
|
-
assert len(petal) == 1
|
|
700
|
-
assert petal[0].value == 1.23
|
|
913
|
+
assert bud_obj.bud.value == 1.23
|
|
701
914
|
|
|
702
915
|
|
|
703
916
|
def test_task_near_bud(basic_header_objs):
|
|
@@ -706,17 +919,53 @@ def test_task_near_bud(basic_header_objs):
|
|
|
706
919
|
When: Ingesting headers with a TaskNearBud and asking for the value
|
|
707
920
|
Then: The bud's value is the average of the header values of that task type
|
|
708
921
|
"""
|
|
709
|
-
|
|
710
|
-
constant_name="near", metadata_key="near_thing",
|
|
922
|
+
bud_obj = TaskNearFloatBud(
|
|
923
|
+
constant_name="near", metadata_key="near_thing", ip_task_types="observe", tolerance=0.5
|
|
711
924
|
)
|
|
712
|
-
assert
|
|
925
|
+
assert bud_obj.stem_name == "near"
|
|
713
926
|
for fo in basic_header_objs:
|
|
714
927
|
key = fo.name
|
|
715
|
-
|
|
928
|
+
bud_obj.update(key, fo)
|
|
929
|
+
|
|
930
|
+
assert round(bud_obj.bud.value, 3) == 1.227
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
def test_multi_task_near_bud():
|
|
934
|
+
"""
|
|
935
|
+
Given: A set of headers where multiple, but not all, task types have the same values
|
|
936
|
+
When: Ingesting the headers with a `TaskNearBud`
|
|
937
|
+
Then: When multiple tasks have the same value the correct value is returned. When a task has a different value, an
|
|
938
|
+
Error is raised.
|
|
939
|
+
"""
|
|
940
|
+
header_dicts = [
|
|
941
|
+
{"DKIST004": "observe", "near": 3.2},
|
|
942
|
+
{"DKIST004": "dark", "near": 3.11},
|
|
943
|
+
{"DKIST004": "solar", "near": 1e3},
|
|
944
|
+
]
|
|
945
|
+
header_objs = [FitsReader.from_header(h, f"{i}") for i, h in enumerate(header_dicts)]
|
|
946
|
+
|
|
947
|
+
good_bud_obj = TaskNearFloatBud(
|
|
948
|
+
constant_name="near",
|
|
949
|
+
metadata_key="near_thing",
|
|
950
|
+
ip_task_types=["observe", "dark"],
|
|
951
|
+
tolerance=0.1,
|
|
952
|
+
)
|
|
953
|
+
for fo in header_objs:
|
|
954
|
+
good_bud_obj.update(fo.name, fo)
|
|
955
|
+
|
|
956
|
+
assert round(good_bud_obj.bud.value, 0) == 3.0
|
|
957
|
+
|
|
958
|
+
bad_bud_obj = TaskNearFloatBud(
|
|
959
|
+
constant_name="near",
|
|
960
|
+
metadata_key="near_thing",
|
|
961
|
+
ip_task_types=["observe", "solar"],
|
|
962
|
+
tolerance=0.1,
|
|
963
|
+
)
|
|
964
|
+
for fo in header_objs:
|
|
965
|
+
bad_bud_obj.update(fo.name, fo)
|
|
716
966
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
assert round(petal[0].value, 3) == 1.227
|
|
967
|
+
with pytest.raises(ValueError, match="near values are not close enough"):
|
|
968
|
+
_ = bad_bud_obj.bud
|
|
720
969
|
|
|
721
970
|
|
|
722
971
|
def test_near_bud_not_near_inputs(bad_header_objs):
|
|
@@ -725,18 +974,112 @@ def test_near_bud_not_near_inputs(bad_header_objs):
|
|
|
725
974
|
When: Ingesting headers with a NearBud and asking for the value
|
|
726
975
|
Then: An error is raised
|
|
727
976
|
"""
|
|
728
|
-
|
|
977
|
+
bud_obj = NearFloatBud(
|
|
729
978
|
constant_name="near",
|
|
730
979
|
metadata_key="near_thing",
|
|
731
980
|
tolerance=0.5,
|
|
732
981
|
)
|
|
733
|
-
assert
|
|
982
|
+
assert bud_obj.stem_name == "near"
|
|
734
983
|
for fo in bad_header_objs:
|
|
735
984
|
key = fo.name
|
|
736
|
-
|
|
985
|
+
bud_obj.update(key, fo)
|
|
737
986
|
|
|
738
987
|
with pytest.raises(ValueError):
|
|
739
|
-
|
|
988
|
+
_ = bud_obj.bud
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
def test_retarder_name_bud(basic_header_objs, task_with_polcal_header_objs, retarder_name):
|
|
992
|
+
"""
|
|
993
|
+
Given: A set of headers with two values for LVL1STAT: "clear" and another name
|
|
994
|
+
When: Ingesting the headers with RetarderNameBud and asking for the value
|
|
995
|
+
Then: The retarder name is returned
|
|
996
|
+
"""
|
|
997
|
+
bud_obj = RetarderNameBud()
|
|
998
|
+
input_objects = chain(basic_header_objs, task_with_polcal_header_objs)
|
|
999
|
+
for fo in input_objects:
|
|
1000
|
+
key = fo.name
|
|
1001
|
+
bud_obj.update(key, fo)
|
|
1002
|
+
|
|
1003
|
+
assert bud_obj.bud.value == retarder_name
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def test_retarder_name_bud_error(bad_polcal_header_objs):
|
|
1007
|
+
"""
|
|
1008
|
+
Given: A set of headers with "clear" and two other values for LVL1STAT
|
|
1009
|
+
When: Ingesting the headers with RetarderNameBud and asking for the value
|
|
1010
|
+
Then: An error is raised
|
|
1011
|
+
"""
|
|
1012
|
+
bud_obj = RetarderNameBud()
|
|
1013
|
+
for fo in bad_polcal_header_objs:
|
|
1014
|
+
key = fo.name
|
|
1015
|
+
bud_obj.update(key, fo)
|
|
1016
|
+
|
|
1017
|
+
# Crazy regex to handle non-deterministic order of sets.
|
|
1018
|
+
# https://regex101.com/r/zh9iG6/1
|
|
1019
|
+
with pytest.raises(
|
|
1020
|
+
ValueError,
|
|
1021
|
+
match=r"Multiple RETARDER_NAME values found! Values: {'RET(1)?(?(1)|2)', 'RET(?(1)2|1)'}",
|
|
1022
|
+
):
|
|
1023
|
+
_ = bud_obj.bud
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
def test_task_average_bud(basic_header_objs):
|
|
1027
|
+
"""
|
|
1028
|
+
Given: A set of headers with a differently valued header key
|
|
1029
|
+
When: Ingesting headers with an TaskAverageBud and asking for the value
|
|
1030
|
+
Then: The bud's value is the average of the header values of that task type
|
|
1031
|
+
"""
|
|
1032
|
+
bud_obj = TaskAverageBud(
|
|
1033
|
+
constant_name="average", metadata_key="near_thing", ip_task_types="observe"
|
|
1034
|
+
)
|
|
1035
|
+
assert bud_obj.stem_name == "average"
|
|
1036
|
+
for fo in basic_header_objs:
|
|
1037
|
+
key = fo.name
|
|
1038
|
+
bud_obj.update(key, fo)
|
|
1039
|
+
|
|
1040
|
+
assert round(bud_obj.bud.value, 3) == 1.227
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
def test_time_lookup_bud(basic_header_objs):
|
|
1044
|
+
"""
|
|
1045
|
+
Given: A set of headers with two differently valued header keys
|
|
1046
|
+
When: Ingesting headers with a TimeLookupBud and asking for the value
|
|
1047
|
+
Then: The bud's value is a dictionary of one key to sets of the other key as nested tuples
|
|
1048
|
+
"""
|
|
1049
|
+
bud = TimeLookupBud(
|
|
1050
|
+
constant_name="lookup",
|
|
1051
|
+
key_metadata_key=FitsReaderMetadataKey.fpa_exposure_time_ms,
|
|
1052
|
+
value_metadata_key=FitsReaderMetadataKey.num_raw_frames_per_fpa,
|
|
1053
|
+
)
|
|
1054
|
+
assert bud.stem_name == "lookup"
|
|
1055
|
+
for fo in basic_header_objs:
|
|
1056
|
+
key = fo.name
|
|
1057
|
+
bud.update(key, fo)
|
|
1058
|
+
|
|
1059
|
+
assert type(bud.mapping) == collections.defaultdict
|
|
1060
|
+
assert bud.mapping == {0.0013: {3}, 12.345: {1}, 100.0: {4, 5}}
|
|
1061
|
+
assert bud.bud.value == {0.0013: [3], 12.345: [1], 100.0: [4, 5]}
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
def test_task_time_lookup_bud(basic_header_objs):
|
|
1065
|
+
"""
|
|
1066
|
+
Given: A set of headers with two differently valued header keys
|
|
1067
|
+
When: Ingesting headers with a TaskTimeLookupBud and asking for the value
|
|
1068
|
+
Then: The bud's value is a dictionary of one key to sets of the other key as nested tuples
|
|
1069
|
+
"""
|
|
1070
|
+
bud = TaskTimeLookupBud(
|
|
1071
|
+
constant_name="task_lookup",
|
|
1072
|
+
key_metadata_key=FitsReaderMetadataKey.fpa_exposure_time_ms,
|
|
1073
|
+
value_metadata_key=FitsReaderMetadataKey.num_raw_frames_per_fpa,
|
|
1074
|
+
ip_task_types="dark",
|
|
1075
|
+
)
|
|
1076
|
+
assert bud.stem_name == "task_lookup"
|
|
1077
|
+
for fo in basic_header_objs:
|
|
1078
|
+
key = fo.name
|
|
1079
|
+
bud.update(key, fo)
|
|
1080
|
+
|
|
1081
|
+
assert bud.mapping == {12.345: {1}}
|
|
1082
|
+
assert bud.bud.value == {12.345: [1]}
|
|
740
1083
|
|
|
741
1084
|
|
|
742
|
-
# TODO: test new
|
|
1085
|
+
# TODO: test new stem types that have been added to parse_l0_input_data
|