dkist-processing-common 11.4.0rc1__py3-none-any.whl → 11.5.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. dkist_processing_common/models/constants.py +42 -28
  2. dkist_processing_common/models/fits_access.py +56 -0
  3. dkist_processing_common/models/tags.py +2 -1
  4. dkist_processing_common/models/task_name.py +2 -2
  5. dkist_processing_common/parsers/cs_step.py +2 -2
  6. dkist_processing_common/parsers/dsps_repeat.py +5 -4
  7. dkist_processing_common/parsers/experiment_id_bud.py +5 -2
  8. dkist_processing_common/parsers/id_bud.py +6 -3
  9. dkist_processing_common/parsers/l0_fits_access.py +4 -3
  10. dkist_processing_common/parsers/l1_fits_access.py +22 -21
  11. dkist_processing_common/parsers/near_bud.py +5 -2
  12. dkist_processing_common/parsers/proposal_id_bud.py +3 -2
  13. dkist_processing_common/parsers/retarder.py +4 -3
  14. dkist_processing_common/parsers/single_value_single_key_flower.py +6 -1
  15. dkist_processing_common/parsers/task.py +7 -6
  16. dkist_processing_common/parsers/time.py +19 -15
  17. dkist_processing_common/parsers/unique_bud.py +5 -2
  18. dkist_processing_common/parsers/wavelength.py +4 -3
  19. dkist_processing_common/tasks/parse_l0_input_data.py +5 -3
  20. dkist_processing_common/tests/test_assemble_movie.py +0 -1
  21. dkist_processing_common/tests/test_constants.py +15 -0
  22. dkist_processing_common/tests/test_fits_access.py +62 -7
  23. dkist_processing_common/tests/test_parse_l0_input_data.py +22 -24
  24. dkist_processing_common/tests/test_stems.py +30 -21
  25. dkist_processing_common/tests/test_task_parsing.py +17 -7
  26. {dkist_processing_common-11.4.0rc1.dist-info → dkist_processing_common-11.5.0rc1.dist-info}/METADATA +1 -1
  27. {dkist_processing_common-11.4.0rc1.dist-info → dkist_processing_common-11.5.0rc1.dist-info}/RECORD +29 -29
  28. {dkist_processing_common-11.4.0rc1.dist-info → dkist_processing_common-11.5.0rc1.dist-info}/WHEEL +0 -0
  29. {dkist_processing_common-11.4.0rc1.dist-info → dkist_processing_common-11.5.0rc1.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ Contains names of database entries and Base class for an object that simplifies
5
5
  accessing the database (tab completion, etc.)
6
6
  """
7
7
 
8
- from enum import Enum
8
+ from enum import StrEnum
9
9
  from string import ascii_uppercase
10
10
 
11
11
  from sqids import Sqids
@@ -13,13 +13,13 @@ from sqids import Sqids
13
13
  from dkist_processing_common._util.constants import ConstantsDb
14
14
 
15
15
 
16
- class BudName(str, Enum):
16
+ class BudName(StrEnum):
17
17
  """Controlled list of names for constant stems (buds)."""
18
18
 
19
- recipe_run_id = "RECIPE_RUN_ID"
20
19
  instrument = "INSTRUMENT"
21
20
  num_cs_steps = "NUM_CS_STEPS"
22
21
  num_modstates = "NUM_MODSTATES"
22
+ retarder_name = "RETARDER_NAME"
23
23
  proposal_id = "PROPOSAL_ID"
24
24
  contributing_proposal_ids = "CONTRIBUTING_PROPOSAL_IDS"
25
25
  experiment_id = "EXPERIMENT_ID"
@@ -33,7 +33,6 @@ class BudName(str, Enum):
33
33
  dark_exposure_times = "DARK_EXPOSURE_TIMES"
34
34
  dark_readout_exp_times = "DARK_READOUT_EXP_TIMES"
35
35
  wavelength = "WAVELENGTH"
36
- retarder_name = "RETARDER_NAME"
37
36
 
38
37
 
39
38
  class ConstantsBase:
@@ -63,7 +62,7 @@ class ConstantsBase:
63
62
  Parameters
64
63
  ----------
65
64
  recipe_run_id
66
- Thew recipe_run_id
65
+ The recipe_run_id
67
66
  task_name
68
67
  The task_name
69
68
  """
@@ -91,6 +90,31 @@ class ConstantsBase:
91
90
  """Define the dataset_id constant."""
92
91
  return Sqids(min_length=6, alphabet=ascii_uppercase).encode([self._recipe_run_id])
93
92
 
93
+ @property
94
+ def stokes_params(self) -> [str]:
95
+ """Return the list of stokes parameter names."""
96
+ return ["I", "Q", "U", "V"]
97
+
98
+ @property
99
+ def instrument(self) -> str:
100
+ """Get the instrument name."""
101
+ return self._db_dict[BudName.instrument]
102
+
103
+ @property
104
+ def num_cs_steps(self):
105
+ """Get the number of calibration sequence steps."""
106
+ return self._db_dict[BudName.num_cs_steps]
107
+
108
+ @property
109
+ def num_modstates(self):
110
+ """Get the number of modulation states."""
111
+ return self._db_dict[BudName.num_modstates]
112
+
113
+ @property
114
+ def retarder_name(self):
115
+ """Get the retarder name."""
116
+ return self._db_dict[BudName.retarder_name]
117
+
94
118
  @property
95
119
  def proposal_id(self) -> str:
96
120
  """Get the proposal_id constant."""
@@ -105,14 +129,22 @@ class ConstantsBase:
105
129
  return proposal_ids
106
130
 
107
131
  @property
108
- def instrument(self) -> str:
109
- """Get the instrument name."""
110
- return self._db_dict[BudName.instrument]
132
+ def experiment_id(self) -> str:
133
+ """Get the experiment_id constant."""
134
+ return self._db_dict[BudName.experiment_id]
135
+
136
+ @property
137
+ def contributing_experiment_ids(self) -> [str]:
138
+ """Return the list of contributing experiment IDs."""
139
+ experiment_ids = self._db_dict[BudName.contributing_experiment_ids]
140
+ if isinstance(experiment_ids, str):
141
+ return [experiment_ids]
142
+ return experiment_ids
111
143
 
112
144
  @property
113
145
  def obs_ip_start_time(self) -> str:
114
146
  """Return the start time of the observe IP."""
115
- return self._db_dict[BudName.obs_ip_start_time.value]
147
+ return self._db_dict[BudName.obs_ip_start_time]
116
148
 
117
149
  @property
118
150
  def average_cadence(self) -> float:
@@ -149,25 +181,7 @@ class ConstantsBase:
149
181
  """Get a list of readout exp times for all DARK frames."""
150
182
  return self._db_dict[BudName.dark_readout_exp_times]
151
183
 
152
- @property
153
- def stokes_params(self) -> [str]:
154
- """Return the list of stokes parameter names."""
155
- return ["I", "Q", "U", "V"]
156
-
157
- @property
158
- def experiment_id(self) -> str:
159
- """Get the experiment_id constant."""
160
- return self._db_dict[BudName.experiment_id]
161
-
162
- @property
163
- def contributing_experiment_ids(self) -> [str]:
164
- """Return the list of contributing experiment IDs."""
165
- experiment_ids = self._db_dict[BudName.contributing_experiment_ids]
166
- if isinstance(experiment_ids, str):
167
- return [experiment_ids]
168
- return experiment_ids
169
-
170
184
  @property
171
185
  def wavelength(self) -> float:
172
186
  """Wavelength."""
173
- return self._db_dict[BudName.wavelength.value]
187
+ return self._db_dict[BudName.wavelength]
@@ -2,11 +2,44 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from enum import StrEnum
5
6
  from pathlib import Path
7
+ from typing import Any
6
8
 
7
9
  import numpy as np
8
10
  from astropy.io import fits
9
11
 
12
+ NOT_FOUND_MESSAGE = "_HEADER_KEYWORD_NOT_FOUND"
13
+
14
+
15
+ class MetadataKey(StrEnum):
16
+ """Controlled list of names for FITS metadata header keys."""
17
+
18
+ ip_task_type = "IPTASK" # in L0FitsAccess
19
+ ip_start_time = "DKIST011" # in L0FitsAccess
20
+ ip_end_time = "DKIST012" # in L0FitsAccess
21
+ elevation = "ELEV_ANG"
22
+ azimuth = "TAZIMUTH"
23
+ table_angle = "TTBLANGL"
24
+ gos_level3_status = "LVL3STAT"
25
+ gos_level3_lamp_status = "LAMPSTAT"
26
+ gos_polarizer_status = "LVL2STAT"
27
+ gos_polarizer_angle = "POLANGLE"
28
+ gos_retarder_status = "LVL1STAT"
29
+ gos_retarder_angle = "RETANGLE"
30
+ gos_level0_status = "LVL0STAT"
31
+ time_obs = "DATE-BEG"
32
+ ip_id = "IP_ID"
33
+ instrument = "INSTRUME"
34
+ wavelength = "LINEWAV"
35
+ proposal_id = "PROP_ID"
36
+ experiment_id = "EXPER_ID"
37
+ num_dsps_repeats = "DSPSREPS"
38
+ current_dsps_repeat = "DSPSNUM"
39
+ fpa_exposure_time_ms = "XPOSURE"
40
+ sensor_readout_exposure_time_ms = "TEXPOSUR"
41
+ num_raw_frames_per_fpa = "NSUMEXP"
42
+
10
43
 
11
44
  class FitsAccessBase:
12
45
  """
@@ -35,6 +68,29 @@ class FitsAccessBase:
35
68
  def __repr__(self):
36
69
  return f"{self.__class__.__name__}(hdu={self._hdu!r}, name={self.name!r}, auto_squeeze={self.auto_squeeze})"
37
70
 
71
+ def _set_metadata_key_value(
72
+ self, key: StrEnum, optional: bool = False, default: Any = NOT_FOUND_MESSAGE
73
+ ) -> None:
74
+ """
75
+ Get the header value and assign it as a metadata key name attribute.
76
+
77
+ Parameters
78
+ ----------
79
+ key
80
+ The StrEnum member in attribute_name = fits_keyword structure
81
+ optional
82
+ If the keyword is optional
83
+ default
84
+ Value for the attribute if the key is not found
85
+ """
86
+ if optional:
87
+ if default != NOT_FOUND_MESSAGE:
88
+ setattr(self, key.name, self.header.get(key, default))
89
+ else:
90
+ setattr(self, key.name, self.header.get(key, key + NOT_FOUND_MESSAGE))
91
+ else:
92
+ setattr(self, key.name, self.header[key])
93
+
38
94
  @property
39
95
  def data(self) -> np.ndarray:
40
96
  """
@@ -1,6 +1,7 @@
1
1
  """Components of the Tag model. Stem + Optional Suffix = Tag."""
2
2
 
3
3
  from enum import Enum
4
+ from enum import StrEnum
4
5
 
5
6
  from dkist_processing_common.models.task_name import TaskName
6
7
 
@@ -8,7 +9,7 @@ from dkist_processing_common.models.task_name import TaskName
8
9
  EXP_TIME_ROUND_DIGITS: int = 6
9
10
 
10
11
 
11
- class StemName(str, Enum):
12
+ class StemName(StrEnum):
12
13
  """Controlled list of Tag Stems."""
13
14
 
14
15
  output = "OUTPUT"
@@ -1,9 +1,9 @@
1
1
  """Controlled list of common IP task tag names."""
2
2
 
3
- from enum import Enum
3
+ from enum import StrEnum
4
4
 
5
5
 
6
- class TaskName(str, Enum):
6
+ class TaskName(StrEnum):
7
7
  """Controlled list of task tag names."""
8
8
 
9
9
  observe = "OBSERVE"
@@ -101,7 +101,7 @@ class CSStepFlower(Stem):
101
101
  """
102
102
 
103
103
  def __init__(self, max_cs_step_time_sec: float):
104
- super().__init__(stem_name=StemName.cs_step.value)
104
+ super().__init__(stem_name=StemName.cs_step)
105
105
  self.max_cs_step_time_sec = max_cs_step_time_sec
106
106
 
107
107
  def setter(self, fits_obj: L0FitsAccess) -> CSStep | Type[SpilledDirt]:
@@ -148,7 +148,7 @@ class NumCSStepBud(Stem):
148
148
  """
149
149
 
150
150
  def __init__(self, max_cs_step_time_sec: float):
151
- super().__init__(stem_name=BudName.num_cs_steps.value)
151
+ super().__init__(stem_name=BudName.num_cs_steps)
152
152
  self.max_cs_step_time_sec = max_cs_step_time_sec
153
153
 
154
154
  def setter(self, fits_obj: L0FitsAccess) -> CSStep | Type[SpilledDirt]:
@@ -1,6 +1,7 @@
1
1
  """Classes supporting the Data Set Parameters Set (DSPS) Repeat parameter."""
2
2
 
3
3
  from dkist_processing_common.models.constants import BudName
4
+ from dkist_processing_common.models.fits_access import MetadataKey
4
5
  from dkist_processing_common.models.flower_pot import SpilledDirt
5
6
  from dkist_processing_common.models.tags import StemName
6
7
  from dkist_processing_common.models.task_name import TaskName
@@ -16,9 +17,9 @@ class TotalDspsRepeatsBud(TaskUniqueBud):
16
17
 
17
18
  def __init__(self):
18
19
  super().__init__(
19
- constant_name=BudName.num_dsps_repeats.value,
20
- metadata_key="num_dsps_repeats",
21
- ip_task_types=TaskName.observe.value,
20
+ constant_name=BudName.num_dsps_repeats,
21
+ metadata_key=MetadataKey.num_dsps_repeats,
22
+ ip_task_types=TaskName.observe,
22
23
  )
23
24
 
24
25
 
@@ -27,7 +28,7 @@ class DspsRepeatNumberFlower(SingleValueSingleKeyFlower):
27
28
 
28
29
  def __init__(self):
29
30
  super().__init__(
30
- tag_stem_name=StemName.dsps_repeat.value, metadata_key="current_dsps_repeat"
31
+ tag_stem_name=StemName.dsps_repeat, metadata_key=MetadataKey.current_dsps_repeat
31
32
  )
32
33
 
33
34
  def setter(self, fits_obj: L0FitsAccess):
@@ -1,6 +1,7 @@
1
1
  """Experiment Id parser."""
2
2
 
3
3
  from dkist_processing_common.models.constants import BudName
4
+ from dkist_processing_common.models.fits_access import MetadataKey
4
5
  from dkist_processing_common.parsers.id_bud import ContributingIdsBud
5
6
  from dkist_processing_common.parsers.id_bud import IdBud
6
7
 
@@ -9,7 +10,9 @@ class ExperimentIdBud(IdBud):
9
10
  """Class to create a Bud for the experiment_id."""
10
11
 
11
12
  def __init__(self):
12
- super().__init__(constant_name=BudName.experiment_id.value, metadata_key="experiment_id")
13
+ super().__init__(
14
+ constant_name=BudName.experiment_id, metadata_key=MetadataKey.experiment_id
15
+ )
13
16
 
14
17
 
15
18
  class ContributingExperimentIdsBud(ContributingIdsBud):
@@ -17,5 +20,5 @@ class ContributingExperimentIdsBud(ContributingIdsBud):
17
20
 
18
21
  def __init__(self):
19
22
  super().__init__(
20
- stem_name=BudName.contributing_experiment_ids.value, metadata_key="experiment_id"
23
+ stem_name=BudName.contributing_experiment_ids, metadata_key=MetadataKey.experiment_id
21
24
  )
@@ -1,5 +1,6 @@
1
1
  """Base classes for ID bud parsing."""
2
2
 
3
+ from enum import StrEnum
3
4
  from typing import Type
4
5
 
5
6
  from dkist_processing_common.models.flower_pot import SpilledDirt
@@ -12,19 +13,21 @@ from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
12
13
  class IdBud(TaskUniqueBud):
13
14
  """Base class for ID buds."""
14
15
 
15
- def __init__(self, constant_name, metadata_key):
16
+ def __init__(self, constant_name: str, metadata_key: str | StrEnum):
16
17
  super().__init__(
17
18
  constant_name=constant_name,
18
19
  metadata_key=metadata_key,
19
- ip_task_types=TaskName.observe.value,
20
+ ip_task_types=TaskName.observe,
20
21
  )
21
22
 
22
23
 
23
24
  class ContributingIdsBud(Stem):
24
25
  """Base class for contributing ID buds."""
25
26
 
26
- def __init__(self, stem_name, metadata_key):
27
+ def __init__(self, stem_name: str, metadata_key: str | StrEnum):
27
28
  super().__init__(stem_name=stem_name)
29
+ if isinstance(metadata_key, StrEnum):
30
+ metadata_key = metadata_key.name
28
31
  self.metadata_key = metadata_key
29
32
 
30
33
  def setter(self, fits_obj: L0FitsAccess) -> str | Type[SpilledDirt]:
@@ -2,6 +2,7 @@
2
2
 
3
3
  from astropy.io import fits
4
4
 
5
+ from dkist_processing_common.models.fits_access import MetadataKey
5
6
  from dkist_processing_common.parsers.l1_fits_access import L1FitsAccess
6
7
 
7
8
 
@@ -26,6 +27,6 @@ class L0FitsAccess(L1FitsAccess):
26
27
  auto_squeeze: bool = True,
27
28
  ):
