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,15 +1,18 @@
|
|
|
1
1
|
"""Time parser."""
|
|
2
|
+
|
|
2
3
|
from datetime import datetime
|
|
3
4
|
from datetime import timezone
|
|
5
|
+
from enum import StrEnum
|
|
4
6
|
from typing import Callable
|
|
5
|
-
from typing import Hashable
|
|
6
7
|
from typing import Type
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
|
|
10
11
|
from dkist_processing_common.models.constants import BudName
|
|
12
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
13
|
+
from dkist_processing_common.models.flower_pot import ListStem
|
|
14
|
+
from dkist_processing_common.models.flower_pot import SetStem
|
|
11
15
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
12
|
-
from dkist_processing_common.models.flower_pot import Stem
|
|
13
16
|
from dkist_processing_common.models.tags import EXP_TIME_ROUND_DIGITS
|
|
14
17
|
from dkist_processing_common.models.tags import StemName
|
|
15
18
|
from dkist_processing_common.models.task_name import TaskName
|
|
@@ -19,7 +22,6 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
|
19
22
|
)
|
|
20
23
|
from dkist_processing_common.parsers.task import passthrough_header_ip_task
|
|
21
24
|
from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
|
|
22
|
-
from dkist_processing_common.parsers.unique_bud import UniqueBud
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
class ObsIpStartTimeBud(TaskUniqueBud):
|
|
@@ -27,21 +29,56 @@ class ObsIpStartTimeBud(TaskUniqueBud):
|
|
|
27
29
|
|
|
28
30
|
def __init__(self):
|
|
29
31
|
super().__init__(
|
|
30
|
-
constant_name=BudName.obs_ip_start_time
|
|
31
|
-
metadata_key=
|
|
32
|
-
|
|
32
|
+
constant_name=BudName.obs_ip_start_time,
|
|
33
|
+
metadata_key=MetadataKey.ip_start_time,
|
|
34
|
+
ip_task_types=TaskName.observe,
|
|
33
35
|
)
|
|
34
36
|
|
|
35
37
|
|
|
36
|
-
class
|
|
37
|
-
"""
|
|
38
|
+
class TaskDatetimeBudBase(ListStem):
|
|
39
|
+
"""
|
|
40
|
+
Base class for making datetime-related buds.
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
Returns a tuple of sorted values converted from datetimes to unix seconds.
|
|
43
|
+
|
|
44
|
+
Complicated parsing of the header into a task type can be achieved by passing in a different
|
|
45
|
+
header task parsing function.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
stem_name
|
|
50
|
+
The name for the constant to be defined
|
|
51
|
+
|
|
52
|
+
metadata_key
|
|
53
|
+
The metadata key associated with the constant
|
|
54
|
+
|
|
55
|
+
ip_task_types
|
|
56
|
+
Only consider objects whose parsed header IP task type matches a string in this list
|
|
57
|
+
|
|
58
|
+
header_type_parsing_func
|
|
59
|
+
The function used to convert a header into an IP task type
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
stem_name: str,
|
|
65
|
+
metadata_key: str | StrEnum,
|
|
66
|
+
ip_task_types: str | list[str],
|
|
67
|
+
task_type_parsing_function: Callable = passthrough_header_ip_task,
|
|
68
|
+
):
|
|
69
|
+
super().__init__(stem_name=stem_name)
|
|
70
|
+
|
|
71
|
+
if isinstance(metadata_key, StrEnum):
|
|
72
|
+
metadata_key = metadata_key.name
|
|
73
|
+
self.metadata_key = metadata_key
|
|
74
|
+
if isinstance(ip_task_types, str):
|
|
75
|
+
ip_task_types = [ip_task_types]
|
|
76
|
+
self.ip_task_types = [task.casefold() for task in ip_task_types]
|
|
77
|
+
self.header_parsing_function = task_type_parsing_function
|
|
41
78
|
|
|
42
79
|
def setter(self, fits_obj: L0FitsAccess) -> float | Type[SpilledDirt]:
|
|
43
80
|
"""
|
|
44
|
-
|
|
81
|
+
Store the metadata key datetime value as unix seconds if the task type is in the desired types.
|
|
45
82
|
|
|
46
83
|
Parameters
|
|
47
84
|
----------
|
|
@@ -49,106 +86,141 @@ class CadenceBudBase(UniqueBud):
|
|
|
49
86
|
The input fits object
|
|
50
87
|
Returns
|
|
51
88
|
-------
|
|
52
|
-
The
|
|
89
|
+
The datetime in seconds
|
|
53
90
|
"""
|
|
54
|
-
|
|
91
|
+
task = self.header_parsing_function(fits_obj)
|
|
92
|
+
|
|
93
|
+
if task.casefold() in self.ip_task_types:
|
|
55
94
|
return (
|
|
56
95
|
datetime.fromisoformat(getattr(fits_obj, self.metadata_key))
|
|
57
96
|
.replace(tzinfo=timezone.utc)
|
|
58
97
|
.timestamp()
|
|
59
98
|
)
|
|
99
|
+
|
|
60
100
|
return SpilledDirt
|
|
61
101
|
|
|
102
|
+
def getter(self) -> tuple[float, ...]:
|
|
103
|
+
"""
|
|
104
|
+
Return a tuple of sorted times in unix seconds.
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
A tuple that is sorted times in unix seconds
|
|
109
|
+
"""
|
|
110
|
+
return tuple(sorted(self.value_list))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class CadenceBudBase(TaskDatetimeBudBase):
|
|
114
|
+
"""Base class for all Cadence Buds."""
|
|
115
|
+
|
|
116
|
+
def __init__(self, constant_name: str):
|
|
117
|
+
super().__init__(
|
|
118
|
+
stem_name=constant_name,
|
|
119
|
+
metadata_key=MetadataKey.time_obs,
|
|
120
|
+
ip_task_types=TaskName.observe,
|
|
121
|
+
)
|
|
122
|
+
|
|
62
123
|
|
|
63
124
|
class AverageCadenceBud(CadenceBudBase):
|
|
64
125
|
"""Class for the average cadence Bud."""
|
|
65
126
|
|
|
66
127
|
def __init__(self):
|
|
67
|
-
super().__init__(constant_name=BudName.average_cadence
|
|
128
|
+
super().__init__(constant_name=BudName.average_cadence)
|
|
68
129
|
|
|
69
|
-
def getter(self
|
|
130
|
+
def getter(self) -> np.float64:
|
|
70
131
|
"""
|
|
71
132
|
Return the mean cadence between frames.
|
|
72
133
|
|
|
73
|
-
Parameters
|
|
74
|
-
----------
|
|
75
|
-
key
|
|
76
|
-
The input key
|
|
77
|
-
|
|
78
134
|
Returns
|
|
79
135
|
-------
|
|
80
136
|
The mean value of the cadences of the input frames
|
|
81
137
|
"""
|
|
82
|
-
return np.mean(np.diff(
|
|
138
|
+
return np.mean(np.diff(super().getter()))
|
|
83
139
|
|
|
84
140
|
|
|
85
141
|
class MaximumCadenceBud(CadenceBudBase):
|
|
86
142
|
"""Class for the maximum cadence bud."""
|
|
87
143
|
|
|
88
144
|
def __init__(self):
|
|
89
|
-
super().__init__(constant_name=BudName.maximum_cadence
|
|
145
|
+
super().__init__(constant_name=BudName.maximum_cadence)
|
|
90
146
|
|
|
91
|
-
def getter(self
|
|
147
|
+
def getter(self) -> np.float64:
|
|
92
148
|
"""
|
|
93
149
|
Return the maximum cadence between frames.
|
|
94
150
|
|
|
95
|
-
Parameters
|
|
96
|
-
----------
|
|
97
|
-
key
|
|
98
|
-
The input key
|
|
99
|
-
|
|
100
151
|
Returns
|
|
101
152
|
-------
|
|
102
153
|
The maximum cadence between frames
|
|
103
154
|
"""
|
|
104
|
-
return np.max(np.diff(
|
|
155
|
+
return np.max(np.diff(super().getter()))
|
|
105
156
|
|
|
106
157
|
|
|
107
158
|
class MinimumCadenceBud(CadenceBudBase):
|
|
108
159
|
"""Class for the minimum cadence bud."""
|
|
109
160
|
|
|
110
161
|
def __init__(self):
|
|
111
|
-
super().__init__(constant_name=BudName.minimum_cadence
|
|
162
|
+
super().__init__(constant_name=BudName.minimum_cadence)
|
|
112
163
|
|
|
113
|
-
def getter(self
|
|
164
|
+
def getter(self) -> np.float64:
|
|
114
165
|
"""
|
|
115
166
|
Return the minimum cadence between frames.
|
|
116
167
|
|
|
117
|
-
Parameters
|
|
118
|
-
----------
|
|
119
|
-
key
|
|
120
|
-
The input key
|
|
121
|
-
|
|
122
168
|
Returns
|
|
123
169
|
-------
|
|
124
170
|
The minimum cadence between frames
|
|
125
171
|
"""
|
|
126
|
-
return np.min(np.diff(
|
|
172
|
+
return np.min(np.diff(super().getter()))
|
|
127
173
|
|
|
128
174
|
|
|
129
175
|
class VarianceCadenceBud(CadenceBudBase):
|
|
130
176
|
"""Class for the variance cadence Bud."""
|
|
131
177
|
|
|
132
178
|
def __init__(self):
|
|
133
|
-
super().__init__(constant_name=BudName.variance_cadence
|
|
179
|
+
super().__init__(constant_name=BudName.variance_cadence)
|
|
134
180
|
|
|
135
|
-
def getter(self
|
|
181
|
+
def getter(self) -> np.float64:
|
|
136
182
|
"""
|
|
137
183
|
Return the cadence variance between frames.
|
|
138
184
|
|
|
139
|
-
Parameters
|
|
140
|
-
----------
|
|
141
|
-
key
|
|
142
|
-
The input key
|
|
143
185
|
Returns
|
|
144
186
|
-------
|
|
145
187
|
Return the variance of the cadences over the input frames
|
|
146
188
|
"""
|
|
147
|
-
return np.var(np.diff(
|
|
189
|
+
return np.var(np.diff(super().getter()))
|
|
148
190
|
|
|
149
191
|
|
|
150
|
-
class
|
|
151
|
-
"""
|
|
192
|
+
class TaskDateBeginBud(TaskDatetimeBudBase):
|
|
193
|
+
"""Class for the date begin task Bud."""
|
|
194
|
+
|
|
195
|
+
def __init__(
|
|
196
|
+
self,
|
|
197
|
+
constant_name: str,
|
|
198
|
+
ip_task_types: str | list[str],
|
|
199
|
+
task_type_parsing_function: Callable = passthrough_header_ip_task,
|
|
200
|
+
):
|
|
201
|
+
super().__init__(
|
|
202
|
+
stem_name=constant_name,
|
|
203
|
+
metadata_key=MetadataKey.time_obs,
|
|
204
|
+
ip_task_types=ip_task_types,
|
|
205
|
+
task_type_parsing_function=task_type_parsing_function,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def getter(self) -> str:
|
|
209
|
+
"""
|
|
210
|
+
Return the earliest date begin for the ip task type converted from unix seconds to datetime string.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
Return the minimum date begin as a datetime string
|
|
215
|
+
"""
|
|
216
|
+
# super().getter() returns a sorted list
|
|
217
|
+
min_time = super().getter()[0]
|
|
218
|
+
min_time_dt = datetime.fromtimestamp(min_time, tz=timezone.utc)
|
|
219
|
+
return min_time_dt.strftime("%Y-%m-%dT%H:%M:%S.%f")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class RoundTimeFlowerBase(SingleValueSingleKeyFlower):
|
|
223
|
+
"""Base flower for SingleValueSingleKeyFlowers that need to round their values to avoid value jitter."""
|
|
152
224
|
|
|
153
225
|
def setter(self, fits_obj: L0FitsAccess):
|
|
154
226
|
"""
|
|
@@ -166,92 +238,126 @@ class TimeFlowerBase(SingleValueSingleKeyFlower):
|
|
|
166
238
|
return round(raw_value, EXP_TIME_ROUND_DIGITS)
|
|
167
239
|
|
|
168
240
|
|
|
169
|
-
class ExposureTimeFlower(
|
|
241
|
+
class ExposureTimeFlower(RoundTimeFlowerBase):
|
|
170
242
|
"""For tagging the frame FPA exposure time."""
|
|
171
243
|
|
|
172
244
|
def __init__(self):
|
|
173
245
|
super().__init__(
|
|
174
|
-
tag_stem_name=StemName.exposure_time
|
|
246
|
+
tag_stem_name=StemName.exposure_time, metadata_key=MetadataKey.fpa_exposure_time_ms
|
|
175
247
|
)
|
|
176
248
|
|
|
177
249
|
|
|
178
|
-
class ReadoutExpTimeFlower(
|
|
250
|
+
class ReadoutExpTimeFlower(RoundTimeFlowerBase):
|
|
179
251
|
"""For tagging the exposure time of each readout that contributes to an FPA."""
|
|
180
252
|
|
|
181
253
|
def __init__(self):
|
|
182
254
|
super().__init__(
|
|
183
|
-
tag_stem_name=StemName.readout_exp_time
|
|
184
|
-
metadata_key=
|
|
255
|
+
tag_stem_name=StemName.readout_exp_time,
|
|
256
|
+
metadata_key=MetadataKey.sensor_readout_exposure_time_ms,
|
|
185
257
|
)
|
|
186
258
|
|
|
187
259
|
|
|
188
|
-
class
|
|
260
|
+
class TaskRoundTimeBudBase(SetStem):
|
|
189
261
|
"""
|
|
190
|
-
Base class for making
|
|
262
|
+
Base class for making buds that need a set of rounded times for computing for specific task types.
|
|
191
263
|
|
|
192
|
-
|
|
264
|
+
Metadata key values are already floats. Returns tuple of sorted unique rounded values.
|
|
193
265
|
|
|
194
266
|
Complicated parsing of the header into a task type can be achieved by passing in a different
|
|
195
267
|
header task parsing function.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
stem_name
|
|
272
|
+
The name for the constant to be defined
|
|
273
|
+
|
|
274
|
+
metadata_key
|
|
275
|
+
The metadata key associated with the constant
|
|
276
|
+
|
|
277
|
+
ip_task_types
|
|
278
|
+
Only consider objects whose parsed header IP task type matches a string in this list
|
|
279
|
+
|
|
280
|
+
header_task_parsing_func
|
|
281
|
+
The function used to convert a header into an IP task type
|
|
196
282
|
"""
|
|
197
283
|
|
|
198
284
|
def __init__(
|
|
199
285
|
self,
|
|
200
286
|
stem_name: str,
|
|
201
|
-
metadata_key: str,
|
|
202
|
-
|
|
287
|
+
metadata_key: str | StrEnum,
|
|
288
|
+
ip_task_types: str | list[str],
|
|
203
289
|
header_task_parsing_func: Callable = passthrough_header_ip_task,
|
|
204
290
|
):
|
|
205
291
|
super().__init__(stem_name=stem_name)
|
|
292
|
+
|
|
293
|
+
if isinstance(metadata_key, StrEnum):
|
|
294
|
+
metadata_key = metadata_key.name
|
|
206
295
|
self.metadata_key = metadata_key
|
|
207
|
-
|
|
296
|
+
if isinstance(ip_task_types, str):
|
|
297
|
+
ip_task_types = [ip_task_types]
|
|
298
|
+
self.ip_task_types = [task.casefold() for task in ip_task_types]
|
|
208
299
|
self.header_parsing_function = header_task_parsing_func
|
|
209
300
|
|
|
210
|
-
def setter(self, fits_obj: L0FitsAccess):
|
|
211
|
-
"""
|
|
301
|
+
def setter(self, fits_obj: L0FitsAccess) -> float | Type[SpilledDirt]:
|
|
302
|
+
"""
|
|
303
|
+
Store the metadata key value if the parsed task type is in the desired types.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
fits_obj
|
|
308
|
+
The input fits object
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
The rounded time
|
|
312
|
+
"""
|
|
212
313
|
task = self.header_parsing_function(fits_obj)
|
|
213
314
|
|
|
214
|
-
if task.casefold()
|
|
315
|
+
if task.casefold() in self.ip_task_types:
|
|
215
316
|
raw_value = getattr(fits_obj, self.metadata_key)
|
|
216
317
|
return round(raw_value, EXP_TIME_ROUND_DIGITS)
|
|
217
318
|
|
|
218
319
|
return SpilledDirt
|
|
219
320
|
|
|
220
|
-
def getter(self
|
|
221
|
-
"""
|
|
222
|
-
|
|
223
|
-
|
|
321
|
+
def getter(self) -> tuple[float, ...]:
|
|
322
|
+
"""
|
|
323
|
+
Return a tuple of the sorted unique values found.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
A tuple that is the sorted set of unique times
|
|
328
|
+
"""
|
|
329
|
+
return tuple(sorted(self.value_set))
|
|
224
330
|
|
|
225
331
|
|
|
226
|
-
class TaskExposureTimesBud(
|
|
332
|
+
class TaskExposureTimesBud(TaskRoundTimeBudBase):
|
|
227
333
|
"""Produce a tuple of all FPA exposure times present in the dataset for a specific ip task type."""
|
|
228
334
|
|
|
229
335
|
def __init__(
|
|
230
336
|
self,
|
|
231
337
|
stem_name: str,
|
|
232
|
-
|
|
338
|
+
ip_task_types: str | list[str],
|
|
233
339
|
header_task_parsing_func: Callable = passthrough_header_ip_task,
|
|
234
340
|
):
|
|
235
341
|
super().__init__(
|
|
236
342
|
stem_name=stem_name,
|
|
237
|
-
metadata_key=
|
|
238
|
-
|
|
343
|
+
metadata_key=MetadataKey.fpa_exposure_time_ms,
|
|
344
|
+
ip_task_types=ip_task_types,
|
|
239
345
|
header_task_parsing_func=header_task_parsing_func,
|
|
240
346
|
)
|
|
241
347
|
|
|
242
348
|
|
|
243
|
-
class TaskReadoutExpTimesBud(
|
|
349
|
+
class TaskReadoutExpTimesBud(TaskRoundTimeBudBase):
|
|
244
350
|
"""Produce a tuple of all sensor readout exposure times present in the dataset for a specific task type."""
|
|
245
351
|
|
|
246
352
|
def __init__(
|
|
247
353
|
self,
|
|
248
354
|
stem_name: str,
|
|
249
|
-
|
|
355
|
+
ip_task_types: str | list[str],
|
|
250
356
|
header_task_parsing_func: Callable = passthrough_header_ip_task,
|
|
251
357
|
):
|
|
252
358
|
super().__init__(
|
|
253
359
|
stem_name=stem_name,
|
|
254
|
-
metadata_key=
|
|
255
|
-
|
|
360
|
+
metadata_key=MetadataKey.sensor_readout_exposure_time_ms,
|
|
361
|
+
ip_task_types=ip_task_types,
|
|
256
362
|
header_task_parsing_func=header_task_parsing_func,
|
|
257
363
|
)
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"""Pre-made flower that reads a single header key from all files and raises a ValueError if it is not unique."""
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
2
4
|
from typing import Callable
|
|
3
5
|
|
|
6
|
+
from dkist_processing_common.models.flower_pot import SetStem
|
|
4
7
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
5
|
-
from dkist_processing_common.models.flower_pot import Stem
|
|
6
8
|
from dkist_processing_common.parsers.l0_fits_access import L0FitsAccess
|
|
7
9
|
from dkist_processing_common.parsers.task import passthrough_header_ip_task
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
class UniqueBud(
|
|
12
|
+
class UniqueBud(SetStem):
|
|
11
13
|
"""
|
|
12
|
-
Pre-made
|
|
14
|
+
Pre-made `SetStem` that reads a single header key from all files and raises a ValueError if it is not unique.
|
|
13
15
|
|
|
14
16
|
Parameters
|
|
15
17
|
----------
|
|
@@ -23,9 +25,11 @@ class UniqueBud(Stem):
|
|
|
23
25
|
def __init__(
|
|
24
26
|
self,
|
|
25
27
|
constant_name: str,
|
|
26
|
-
metadata_key: str,
|
|
28
|
+
metadata_key: str | StrEnum,
|
|
27
29
|
):
|
|
28
30
|
super().__init__(stem_name=constant_name)
|
|
31
|
+
if isinstance(metadata_key, StrEnum):
|
|
32
|
+
metadata_key = metadata_key.name
|
|
29
33
|
self.metadata_key = metadata_key
|
|
30
34
|
|
|
31
35
|
def setter(self, fits_obj: L0FitsAccess):
|
|
@@ -42,29 +46,22 @@ class UniqueBud(Stem):
|
|
|
42
46
|
"""
|
|
43
47
|
return getattr(fits_obj, self.metadata_key)
|
|
44
48
|
|
|
45
|
-
def getter(self
|
|
49
|
+
def getter(self):
|
|
46
50
|
"""
|
|
47
51
|
Get the value for this key and raise an error if it is not unique.
|
|
48
52
|
|
|
49
|
-
Parameters
|
|
50
|
-
----------
|
|
51
|
-
key
|
|
52
|
-
The input key
|
|
53
53
|
Returns
|
|
54
54
|
-------
|
|
55
55
|
The value associated with this input key
|
|
56
56
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
f"Multiple {self.stem_name} values found for key {key}. Values: {value_set}"
|
|
61
|
-
)
|
|
62
|
-
return value_set.pop()
|
|
57
|
+
if len(self.value_set) > 1:
|
|
58
|
+
raise ValueError(f"Multiple {self.stem_name} values found! Values: {self.value_set}")
|
|
59
|
+
return self.value_set.pop()
|
|
63
60
|
|
|
64
61
|
|
|
65
62
|
class TaskUniqueBud(UniqueBud):
|
|
66
63
|
"""
|
|
67
|
-
Subclass of `UniqueBud` that only considers objects that have
|
|
64
|
+
Subclass of `UniqueBud` that only considers objects that have specific task types.
|
|
68
65
|
|
|
69
66
|
Parameters
|
|
70
67
|
----------
|
|
@@ -74,8 +71,8 @@ class TaskUniqueBud(UniqueBud):
|
|
|
74
71
|
metadata_key
|
|
75
72
|
The metadata key associated with the constant
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
Only consider objects whose parsed header IP task type matches this
|
|
74
|
+
ip_task_types
|
|
75
|
+
Only consider objects whose parsed header IP task type matches a string in this list
|
|
79
76
|
|
|
80
77
|
task_type_parsing_function
|
|
81
78
|
The function used to convert a header into an IP task type
|
|
@@ -84,20 +81,22 @@ class TaskUniqueBud(UniqueBud):
|
|
|
84
81
|
def __init__(
|
|
85
82
|
self,
|
|
86
83
|
constant_name: str,
|
|
87
|
-
metadata_key: str,
|
|
88
|
-
|
|
84
|
+
metadata_key: str | StrEnum,
|
|
85
|
+
ip_task_types: str | list[str],
|
|
89
86
|
task_type_parsing_function: Callable = passthrough_header_ip_task,
|
|
90
87
|
):
|
|
91
88
|
super().__init__(constant_name=constant_name, metadata_key=metadata_key)
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
if isinstance(ip_task_types, str):
|
|
91
|
+
ip_task_types = [ip_task_types]
|
|
92
|
+
self.ip_task_types = [task.casefold() for task in ip_task_types]
|
|
94
93
|
self.parsing_function = task_type_parsing_function
|
|
95
94
|
|
|
96
95
|
def setter(self, fits_obj: L0FitsAccess):
|
|
97
96
|
"""Ingest an object only if its parsed IP task type matches what's desired."""
|
|
98
97
|
task = self.parsing_function(fits_obj)
|
|
99
98
|
|
|
100
|
-
if task.casefold()
|
|
99
|
+
if task.casefold() in self.ip_task_types:
|
|
101
100
|
return super().setter(fits_obj)
|
|
102
101
|
|
|
103
102
|
return SpilledDirt
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Bud to get the wavelength of observe frames."""
|
|
2
|
+
|
|
2
3
|
from dkist_processing_common.models.constants import BudName
|
|
4
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
3
5
|
from dkist_processing_common.models.task_name import TaskName
|
|
4
6
|
from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
|
|
5
7
|
|
|
@@ -9,7 +11,7 @@ class ObserveWavelengthBud(TaskUniqueBud):
|
|
|
9
11
|
|
|
10
12
|
def __init__(self):
|
|
11
13
|
super().__init__(
|
|
12
|
-
constant_name=BudName.wavelength
|
|
13
|
-
metadata_key=
|
|
14
|
-
|
|
14
|
+
constant_name=BudName.wavelength,
|
|
15
|
+
metadata_key=MetadataKey.wavelength,
|
|
16
|
+
ip_task_types=TaskName.observe,
|
|
15
17
|
)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Common tasks and bases."""
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from dkist_processing_common.tasks.base import * # isort: skip
|
|
3
4
|
from dkist_processing_common.tasks.assemble_movie import *
|
|
4
5
|
from dkist_processing_common.tasks.l1_output_data import *
|
|
5
6
|
from dkist_processing_common.tasks.parse_l0_input_data import *
|
|
6
7
|
from dkist_processing_common.tasks.quality_metrics import *
|
|
7
8
|
from dkist_processing_common.tasks.teardown import *
|
|
8
9
|
from dkist_processing_common.tasks.transfer_input_data import *
|
|
9
|
-
from dkist_processing_common.tasks.write_l1 import *
|
|
10
10
|
from dkist_processing_common.tasks.trial_catalog import *
|
|
11
11
|
from dkist_processing_common.tasks.trial_output_data import *
|
|
12
|
+
from dkist_processing_common.tasks.write_l1 import *
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Task(s) for assembling a browse movie."""
|
|
2
|
+
|
|
2
3
|
import logging
|
|
3
4
|
from abc import ABC
|
|
4
5
|
from abc import abstractmethod
|
|
6
|
+
from importlib.resources import files
|
|
5
7
|
from typing import Literal
|
|
6
8
|
|
|
7
9
|
import numpy as np
|
|
8
|
-
import pkg_resources
|
|
9
10
|
from matplotlib import colormaps
|
|
10
11
|
from moviepy import VideoClip
|
|
11
12
|
from PIL import Image
|
|
@@ -66,7 +67,7 @@ class AssembleMovie(WorkflowTaskBase, ABC):
|
|
|
66
67
|
MINIMUM_DURATION = 10 # seconds
|
|
67
68
|
MAXIMUM_DURATION = 60 # seconds
|
|
68
69
|
FPS = 15
|
|
69
|
-
FONT_FILE =
|
|
70
|
+
FONT_FILE = files("dkist_processing_common").joinpath("fonts/Lato-Regular.ttf")
|
|
70
71
|
TEXT_MARGIN_PX = 5
|
|
71
72
|
MPL_COLOR_MAP = "viridis"
|
|
72
73
|
|
|
@@ -162,7 +163,7 @@ class AssembleMovie(WorkflowTaskBase, ABC):
|
|
|
162
163
|
relative_movie_path = f"{self.constants.dataset_id}_browse_movie.mp4"
|
|
163
164
|
absolute_movie_path = str(self.scratch.absolute_path(relative_movie_path))
|
|
164
165
|
|
|
165
|
-
with self.
|
|
166
|
+
with self.telemetry_span("Assembling movie frames"):
|
|
166
167
|
clip.write_videofile(absolute_movie_path, fps=self.FPS, codec="libx264", audio=False)
|
|
167
168
|
|
|
168
169
|
self.tag(path=absolute_movie_path, tags=[Tag.movie(), Tag.output()])
|