28
29
  super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
29
- self.ip_task_type: str = self.header["IPTASK"]
30
- self.ip_start_time: str = self.header["DKIST011"]
31
- self.ip_end_time: str = self.header["DKIST012"]
30
+ self._set_metadata_key_value(MetadataKey.ip_task_type)
31
+ self._set_metadata_key_value(MetadataKey.ip_start_time)
32
+ self._set_metadata_key_value(MetadataKey.ip_end_time)
@@ -3,6 +3,7 @@
3
3
  from astropy.io import fits
4
4
 
5
5
  from dkist_processing_common.models.fits_access import FitsAccessBase
6
+ from dkist_processing_common.models.fits_access import MetadataKey
6
7
 
7
8
 
8
9
  class L1FitsAccess(FitsAccessBase):
@@ -27,31 +28,31 @@ class L1FitsAccess(FitsAccessBase):
27
28
  ):
28
29
  super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
29
30
 
30
- self.elevation: float = self.header["ELEV_ANG"]
31
- self.azimuth: float = self.header["TAZIMUTH"]
32
- self.table_angle: float = self.header["TTBLANGL"]
33
- self.gos_level3_status: str = self.header["LVL3STAT"]
34
- self.gos_level3_lamp_status: str = self.header["LAMPSTAT"]
35
- self.gos_polarizer_status: str = self.header["LVL2STAT"]
36
- self.gos_retarder_status: str = self.header["LVL1STAT"]
37
- self.gos_level0_status: str = self.header["LVL0STAT"]
38
- self.time_obs: str = self.header["DATE-BEG"]
39
- self.ip_id: str = self.header["IP_ID"]
40
- self.instrument: str = self.header["INSTRUME"]
41
- self.wavelength: float = self.header["LINEWAV"]
42
- self.proposal_id: str = self.header["PROP_ID"]
43
- self.experiment_id: str = self.header["EXPER_ID"]
44
- self.num_dsps_repeats: int = self.header["DSPSREPS"]
45
- self.current_dsps_repeat: int = self.header["DSPSNUM"]
46
- self.fpa_exposure_time_ms: float = self.header["XPOSURE"]
47
- self.sensor_readout_exposure_time_ms: float = self.header["TEXPOSUR"]
48
- self.num_raw_frames_per_fpa: int = self.header["NSUMEXP"]
31
+ self._set_metadata_key_value(MetadataKey.elevation)
32
+ self._set_metadata_key_value(MetadataKey.azimuth)
33
+ self._set_metadata_key_value(MetadataKey.table_angle)
34
+ self._set_metadata_key_value(MetadataKey.gos_level3_status)
35
+ self._set_metadata_key_value(MetadataKey.gos_level3_lamp_status)
36
+ self._set_metadata_key_value(MetadataKey.gos_polarizer_status)
37
+ self._set_metadata_key_value(MetadataKey.gos_retarder_status)
38
+ self._set_metadata_key_value(MetadataKey.gos_level0_status)
39
+ self._set_metadata_key_value(MetadataKey.time_obs)
40
+ self._set_metadata_key_value(MetadataKey.ip_id)
41
+ self._set_metadata_key_value(MetadataKey.instrument)
42
+ self._set_metadata_key_value(MetadataKey.wavelength)
43
+ self._set_metadata_key_value(MetadataKey.proposal_id)
44
+ self._set_metadata_key_value(MetadataKey.experiment_id)
45
+ self._set_metadata_key_value(MetadataKey.num_dsps_repeats)
46
+ self._set_metadata_key_value(MetadataKey.current_dsps_repeat)
47
+ self._set_metadata_key_value(MetadataKey.fpa_exposure_time_ms)
48
+ self._set_metadata_key_value(MetadataKey.sensor_readout_exposure_time_ms)
49
+ self._set_metadata_key_value(MetadataKey.num_raw_frames_per_fpa)
49
50
 
50
51
  @property
51
52
  def gos_polarizer_angle(self) -> float:
52
53
  """Convert the polarizer angle to a float if possible before returning."""
53
54
  try:
54
- return float(self.header["POLANGLE"])
55
+ return float(self.header[MetadataKey.gos_polarizer_angle])
55
56
  except ValueError:
56
57
  return -999 # The angle is only used if the polarizer is in the beam
57
58
 
@@ -59,6 +60,6 @@ class L1FitsAccess(FitsAccessBase):
59
60
  def gos_retarder_angle(self) -> float:
60
61
  """Convert the retarder angle to a float if possible before returning."""
61
62
  try:
62
- return float(self.header["RETANGLE"])
63
+ return float(self.header[MetadataKey.gos_retarder_angle])
63
64
  except ValueError:
64
65
  return -999 # The angle is only used if the retarder is in the beam
@@ -1,5 +1,6 @@
1
1
  """Pre-made flower that reads a single header key from all files and raises a ValueError if the values are not in a supplied range."""
2
2
 
3
+ from enum import StrEnum
3
4
  from statistics import mean
4
5
  from typing import Callable
5
6
 
@@ -30,10 +31,12 @@ class NearFloatBud(Stem):
30
31
  def __init__(
31
32
  self,
32
33
  constant_name: str,
33
- metadata_key: str,
34
+ metadata_key: str | StrEnum,
34
35
  tolerance: float,
35
36
  ):
36
37
  super().__init__(stem_name=constant_name)
38
+ if isinstance(metadata_key, StrEnum):
39
+ metadata_key = metadata_key.name
37
40
  self.metadata_key = metadata_key
38
41
  self.tolerance = tolerance
39
42
 
@@ -98,7 +101,7 @@ class TaskNearFloatBud(NearFloatBud):
98
101
  def __init__(
99
102
  self,
100
103
  constant_name: str,
101
- metadata_key: str,
104
+ metadata_key: str | StrEnum,
102
105
  ip_task_types: str | list[str],
103
106
  tolerance: float,
104
107
  task_type_parsing_function: Callable = passthrough_header_ip_task,
@@ -1,6 +1,7 @@
1
1
  """Proposal Id parser."""
2
2
 
3
3
  from dkist_processing_common.models.constants import BudName
4
+ from dkist_processing_common.models.fits_access import MetadataKey
4
5
  from dkist_processing_common.parsers.id_bud import ContributingIdsBud
5
6
  from dkist_processing_common.parsers.id_bud import IdBud
6
7
 
@@ -9,7 +10,7 @@ class ProposalIdBud(IdBud):
9
10
  """Class to create a Bud for the proposal_id."""
10
11
 
11
12
  def __init__(self):
12
- super().__init__(constant_name=BudName.proposal_id.value, metadata_key="proposal_id")
13
+ super().__init__(constant_name=BudName.proposal_id, metadata_key=MetadataKey.proposal_id)
13
14
 
14
15
 
15
16
  class ContributingProposalIdsBud(ContributingIdsBud):
@@ -17,5 +18,5 @@ class ContributingProposalIdsBud(ContributingIdsBud):
17
18
 
18
19
  def __init__(self):
19
20
  super().__init__(
20
- stem_name=BudName.contributing_proposal_ids.value, metadata_key="proposal_id"
21
+ stem_name=BudName.contributing_proposal_ids, metadata_key=MetadataKey.proposal_id
21
22
  )
@@ -1,6 +1,7 @@
1
1
  """Bud that parses the name of the retarder used during POLCAL task observations."""
2
2
 
3
3
  from dkist_processing_common.models.constants import BudName
4
+ from dkist_processing_common.models.fits_access import MetadataKey
4
5
  from dkist_processing_common.models.task_name import TaskName
5
6
  from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
6
7
 
@@ -19,9 +20,9 @@ class RetarderNameBud(TaskUniqueBud):
19
20
 
20
21
  def __init__(self):
21
22
  super().__init__(
22
- constant_name=BudName.retarder_name.value,
23
- metadata_key="gos_retarder_status",
24
- ip_task_types=TaskName.polcal.value,
23
+ constant_name=BudName.retarder_name,
24
+ metadata_key=MetadataKey.gos_retarder_status,
25
+ ip_task_types=TaskName.polcal,
25
26
  )
26
27
 
27
28
  def getter(self, key) -> str:
@@ -1,5 +1,8 @@
1
1
  """Pre-made flower that produces tag based on a single header key."""
2
2
 
3
+ from enum import StrEnum
4
+
5
+ from dkist_processing_common.models.fits_access import MetadataKey
3
6
  from dkist_processing_common.models.flower_pot import Stem
4
7
  from dkist_processing_common.parsers.l0_fits_access import L0FitsAccess
5
8
 
@@ -16,8 +19,10 @@ class SingleValueSingleKeyFlower(Stem):
16
19
  The metadata key
17
20
  """
18
21
 
19
- def __init__(self, tag_stem_name: str, metadata_key: str):
22
+ def __init__(self, tag_stem_name: str, metadata_key: str | StrEnum):
20
23
  super().__init__(stem_name=tag_stem_name)
24
+ if isinstance(metadata_key, StrEnum):
25
+ metadata_key = metadata_key.name
21
26
  self.metadata_key = metadata_key
22
27
 
23
28
  def setter(self, fits_obj: L0FitsAccess):
@@ -4,6 +4,7 @@ from typing import Callable
4
4
  from typing import Type
5
5
 
6
6
  from dkist_processing_common.models.fits_access import FitsAccessBase
7
+ from dkist_processing_common.models.fits_access import MetadataKey
7
8
  from dkist_processing_common.models.flower_pot import SpilledDirt
8
9
  from dkist_processing_common.models.tags import StemName
9
10
  from dkist_processing_common.models.task_name import TaskName
@@ -36,9 +37,9 @@ def parse_header_ip_task_with_gains(fits_obj: FitsAccessBase) -> str:
36
37
  and fits_obj.gos_level3_status == "lamp"
37
38
  and fits_obj.gos_level3_lamp_status == "on"
38
39
  ):
39
- return TaskName.lamp_gain.value
40
+ return TaskName.lamp_gain
40
41
  if fits_obj.ip_task_type == "gain" and fits_obj.gos_level3_status == "clear":
41
- return TaskName.solar_gain.value
42
+ return TaskName.solar_gain
42
43
 
43
44
  # Everything else is unchanged
44
45
  return passthrough_header_ip_task(fits_obj)
@@ -59,14 +60,14 @@ def parse_polcal_task_type(fits_obj: Type[FitsAccessBase]) -> str | Type[Spilled
59
60
  and fits_obj.gos_retarder_status == "clear"
60
61
  and fits_obj.gos_polarizer_status == "clear"
61
62
  ):
62
- return TaskName.polcal_dark.value
63
+ return TaskName.polcal_dark
63
64
 
64
65
  elif (
65
66
  fits_obj.gos_level0_status.startswith("FieldStop")
66
67
  and fits_obj.gos_retarder_status == "clear"
67
68
  and fits_obj.gos_polarizer_status == "clear"
68
69
  ):
69
- return TaskName.polcal_gain.value
70
+ return TaskName.polcal_gain
70
71
 
71
72
  # We don't care about a POLCAL frame that is neither dark nor clear
72
73
  return SpilledDirt
@@ -78,7 +79,7 @@ class TaskTypeFlower(SingleValueSingleKeyFlower):
78
79
  def __init__(
79
80
  self, header_task_parsing_func: Callable[[FitsAccessBase], str] = passthrough_header_ip_task
80
81
  ):
81
- super().__init__(tag_stem_name=StemName.task.value, metadata_key="ip_task_type")
82
+ super().__init__(tag_stem_name=StemName.task, metadata_key=MetadataKey.ip_task_type)
82
83
  self.header_parsing_function = header_task_parsing_func
83
84
 
84
85
  def setter(self, fits_obj: FitsAccessBase):
@@ -103,7 +104,7 @@ class PolcalTaskFlower(SingleValueSingleKeyFlower):
103
104
  """
104
105
 
105
106
  def __init__(self):
106
- super().__init__(tag_stem_name=StemName.task.value, metadata_key="ip_task_type")
107
+ super().__init__(tag_stem_name=StemName.task, metadata_key=MetadataKey.ip_task_type)
107
108
 
108
109
  def setter(self, fits_obj: FitsAccessBase):
109
110
  """
@@ -2,6 +2,7 @@
2
2
 
3
3
  from datetime import datetime
4
4
  from datetime import timezone
5
+ from enum import StrEnum
5
6
  from typing import Callable
6
7
  from typing import Hashable
7
8
  from typing import Type
@@ -9,6 +10,7 @@ from typing import Type
9
10
  import numpy as np
10
11
 
11
12
  from dkist_processing_common.models.constants import BudName
13
+ from dkist_processing_common.models.fits_access import MetadataKey
12
14
  from dkist_processing_common.models.flower_pot import SpilledDirt
13
15
  from dkist_processing_common.models.flower_pot import Stem
14
16
  from dkist_processing_common.models.tags import EXP_TIME_ROUND_DIGITS
@@ -28,9 +30,9 @@ class ObsIpStartTimeBud(TaskUniqueBud):
28
30
 
29
31
  def __init__(self):
30
32
  super().__init__(
31
- constant_name=BudName.obs_ip_start_time.value,
32
- metadata_key="ip_start_time",
33
- ip_task_types=TaskName.observe.value,
33
+ constant_name=BudName.obs_ip_start_time,
34
+ metadata_key=MetadataKey.ip_start_time,
35
+ ip_task_types=TaskName.observe,
34
36
  )
35
37
 
36
38
 
@@ -38,7 +40,7 @@ class CadenceBudBase(UniqueBud):
38
40
  """Base class for all Cadence Buds."""
39
41
 
40
42
  def __init__(self, constant_name: str):
41
- super().__init__(constant_name, metadata_key="time_obs")
43
+ super().__init__(constant_name, metadata_key=MetadataKey.time_obs)
42
44
 
43
45
  def setter(self, fits_obj: L0FitsAccess) -> float | Type[SpilledDirt]:
44
46
  """
@@ -65,7 +67,7 @@ class AverageCadenceBud(CadenceBudBase):
65
67
  """Class for the average cadence Bud."""
66
68
 
67
69
  def __init__(self):
68
- super().__init__(constant_name=BudName.average_cadence.value)
70
+ super().__init__(constant_name=BudName.average_cadence)
69
71
 
70
72
  def getter(self, key) -> np.float64:
71
73
  """
@@ -87,7 +89,7 @@ class MaximumCadenceBud(CadenceBudBase):
87
89
  """Class for the maximum cadence bud."""
88
90
 
89
91
  def __init__(self):
90
- super().__init__(constant_name=BudName.maximum_cadence.value)
92
+ super().__init__(constant_name=BudName.maximum_cadence)
91
93
 
92
94
  def getter(self, key) -> np.float64:
93
95
  """
@@ -109,7 +111,7 @@ class MinimumCadenceBud(CadenceBudBase):
109
111
  """Class for the minimum cadence bud."""
110
112
 
111
113
  def __init__(self):
112
- super().__init__(constant_name=BudName.minimum_cadence.value)
114
+ super().__init__(constant_name=BudName.minimum_cadence)
113
115
 
114
116
  def getter(self, key) -> np.float64:
115
117
  """
@@ -131,7 +133,7 @@ class VarianceCadenceBud(CadenceBudBase):
131
133
  """Class for the variance cadence Bud."""
132
134
 
133
135
  def __init__(self):
134
- super().__init__(constant_name=BudName.variance_cadence.value)
136
+ super().__init__(constant_name=BudName.variance_cadence)
135
137
 
136
138
  def getter(self, key) -> np.float64:
137
139
  """
@@ -172,7 +174,7 @@ class ExposureTimeFlower(TimeFlowerBase):
172
174
 
173
175
  def __init__(self):
174
176
  super().__init__(
175
- tag_stem_name=StemName.exposure_time.value, metadata_key="fpa_exposure_time_ms"
177
+ tag_stem_name=StemName.exposure_time, metadata_key=MetadataKey.fpa_exposure_time_ms
176
178
  )
177
179
 
178
180
 
@@ -181,8 +183,8 @@ class ReadoutExpTimeFlower(TimeFlowerBase):
181
183
 
182
184
  def __init__(self):
183
185
  super().__init__(
184
- tag_stem_name=StemName.readout_exp_time.value,
185
- metadata_key="sensor_readout_exposure_time_ms",
186
+ tag_stem_name=StemName.readout_exp_time,
187
+ metadata_key=MetadataKey.sensor_readout_exposure_time_ms,
186
188
  )
187
189
 
188
190
 
@@ -213,15 +215,17 @@ class TaskTimeBudBase(Stem):
213
215
  def __init__(
214
216
  self,
215
217
  stem_name: str,
216
- metadata_key: str,
218
+ metadata_key: str | StrEnum,
217
219
  ip_task_types: str | list[str],
218
220
  header_task_parsing_func: Callable = passthrough_header_ip_task,
219
221
  ):
220
222
  super().__init__(stem_name=stem_name)
221
223
 
224
+ if isinstance(metadata_key, StrEnum):
225
+ metadata_key = metadata_key.name
226
+ self.metadata_key = metadata_key
222
227
  if isinstance(ip_task_types, str):
223
228
  ip_task_types = [ip_task_types]
224
- self.metadata_key = metadata_key
225
229
  self.ip_task_types = [task.casefold() for task in ip_task_types]
226
230
  self.header_parsing_function = header_task_parsing_func
227
231
 
@@ -252,7 +256,7 @@ class TaskExposureTimesBud(TaskTimeBudBase):
252
256
  ):
253
257
  super().__init__(
254
258
  stem_name=stem_name,
255
- metadata_key="fpa_exposure_time_ms",
259
+ metadata_key=MetadataKey.fpa_exposure_time_ms,
256
260
  ip_task_types=ip_task_types,
257
261
  header_task_parsing_func=header_task_parsing_func,
258
262
  )
@@ -269,7 +273,7 @@ class TaskReadoutExpTimesBud(TaskTimeBudBase):
269
273
  ):
270
274
  super().__init__(
271
275
  stem_name=stem_name,
272
- metadata_key="sensor_readout_exposure_time_ms",
276
+ metadata_key=MetadataKey.sensor_readout_exposure_time_ms,
273
277
  ip_task_types=ip_task_types,
274
278
  header_task_parsing_func=header_task_parsing_func,
275
279
  )
@@ -1,5 +1,6 @@
1
1
  """Pre-made flower that reads a single header key from all files and raises a ValueError if it is not unique."""
2
2
 
3
+ from enum import StrEnum
3
4
  from typing import Callable
4
5
 
5
6
  from dkist_processing_common.models.flower_pot import SpilledDirt
@@ -24,9 +25,11 @@ class UniqueBud(Stem):
24
25
  def __init__(
25
26
  self,
26
27
  constant_name: str,
27
- metadata_key: str,
28
+ metadata_key: str | StrEnum,
28
29
  ):
29
30
  super().__init__(stem_name=constant_name)
31
+ if isinstance(metadata_key, StrEnum):
32
+ metadata_key = metadata_key.name
30
33
  self.metadata_key = metadata_key
31
34
 
32
35
  def setter(self, fits_obj: L0FitsAccess):
@@ -85,7 +88,7 @@ class TaskUniqueBud(UniqueBud):
85
88
  def __init__(
86
89
  self,
87
90
  constant_name: str,
88
- metadata_key: str,
91
+ metadata_key: str | StrEnum,
89
92
  ip_task_types: str | list[str],
90
93
  task_type_parsing_function: Callable = passthrough_header_ip_task,
91
94
  ):
@@ -1,6 +1,7 @@
1
1
  """Bud to get the wavelength of observe frames."""
2
2
 
3
3
  from dkist_processing_common.models.constants import BudName
4
+ from dkist_processing_common.models.fits_access import MetadataKey
4
5
  from dkist_processing_common.models.task_name import TaskName
5
6
  from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
6
7
 
@@ -10,7 +11,7 @@ class ObserveWavelengthBud(TaskUniqueBud):
10
11
 
11
12
  def __init__(self):
12
13
  super().__init__(
13
- constant_name=BudName.wavelength.value,
14
- metadata_key="wavelength",
15
- ip_task_types=TaskName.observe.value,
14
+ constant_name=BudName.wavelength,
15
+ metadata_key=MetadataKey.wavelength,
16
+ ip_task_types=TaskName.observe,
16
17
  )
@@ -28,10 +28,12 @@ from typing import TypeVar
28
28
 
29
29
  from dkist_processing_common.codecs.fits import fits_access_decoder
30
30
  from dkist_processing_common.models.constants import BudName
31
+ from dkist_processing_common.models.fits_access import MetadataKey
31
32
  from dkist_processing_common.models.flower_pot import FlowerPot
32
33
  from dkist_processing_common.models.flower_pot import Stem
33
34
  from dkist_processing_common.models.flower_pot import Thorn
34
35
  from dkist_processing_common.models.tags import Tag
36
+ from dkist_processing_common.models.task_name import TaskName
35
37
  from dkist_processing_common.parsers.experiment_id_bud import ContributingExperimentIdsBud
36
38
  from dkist_processing_common.parsers.experiment_id_bud import ExperimentIdBud
37
39
  from dkist_processing_common.parsers.proposal_id_bud import ContributingProposalIdsBud
@@ -60,7 +62,7 @@ S = TypeVar("S", bound=Stem)
60
62
  def default_constant_bud_factory() -> list[S]:
61
63
  """Provide default constant buds for use in common parsing tasks."""
62
64
  return [
63
- UniqueBud(constant_name=BudName.instrument.value, metadata_key="instrument"),
65
+ UniqueBud(constant_name=BudName.instrument, metadata_key=MetadataKey.instrument),
64
66
  ProposalIdBud(),
65
67
  ContributingProposalIdsBud(),
66
68
  ExperimentIdBud(),
@@ -69,9 +71,9 @@ def default_constant_bud_factory() -> list[S]:
69
71
  MaximumCadenceBud(),
70
72
  MinimumCadenceBud(),
71
73
  VarianceCadenceBud(),
72
- TaskExposureTimesBud(stem_name=BudName.dark_exposure_times.value, ip_task_types="dark"),
74
+ TaskExposureTimesBud(stem_name=BudName.dark_exposure_times, ip_task_types=TaskName.dark),
73
75
  TaskReadoutExpTimesBud(
74
- stem_name=BudName.dark_readout_exp_times.value, ip_task_types="dark"
76
+ stem_name=BudName.dark_readout_exp_times, ip_task_types=TaskName.dark
75
77
  ),
76
78
  ]
77
79
 
@@ -60,7 +60,6 @@ def assemble_task_with_tagged_movie_frames(
60
60
  task.constants._update(
61
61
  {
62
62
  BudName.num_dsps_repeats.value: num_dsps_repeats,
63
- BudName.recipe_run_id.value: recipe_run_id,
64
63
  }
65
64
  )
66
65
  for d in range(num_dsps_repeats):
@@ -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(tmp_path):
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 = self.header["NAXIS"]
134
- self.naxis1 = self.header["NAXIS1"]
135
- self.naxis2 = self.header["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
@@ -1,5 +1,7 @@
1
1
  """Tests for the parse L0 input data task"""
2
2
 
3
+ from enum import StrEnum
4
+
3
5
  import numpy as np
4
6
  import pytest
5
7
  from astropy.io import fits
@@ -20,6 +22,7 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
20
22
  )
21
23
  from dkist_processing_common.parsers.unique_bud import UniqueBud
22
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
23
26
 
24
27
 
25
28
  class VispHeaders(Spec122Dataset):
@@ -85,25 +88,33 @@ class VispHeaders(Spec122Dataset):
85
88
  return self.index % self.num_mod
86
89
 
87
90
 
91
+ class ViSPMetadataKey(StrEnum):
92
+ num_mod = "VISP_010"
93
+ modstate = "VISP_011"
94
+ ip_task_type = "DKIST004"
95
+
96
+
88
97
  class ViSPFitsAccess(FitsAccessBase):
89
98
  def __init__(self, hdu, name, auto_squeeze=False):
90
99
  super().__init__(hdu, name, auto_squeeze=auto_squeeze)
91
- self.num_mod: int = self.header["VISP_010"]
92
- self.modstate: int = self.header["VISP_011"]
93
- self.ip_task_type: str = self.header["DKIST004"]
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)
94
103
  self.name = name
95
104
 
96
105
 
97
106
  @pytest.fixture(scope="function")
98
107
  def visp_flowers():
99
108
  return [
100
- SingleValueSingleKeyFlower(tag_stem_name=StemName.modstate.value, metadata_key="modstate")
109
+ SingleValueSingleKeyFlower(
110
+ tag_stem_name=StemName.modstate, metadata_key=ViSPMetadataKey.modstate
111
+ )
101
112
  ]
102
113
 
103
114
 
104
115
  @pytest.fixture(scope="function")
105
116
  def visp_buds():
106
- return [UniqueBud(constant_name=BudName.num_modstates.value, metadata_key="num_mod")]
117
+ return [UniqueBud(constant_name=BudName.num_modstates, metadata_key=ViSPMetadataKey.num_mod)]
107
118
 
108
119
 
109
120
  @pytest.fixture(scope="function")
@@ -233,9 +244,9 @@ def test_make_flowerpots(parse_inputs_task):
233
244
 
234
245
  assert len(tag_pot.stems) == 2
235
246
  assert len(constant_pot.stems) == 3
236
- assert tag_pot.stems[0].stem_name == StemName.modstate.value
247
+ assert tag_pot.stems[0].stem_name == StemName.modstate
237
248
  assert tag_pot.stems[1].stem_name == "EMPTY_FLOWER"
238
- assert constant_pot.stems[0].stem_name == BudName.num_modstates.value
249
+ assert constant_pot.stems[0].stem_name == BudName.num_modstates
239
250
  assert constant_pot.stems[1].stem_name == "EMPTY_BUD"
240
251
  assert constant_pot.stems[2].stem_name == "PICKY_BUD"
241
252
 
@@ -250,23 +261,10 @@ def test_subclass_flowers(visp_parse_inputs_task, max_cs_step_time_sec):
250
261
 
251
262
  assert len(tag_pot.stems) == 1
252
263
  assert len(constant_pot.stems) == 12
253
- assert sorted([f.stem_name for f in tag_pot.stems]) == sorted([StemName.modstate.value])
254
- assert sorted([f.stem_name for f in constant_pot.stems]) == sorted(
255
- [
256
- BudName.instrument.value,
257
- BudName.num_modstates.value,
258
- BudName.proposal_id.value,
259
- BudName.contributing_proposal_ids.value,
260
- BudName.average_cadence.value,
261
- BudName.maximum_cadence.value,
262
- BudName.minimum_cadence.value,
263
- BudName.variance_cadence.value,
264
- BudName.dark_exposure_times.value,
265
- BudName.dark_readout_exp_times.value,
266
- BudName.experiment_id.value,
267
- BudName.contributing_experiment_ids.value,
268
- ]
269
- )
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)
270
268
 
271
269
 
272
270
  def test_constants_correct(parse_inputs_task):
@@ -1,3 +1,4 @@
1
+ from enum import StrEnum
1
2
  from itertools import chain
2
3
 
3
4
  import pytest
@@ -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
- self.thing_id: int = self.header.get("id_key")
46
- self.constant_thing: float = self.header.get("constant")
47
- self.near_thing: float = self.header.get("near")
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 stems that have been added to parse_l0_input_data
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.ip_task_type: str = self.header["IPTASK"]
22
- self.gos_level3_status: str = self.header["GOSLVL3"]
23
- self.gos_level3_lamp_status: str = self.header["GOSLAMP"]
24
- self.gos_level0_status: str = self.header["GOSLVL0"]
25
- self.gos_retarder_status: str = self.header["GOSRET"]
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dkist-processing-common
3
- Version: 11.4.0rc1
3
+ Version: 11.5.0rc1
4
4
  Summary: Common task classes used by the DKIST science data processing pipelines
5
5
  Author-email: NSO / AURA <dkistdc@nso.edu>
6
6
  License: BSD-3-Clause
@@ -22,9 +22,9 @@ dkist_processing_common/codecs/str.py,sha256=Xqt5k8IhLc95KiiNiFwB1JWcVVc6T8AfcLr
22
22
  dkist_processing_common/fonts/Lato-Regular.ttf,sha256=1jbkaDIx-THtoiLViOlE0IK_0726AvkovuRhwPGFslE,656568
23
23
  dkist_processing_common/fonts/__init__.py,sha256=hBvZRtkoGRPlNDWCK-ZePXdSIlThCcjwBDfYaamVgAw,101
24
24
  dkist_processing_common/models/__init__.py,sha256=6LMqemdzVZ87fRrpAsbEnTtWZ02_Gu_oajsUlwGRH_Q,74
25
- dkist_processing_common/models/constants.py,sha256=PFTyT1Z2I5RnNlwsYospAJyielRj7YJOCrSwvqaOjTE,5442
25
+ dkist_processing_common/models/constants.py,sha256=oghBiVmtVk7YLkaQ2v8oZ-COHlAhmfjSvO9ZwzXCvLw,5828
26
26
  dkist_processing_common/models/dkist_location.py,sha256=6Nk0wvv4R8ptlrV7BXon7abq4YLvmTdUmPsDN5G8nWc,971
27
- dkist_processing_common/models/fits_access.py,sha256=hCQa1c9C_iFg7MjdvjXf38GBwQ3HiIYoKmxecJaOw5w,4114
27
+ dkist_processing_common/models/fits_access.py,sha256=NLxvIsaFrv4Fui1EhGzkV5XXjv-Vi8DQaLfMHOrRdzc,5949
28
28
  dkist_processing_common/models/flower_pot.py,sha256=_J7DwHM8u5kQfdPCpk5pUmALtLrM1L_h-x8JW5BSjXA,5129
29
29
  dkist_processing_common/models/fried_parameter.py,sha256=ro_H2Eo3I88lRf1wJjZfTc_XOjhgLt4whIQR_sjAFbM,1609
30
30
  dkist_processing_common/models/graphql.py,sha256=oSEcdVsVRytnIDEORLs4b6r3C2Lr7gqEEMHu82zpOOg,5912
@@ -34,31 +34,31 @@ dkist_processing_common/models/message_queue_binding.py,sha256=Y4otwkkePrLRSjlry
34
34
  dkist_processing_common/models/metric_code.py,sha256=WSLF9yqcVzk9L9u8WBhgtpUYUWYsG4ZFWFRFtezdUCM,848
35
35
  dkist_processing_common/models/parameters.py,sha256=9An3SxUEBI-oYHjICQ_q-IIScTfpvVeAFH7jLzBzzWI,9649
36
36
  dkist_processing_common/models/quality.py,sha256=TmDVbvPbfl5CIIs1ioD5guLUoEOFTfiJESvDjLTLl5s,3981
37
- dkist_processing_common/models/tags.py,sha256=A3ryBjkIkU8FaFcuMka8UZFc53rVWWyZ4OzOqouVgD4,12059
38
- dkist_processing_common/models/task_name.py,sha256=kTYjHdB_xqMr3hKWN6fn3c0T-Lj4LzJdIYnNXcXkkQI,566
37
+ dkist_processing_common/models/tags.py,sha256=0YqiDrismOSds_3XtFBb2dfv0gjMs6CgRv2dJKsSthI,12082
38
+ dkist_processing_common/models/task_name.py,sha256=uAl7qTK4Xx1nqPAhNAe5nAXqxwPwQzAq58YmoccX6xQ,567
39
39
  dkist_processing_common/models/wavelength.py,sha256=4UhRVoNvCHZitXo5S1oRdewadbmGfmDK6wetMV06POA,967
40
40
  dkist_processing_common/parsers/__init__.py,sha256=XJQzHtPb78F6-qXXKXjyztc0x-aHVlgv1C_l4dR88tI,67
41
- dkist_processing_common/parsers/cs_step.py,sha256=HQIFq1bVnK-0cOhAziMivmNqxtzQ5SWpTjrLXpJDQdg,6462
42
- dkist_processing_common/parsers/dsps_repeat.py,sha256=mb-PRK2M8ZJyGqEMdtiiem82iOgxwn0dMBw_yY5gSi8,1573
43
- dkist_processing_common/parsers/experiment_id_bud.py,sha256=6XlkpmGcHEAK-SKOcux0WuFIsMh_DzX5ANXQhfgPp4Q,707
44
- dkist_processing_common/parsers/id_bud.py,sha256=sebLspD1t4aQ1Ughz9RHjyCAUidF5YLIDOVyv6J0Zmk,1538
45
- dkist_processing_common/parsers/l0_fits_access.py,sha256=hA6AHDA4sNSJCBFxWnzjKEQ9xXWk5YY95E3Zmtp36wY,922
46
- dkist_processing_common/parsers/l1_fits_access.py,sha256=3bQgtugs2TihUhm-72_yLEeXxuSnfTsHLcNBDHt9Yms,2596
47
- dkist_processing_common/parsers/near_bud.py,sha256=mXJedJxAHmQTpEAkzRrwv5SSZnb_7ssq2PEdkhHGVdE,3866
48
- dkist_processing_common/parsers/proposal_id_bud.py,sha256=lv8k_wFThtDC5w1dXVROTvfQHDXMpmRAjbsjhT_TjhU,678
41
+ dkist_processing_common/parsers/cs_step.py,sha256=rL2gdstKEV5aqdPDs3a5EuUaOT_6YXDJVqIPIVKSw8M,6450
42
+ dkist_processing_common/parsers/dsps_repeat.py,sha256=Jg6oI9-PtFQbQHbGul6_eiRzBKr0Z2HIGOitG0G5CD4,1642
43
+ dkist_processing_common/parsers/experiment_id_bud.py,sha256=5ARnj6M5PEef_5-IVD1W6zl5DkYqx8eyt5-26Se-X2s,804
44
+ dkist_processing_common/parsers/id_bud.py,sha256=XohLGwVC-AD66qJe6I3AKdm5ArQIf3ryfDzPCQ5hEBk,1688
45
+ dkist_processing_common/parsers/l0_fits_access.py,sha256=gbE_4vFexOeKNLvqWvUFNxg-zBRpY1rCfRpIWHFpt_0,1009
46
+ dkist_processing_common/parsers/l1_fits_access.py,sha256=dJbbKRExoRpl94cSpgZg5SQZLh4tkV8p0vsKO8P8FLE,2814
47
+ dkist_processing_common/parsers/near_bud.py,sha256=lOHrjERKh3ooFusbd3_N-IDDpEOD9VWq2G82_wJ2c08,4002
48
+ dkist_processing_common/parsers/proposal_id_bud.py,sha256=tKW8dwh7XkECU1Z0T1E4AHj42emV3-RRVBBpVUZloTk,753
49
49
  dkist_processing_common/parsers/quality.py,sha256=hhCT-bHODOYSp9jvgsiWfYcLYtQCSWgfuy0xZnkYXwo,1141
50
- dkist_processing_common/parsers/retarder.py,sha256=YZ1VX9fUerYbyhT-RXSdvPCu-u4_oP4Ob5OpXyRTaiY,1442
51
- dkist_processing_common/parsers/single_value_single_key_flower.py,sha256=PbiYBcePLHjkF7Hc050PO_qhAsisT_EvVL9npz15AGE,1231
52
- dkist_processing_common/parsers/task.py,sha256=axjAPAU230KTVHaLx6_qidoPEwDB31_mRG3IZZnTUX0,3939
53
- dkist_processing_common/parsers/time.py,sha256=M6GQPoEuFaU3ZokyKh1tXrc0yDR_cl0FamEK7BaskCM,8371
54
- dkist_processing_common/parsers/unique_bud.py,sha256=TMj6RqACgRnMG2Pm36pj1Y-KWkImIBo0F_91b6H6mDo,3174
55
- dkist_processing_common/parsers/wavelength.py,sha256=_XYJZqxw-sbzVi2NQyG_aqcQ5XOH1uBhckNIPMm0fEg,547
50
+ dkist_processing_common/parsers/retarder.py,sha256=AvDrMLmxuwKOIP3NNm9mtV7dV2kw_aqW92hTLd1UdK0,1507
51
+ dkist_processing_common/parsers/single_value_single_key_flower.py,sha256=3mcPOG6dEU7IHWm0Jtjx3FVzbxAJCiKtLyVwZqFd2lA,1425
52
+ dkist_processing_common/parsers/task.py,sha256=nuGxDl5_gjbi07jMOrCPD5J9L51lyZZ6VS25FuDC3V8,3990
53
+ dkist_processing_common/parsers/time.py,sha256=z9zHV3Fz6ebEDgiPhv6H-aAS8e-sSW3EkBbPbtHpzGY,8576
54
+ dkist_processing_common/parsers/unique_bud.py,sha256=IkS2zZkVzn3PRsYF2ksBkUxl_HJ4TxCqBKJUs1WdL54,3310
55
+ dkist_processing_common/parsers/wavelength.py,sha256=P5C9mG8DAKK3GB3vWNRBI5l7pAW68lJK-kw-4eqERuQ,612
56
56
  dkist_processing_common/tasks/__init__.py,sha256=l23ctjNsKJbHbbqaZBMeOPaOtw0hmITEljI_JJ-CVsU,627
57
57
  dkist_processing_common/tasks/assemble_movie.py,sha256=m8O1psUBn8bRPj0AwMd1K5wNlcAtlxBQb1PZeSKne6o,12772
58
58
  dkist_processing_common/tasks/base.py,sha256=mebbG-VZp9Iu-J7skE9S5qMCUENZy48ySJkaGh8weC0,13144
59
59
  dkist_processing_common/tasks/l1_output_data.py,sha256=jjJao1YO88sgsKfl6Lg11yurlM1bcljOZpK1-ykFWw4,10576
60
60
  dkist_processing_common/tasks/output_data_base.py,sha256=ZWdxkXKzrQdc1PN97b8cEcFBckraOJkwrBoMOPWii8o,3683
61
- dkist_processing_common/tasks/parse_l0_input_data.py,sha256=B7ZYzT2jFPlzaSq92LgisZQIjqeFD123qTa8KeyLKSQ,7998
61
+ dkist_processing_common/tasks/parse_l0_input_data.py,sha256=Apa1qG6TAVll_oCGp5aSZb_j_ywNnu1GmYNnpjvHqH4,8133
62
62
  dkist_processing_common/tasks/quality_metrics.py,sha256=5rLCkGyE6F76bJp71y2lmKbol5FJ_ynbZbkhX8IBPKA,12489
63
63
  dkist_processing_common/tasks/teardown.py,sha256=eHyOJbtu8w51wFSTVFSt8iVVOkSODqHLvrcP6W-Szcg,2355
64
64
  dkist_processing_common/tasks/transfer_input_data.py,sha256=hjJzgwCVqltF87IMa2Oi9hmKyT0mFqwvPCI8xTV2B44,5787
@@ -76,14 +76,14 @@ dkist_processing_common/tasks/mixin/quality/_metrics.py,sha256=k0hlthVbW7Jso9q_P
76
76
  dkist_processing_common/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  dkist_processing_common/tests/conftest.py,sha256=Tm-Yq956EAafpDtu1d7JjdVY0Unp9e4z9ak-yf4wnH4,22320
78
78
  dkist_processing_common/tests/mock_metadata_store.py,sha256=fbCvSk1-s0ojN6l538RWodPW7dx6k4eXqipemnHKO0Y,8248
79
- dkist_processing_common/tests/test_assemble_movie.py,sha256=RI7Kitd-XNmba-o5pUgqURocXPFi15T-mEvWaSbsN6U,4125
79
+ dkist_processing_common/tests/test_assemble_movie.py,sha256=dyVhowxB-Kc6GuxlDs74UrPtK9fwdUL7y5haA3Bidz0,4065
80
80
  dkist_processing_common/tests/test_assemble_quality.py,sha256=-F22jMY6mPy65VZ1TZY2r1vsxMXOPmZHArGx70OD3BA,17832
81
81
  dkist_processing_common/tests/test_base.py,sha256=EQsIkeWoOtjk0yxr_oPkhW3Uc0p8cMsknSMwKgrJI9E,7078
82
82
  dkist_processing_common/tests/test_codecs.py,sha256=XuvG1sG8DECMPmxtDEi98TxlvTSAy0vrtUUFLrwnHlA,22173
83
- dkist_processing_common/tests/test_constants.py,sha256=Kc9k5TdYy5QkRRlGav6kfI2dy5HHKqtpf9qOuaAfDZU,5903
83
+ dkist_processing_common/tests/test_constants.py,sha256=I_KcJs7ScCn53GYhEO6qjWrrnfZuyC1IVYOy87Pjlg4,6565
84
84
  dkist_processing_common/tests/test_cs_step.py,sha256=RA0QD3D8eaL3YSOL_gIJ9wkngy14RQ2jbD-05KAziW4,2408
85
85
  dkist_processing_common/tests/test_dkist_location.py,sha256=-_OoSw4SZDLFyIuOltHvM6PQjxm5hTiJQsiTGZ8Sadc,456
86
- dkist_processing_common/tests/test_fits_access.py,sha256=tyUPNbUqoTPhQgzua_doWP9l9ee4ir_LLV-I9rHktcs,11393
86
+ dkist_processing_common/tests/test_fits_access.py,sha256=IYLrGRq71ve_yKJA6N-Gxz2-r9iISExwM8Zt6bB-jJI,13832
87
87
  dkist_processing_common/tests/test_flower_pot.py,sha256=X9_UI3maa3ZQncV3jYHgovWnawDsdEkEB5vw6EAB96o,3151
88
88
  dkist_processing_common/tests/test_fried_parameter.py,sha256=iXtlQIifZ6cDOkEi-YDgP3oAlss2loq08Uohgvy1byQ,1295
89
89
  dkist_processing_common/tests/test_input_dataset.py,sha256=pQ01rWAkQ2XQojyHWzAqeOdrMXshNcgEVL5I_9bBTdo,9610
@@ -92,16 +92,16 @@ dkist_processing_common/tests/test_interservice_bus_mixin.py,sha256=IptJkW7Qeu2Y
92
92
  dkist_processing_common/tests/test_manual_processing.py,sha256=iHF7yQPlar9niYAGXtFv28Gw3Undlds38yMfszk4ccY,1037
93
93
  dkist_processing_common/tests/test_output_data_base.py,sha256=D8b1XKvbE3C5cGOiHq58yJ2pzQL3iL0wLZy_AkDdB9Y,3085
94
94
  dkist_processing_common/tests/test_parameters.py,sha256=CUEUIGBPMCUXPll0G0UxFDbMXi8lmnjRwXBarGX1PAQ,14033
95
- dkist_processing_common/tests/test_parse_l0_input_data.py,sha256=ufhn-wXk9gAag6a99f7bXUvqFdlgOKmT2Dm5qlCU9Ag,10630
95
+ dkist_processing_common/tests/test_parse_l0_input_data.py,sha256=rwmtQJQo7SNdzPVzMOFxXS35H_nOLWYgbZsc9QuMEgc,10503
96
96
  dkist_processing_common/tests/test_publish_catalog_messages.py,sha256=l6Wga1s2wNBIf4wGZ78ZIO_rtqjdidmtvlN9nMnQUAs,3222
97
97
  dkist_processing_common/tests/test_quality.py,sha256=IPz7liXcmoqWIsY78oX07Ui0nWHxoUH2FbKGEmMle7E,10258
98
98
  dkist_processing_common/tests/test_quality_mixin.py,sha256=qanm3SXEiLb0OJDawbh8ixVAG9uRglFMzwxTeYxkDsM,55369
99
99
  dkist_processing_common/tests/test_scratch.py,sha256=WO8C1VJlkcC5IzST9Hj08CyyrINwYcN8pyteD9x38xs,16482
100
- dkist_processing_common/tests/test_stems.py,sha256=4BWY5lMfCdDOiSvK7EobmhX6gHJupO6B1ci6dqyyWcQ,32649
100
+ dkist_processing_common/tests/test_stems.py,sha256=8Im6TtEEwkU6MN5wAAbq2gL4Z5bCQp-rbVH_QEdc880,32316
101
101
  dkist_processing_common/tests/test_submit_dataset_metadata.py,sha256=LHEyjoIxJHXXssqKkr8Qn1NzzHD1FLJiD3lP8yaLiXU,3764
102
102
  dkist_processing_common/tests/test_tags.py,sha256=w5gmVfp3Ck92KNV80lJQRMz0OYgTYzWtwVUFWv1b5i8,5024
103
103
  dkist_processing_common/tests/test_task_name.py,sha256=kqFr59XX2K87xzfTlClzDV4-Je1dx72LvdaJ22UE8UU,1233
104
- dkist_processing_common/tests/test_task_parsing.py,sha256=QXt1X6DTO3_liBD2c-t84DToLeEn7B3J-eteIyN4HEM,4027
104
+ dkist_processing_common/tests/test_task_parsing.py,sha256=leXQrIWU5CIkUWRaiG0hW4NCmFtlpxHNsgl4Jz2EghA,4368
105
105
  dkist_processing_common/tests/test_teardown.py,sha256=DaliHSGsiQBZaFkf5wb3XBo6rHNPmx2bmQtVymYeBN4,5601
106
106
  dkist_processing_common/tests/test_transfer_input_data.py,sha256=eyAAWXpTHQ8aew87-MncWpYBn4DAZrTSOL3LvlQfR5Q,12611
107
107
  dkist_processing_common/tests/test_transfer_l1_output_data.py,sha256=PVGDJBEUk4kAeu8ivrhlCE7yd29R18t9kZLFx-mpBwY,2063
@@ -117,7 +117,7 @@ docs/landing_page.rst,sha256=aPAuXFhBx73lEZ59B6E6JXxkK0LlxzD0n-HXqHrfumQ,746
117
117
  docs/make.bat,sha256=mBAhtURwhQ7yc95pqwJzlhqBSvRknr1aqZ5s8NKvdKs,4513
118
118
  docs/requirements.txt,sha256=Kbl_X4c7RQZw035YTeNB63We6I7pvXFU4T0Uflp2yDY,29
119
119
  licenses/LICENSE.rst,sha256=piZaQplkzOMmH1NXg6QIdo9wwo9pPCoHkvm2-DmH76E,1462
120
- dkist_processing_common-11.4.0rc1.dist-info/METADATA,sha256=3GwCMs5Qw11nvxhrzdOIEIf07F8ZIQ3_Q5Rs-WW0nBI,7207
121
- dkist_processing_common-11.4.0rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
122
- dkist_processing_common-11.4.0rc1.dist-info/top_level.txt,sha256=LJhd1W-Vn90K8HnQDIE4r52YDpUjjMWDnllAWHBByW0,48
123
- dkist_processing_common-11.4.0rc1.dist-info/RECORD,,
120
+ dkist_processing_common-11.5.0rc1.dist-info/METADATA,sha256=8FTiTtNr9N0SSWAkAuQTWRChbk0UUh-jW0KoO1xWGTQ,7207
121
+ dkist_processing_common-11.5.0rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
122
+ dkist_processing_common-11.5.0rc1.dist-info/top_level.txt,sha256=LJhd1W-Vn90K8HnQDIE4r52YDpUjjMWDnllAWHBByW0,48
123
+ dkist_processing_common-11.5.0rc1.dist-info/RECORD,,