dkist-processing-common 11.2.1rc3__py3-none-any.whl → 11.3.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. changelog/265.feature.1.rst +3 -0
  2. changelog/265.feature.rst +4 -0
  3. changelog/265.misc.rst +2 -0
  4. dkist_processing_common/__init__.py +1 -0
  5. dkist_processing_common/_util/constants.py +1 -0
  6. dkist_processing_common/_util/graphql.py +1 -0
  7. dkist_processing_common/_util/scratch.py +1 -1
  8. dkist_processing_common/_util/tags.py +1 -0
  9. dkist_processing_common/codecs/array.py +1 -0
  10. dkist_processing_common/codecs/asdf.py +1 -0
  11. dkist_processing_common/codecs/basemodel.py +1 -0
  12. dkist_processing_common/codecs/bytes.py +1 -0
  13. dkist_processing_common/codecs/fits.py +1 -0
  14. dkist_processing_common/codecs/iobase.py +1 -0
  15. dkist_processing_common/codecs/json.py +1 -0
  16. dkist_processing_common/codecs/path.py +1 -0
  17. dkist_processing_common/codecs/quality.py +1 -1
  18. dkist_processing_common/codecs/str.py +1 -0
  19. dkist_processing_common/config.py +1 -0
  20. dkist_processing_common/manual.py +1 -1
  21. dkist_processing_common/models/constants.py +43 -28
  22. dkist_processing_common/models/dkist_location.py +1 -1
  23. dkist_processing_common/models/fits_access.py +57 -0
  24. dkist_processing_common/models/flower_pot.py +1 -0
  25. dkist_processing_common/models/graphql.py +2 -1
  26. dkist_processing_common/models/input_dataset.py +3 -2
  27. dkist_processing_common/models/message.py +1 -1
  28. dkist_processing_common/models/message_queue_binding.py +1 -1
  29. dkist_processing_common/models/metric_code.py +1 -0
  30. dkist_processing_common/models/parameters.py +1 -1
  31. dkist_processing_common/models/quality.py +1 -0
  32. dkist_processing_common/models/tags.py +3 -1
  33. dkist_processing_common/models/task_name.py +3 -2
  34. dkist_processing_common/models/wavelength.py +2 -1
  35. dkist_processing_common/parsers/cs_step.py +3 -2
  36. dkist_processing_common/parsers/dsps_repeat.py +6 -4
  37. dkist_processing_common/parsers/experiment_id_bud.py +6 -2
  38. dkist_processing_common/parsers/id_bud.py +7 -3
  39. dkist_processing_common/parsers/l0_fits_access.py +5 -3
  40. dkist_processing_common/parsers/l1_fits_access.py +23 -21
  41. dkist_processing_common/parsers/near_bud.py +6 -2
  42. dkist_processing_common/parsers/proposal_id_bud.py +4 -2
  43. dkist_processing_common/parsers/quality.py +1 -0
  44. dkist_processing_common/parsers/retarder.py +5 -3
  45. dkist_processing_common/parsers/single_value_single_key_flower.py +7 -1
  46. dkist_processing_common/parsers/task.py +8 -6
  47. dkist_processing_common/parsers/time.py +20 -15
  48. dkist_processing_common/parsers/unique_bud.py +6 -2
  49. dkist_processing_common/parsers/wavelength.py +5 -3
  50. dkist_processing_common/tasks/__init__.py +3 -2
  51. dkist_processing_common/tasks/assemble_movie.py +1 -0
  52. dkist_processing_common/tasks/base.py +1 -0
  53. dkist_processing_common/tasks/l1_output_data.py +1 -1
  54. dkist_processing_common/tasks/mixin/globus.py +1 -1
  55. dkist_processing_common/tasks/mixin/interservice_bus.py +1 -0
  56. dkist_processing_common/tasks/mixin/metadata_store.py +1 -1
  57. dkist_processing_common/tasks/mixin/object_store.py +1 -0
  58. dkist_processing_common/tasks/mixin/quality/__init__.py +1 -0
  59. dkist_processing_common/tasks/mixin/quality/_base.py +1 -0
  60. dkist_processing_common/tasks/mixin/quality/_metrics.py +1 -0
  61. dkist_processing_common/tasks/output_data_base.py +1 -0
  62. dkist_processing_common/tasks/parse_l0_input_data.py +6 -4
  63. dkist_processing_common/tasks/quality_metrics.py +1 -1
  64. dkist_processing_common/tasks/teardown.py +1 -1
  65. dkist_processing_common/tasks/transfer_input_data.py +1 -1
  66. dkist_processing_common/tasks/trial_catalog.py +3 -2
  67. dkist_processing_common/tasks/trial_output_data.py +1 -0
  68. dkist_processing_common/tasks/write_l1.py +19 -8
  69. dkist_processing_common/tests/conftest.py +1 -0
  70. dkist_processing_common/tests/mock_metadata_store.py +2 -3
  71. dkist_processing_common/tests/test_assemble_movie.py +0 -1
  72. dkist_processing_common/tests/test_codecs.py +2 -2
  73. dkist_processing_common/tests/test_constants.py +15 -0
  74. dkist_processing_common/tests/test_fits_access.py +62 -7
  75. dkist_processing_common/tests/test_interservice_bus.py +1 -0
  76. dkist_processing_common/tests/test_interservice_bus_mixin.py +1 -0
  77. dkist_processing_common/tests/test_manual_processing.py +1 -2
  78. dkist_processing_common/tests/test_output_data_base.py +1 -2
  79. dkist_processing_common/tests/test_parameters.py +1 -1
  80. dkist_processing_common/tests/test_parse_l0_input_data.py +23 -24
  81. dkist_processing_common/tests/test_quality.py +1 -0
  82. dkist_processing_common/tests/test_scratch.py +2 -1
  83. dkist_processing_common/tests/test_stems.py +31 -22
  84. dkist_processing_common/tests/test_tags.py +1 -0
  85. dkist_processing_common/tests/test_task_parsing.py +17 -7
  86. dkist_processing_common/tests/test_teardown.py +1 -1
  87. dkist_processing_common/tests/test_transfer_input_data.py +2 -3
  88. dkist_processing_common/tests/test_trial_catalog.py +1 -0
  89. dkist_processing_common/tests/test_trial_output_data.py +1 -1
  90. dkist_processing_common/tests/test_workflow_task_base.py +1 -2
  91. dkist_processing_common/tests/test_write_l1.py +8 -10
  92. {dkist_processing_common-11.2.1rc3.dist-info → dkist_processing_common-11.3.1rc1.dist-info}/METADATA +2 -2
  93. dkist_processing_common-11.3.1rc1.dist-info/RECORD +125 -0
  94. docs/conf.py +1 -0
  95. changelog/262.misc.rst +0 -1
  96. dkist_processing_common-11.2.1rc3.dist-info/RECORD +0 -123
  97. {dkist_processing_common-11.2.1rc3.dist-info → dkist_processing_common-11.3.1rc1.dist-info}/WHEEL +0 -0
  98. {dkist_processing_common-11.2.1rc3.dist-info → dkist_processing_common-11.3.1rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ Add `_set_metadata_key_value` method to FitsAccessBase that takes a StrEnum and sets
2
+ a class attribute. The attribute name is the StrEnum name and the attribute value
3
+ gets the header value with the StrEnum value as key.
@@ -0,0 +1,4 @@
1
+ Create new MetadataKey string enum class. Replace usages like `metadata_key="keyword_name"`
2
+ with `metadata_key=MetadataKey.keyword_name`. Note that unlike other enum classes, we are using
3
+ the name, not the value. Stem bases now check if they are passed a MetadataKey, and take
4
+ the name as the required string.
changelog/265.misc.rst ADDED
@@ -0,0 +1,2 @@
1
+ Change classes that inherit from `str` and `Enum` to inherit `StrEnum` instead: BudName, StemName, TaskName. Convert
2
+ strings to class members where relevant.
@@ -1,4 +1,5 @@
1
1
  """Package providing support classes and methods used by all workflow tasks."""
2
+
2
3
  from importlib.metadata import PackageNotFoundError
3
4
  from importlib.metadata import version
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Wrapper for interactions with shared database that holds arbitrary data that persists across the entire recipe run."""
2
+
2
3
  import json
3
4
  from collections.abc import MutableMapping
4
5
  from enum import Enum
@@ -1,4 +1,5 @@
1
1
  """Extension of the GraphQL supporting retries for data processing use cases."""
2
+
2
3
  import logging
3
4
  from typing import Any
4
5
  from typing import Callable
@@ -1,4 +1,5 @@
1
1
  """Scratch file system api."""
2
+
2
3
  import logging
3
4
  from contextlib import contextmanager
4
5
  from os import umask
@@ -9,7 +10,6 @@ from typing import Generator
9
10
  from dkist_processing_common._util.tags import TagDB
10
11
  from dkist_processing_common.config import common_configurations
11
12
 
12
-
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
 
@@ -1,4 +1,5 @@
1
1
  """Tag cloud manager."""
2
+
2
3
  from pathlib import Path
3
4
  from typing import Iterable
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoder for writing/reading numpy arrays."""
2
+
2
3
  import io
3
4
  from pathlib import Path
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Encoders and decoders for writing and reading ASDF files."""
2
+
2
3
  from io import BytesIO
3
4
  from pathlib import Path
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoder for writing and reading Pydantic BaseModel objects."""
2
+
2
3
  from pathlib import Path
3
4
  from typing import Type
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoder for writing and reading bytes objects."""
2
+
2
3
  from pathlib import Path
3
4
 
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Encoders and decoders for writing and reading FITS files."""
2
+
2
3
  from io import BytesIO
3
4
  from pathlib import Path
4
5
  from typing import Type
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoder for writing and reading IOBase binary (i.e., not Text) objects."""
2
+
2
3
  from io import BytesIO
3
4
  from io import IOBase
4
5
  from io import TextIOBase
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoders for writing and reading JSON files."""
2
+
2
3
  import json
3
4
  from pathlib import Path
4
5
  from typing import Any
@@ -1,4 +1,5 @@
1
1
  """Default decoder to pass through paths from `read`."""
2
+
2
3
  from pathlib import Path
3
4
 
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoders for writing and reading quality data."""
2
+
2
3
  import json
3
4
  import logging
4
5
  from datetime import datetime
@@ -10,7 +11,6 @@ import numpy as np
10
11
  from dkist_processing_common.codecs.json import json_decoder
11
12
  from dkist_processing_common.codecs.json import json_encoder
12
13
 
13
-
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
16
 
@@ -1,4 +1,5 @@
1
1
  """Encoder/decoder for writing and reading str to files."""
2
+
2
3
  from pathlib import Path
3
4
 
4
5
  from dkist_processing_common.codecs.bytes import bytes_decoder
@@ -1,4 +1,5 @@
1
1
  """Common configurations."""
2
+
2
3
  from dkist_processing_core.config import DKISTProcessingCoreConfiguration
3
4
  from dkist_service_configuration.settings import DEFAULT_MESH_SERVICE
4
5
  from dkist_service_configuration.settings import MeshService
@@ -1,4 +1,5 @@
1
1
  """Task wrapper for manual execution outside the workflow engine."""
2
+
2
3
  import json
3
4
  import logging
4
5
  import shutil
@@ -14,7 +15,6 @@ from dkist_processing_common.models.tags import Tag
14
15
  from dkist_processing_common.tasks.base import WorkflowTaskBase
15
16
  from dkist_processing_common.tests.mock_metadata_store import fake_gql_client_factory
16
17
 
17
-
18
18
  logger = logging.getLogger(__name__)
19
19
 
20
20
 
@@ -4,7 +4,8 @@ Components of the Constant model.
4
4
  Contains names of database entries and Base class for an object that simplifies
5
5
  accessing the database (tab completion, etc.)
6
6
  """
7
- from enum import Enum
7
+
8
+ from enum import StrEnum
8
9
  from string import ascii_uppercase
9
10
 
10
11
  from sqids import Sqids
@@ -12,13 +13,13 @@ from sqids import Sqids
12
13
  from dkist_processing_common._util.constants import ConstantsDb
13
14
 
14
15
 
15
- class BudName(str, Enum):
16
+ class BudName(StrEnum):
16
17
  """Controlled list of names for constant stems (buds)."""
17
18
 
18
- recipe_run_id = "RECIPE_RUN_ID"
19
19
  instrument = "INSTRUMENT"
20
20
  num_cs_steps = "NUM_CS_STEPS"
21
21
  num_modstates = "NUM_MODSTATES"
22
+ retarder_name = "RETARDER_NAME"
22
23
  proposal_id = "PROPOSAL_ID"
23
24
  contributing_proposal_ids = "CONTRIBUTING_PROPOSAL_IDS"
24
25
  experiment_id = "EXPERIMENT_ID"
@@ -32,7 +33,6 @@ class BudName(str, Enum):
32
33
  dark_exposure_times = "DARK_EXPOSURE_TIMES"
33
34
  dark_readout_exp_times = "DARK_READOUT_EXP_TIMES"
34
35
  wavelength = "WAVELENGTH"
35
- retarder_name = "RETARDER_NAME"
36
36
 
37
37
 
38
38
  class ConstantsBase:
@@ -62,7 +62,7 @@ class ConstantsBase:
62
62
  Parameters
63
63
  ----------
64
64
  recipe_run_id
65
- Thew recipe_run_id
65
+ The recipe_run_id
66
66
  task_name
67
67
  The task_name
68
68
  """
@@ -90,6 +90,31 @@ class ConstantsBase:
90
90
  """Define the dataset_id constant."""
91
91
  return Sqids(min_length=6, alphabet=ascii_uppercase).encode([self._recipe_run_id])
92
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
+
93
118
  @property
94
119
  def proposal_id(self) -> str:
95
120
  """Get the proposal_id constant."""
@@ -104,14 +129,22 @@ class ConstantsBase:
104
129
  return proposal_ids
105
130
 
106
131
  @property
107
- def instrument(self) -> str:
108
- """Get the instrument name."""
109
- 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
110
143
 
111
144
  @property
112
145
  def obs_ip_start_time(self) -> str:
113
146
  """Return the start time of the observe IP."""
114
- return self._db_dict[BudName.obs_ip_start_time.value]
147
+ return self._db_dict[BudName.obs_ip_start_time]
115
148
 
116
149
  @property
117
150
  def average_cadence(self) -> float:
@@ -148,25 +181,7 @@ class ConstantsBase:
148
181
  """Get a list of readout exp times for all DARK frames."""
149
182
  return self._db_dict[BudName.dark_readout_exp_times]
150
183
 
151
- @property
152
- def stokes_params(self) -> [str]:
153
- """Return the list of stokes parameter names."""
154
- return ["I", "Q", "U", "V"]
155
-
156
- @property
157
- def experiment_id(self) -> str:
158
- """Get the experiment_id constant."""
159
- return self._db_dict[BudName.experiment_id]
160
-
161
- @property
162
- def contributing_experiment_ids(self) -> [str]:
163
- """Return the list of contributing experiment IDs."""
164
- experiment_ids = self._db_dict[BudName.contributing_experiment_ids]
165
- if isinstance(experiment_ids, str):
166
- return [experiment_ids]
167
- return experiment_ids
168
-
169
184
  @property
170
185
  def wavelength(self) -> float:
171
186
  """Wavelength."""
172
- return self._db_dict[BudName.wavelength.value]
187
+ return self._db_dict[BudName.wavelength]
@@ -3,10 +3,10 @@
3
3
  Cartesian geocentric coordinates of DKIST on Earth as retrieved from
4
4
  https://github.com/astropy/astropy-data/blob/gh-pages/coordinates/sites.json#L838
5
5
  """
6
+
6
7
  import astropy.units as u
7
8
  from astropy.coordinates import EarthLocation
8
9
 
9
-
10
10
  _dkist_site_info = {
11
11
  "aliases": ["DKIST", "ATST"],
12
12
  "name": "Daniel K. Inouye Solar Telescope",
@@ -1,11 +1,45 @@
1
1
  """Abstraction layer for accessing fits data via class attributes."""
2
+
2
3
  from __future__ import annotations
3
4
 
5
+ from enum import StrEnum
4
6
  from pathlib import Path
7
+ from typing import Any
5
8
 
6
9
  import numpy as np
7
10
  from astropy.io import fits
8
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
+
9
43
 
10
44
  class FitsAccessBase:
11
45
  """
@@ -34,6 +68,29 @@ class FitsAccessBase:
34
68
  def __repr__(self):
35
69
  return f"{self.__class__.__name__}(hdu={self._hdu!r}, name={self.name!r}, auto_squeeze={self.auto_squeeze})"
36
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
+
37
94
  @property
38
95
  def data(self) -> np.ndarray:
39
96
  """
@@ -6,6 +6,7 @@ Defines:
6
6
 
7
7
  FlowerPot -> Container for Stem children (Flowers)
8
8
  """
9
+
9
10
  from __future__ import annotations
10
11
 
11
12
  from abc import ABC
@@ -1,9 +1,10 @@
1
1
  """GraphQL Data models for the metadata store api."""
2
+
2
3
  from pydantic import BaseModel
3
4
  from pydantic import ConfigDict
5
+ from pydantic import Json
4
6
  from pydantic import field_serializer
5
7
  from pydantic import field_validator
6
- from pydantic import Json
7
8
 
8
9
  from dkist_processing_common.models.input_dataset import InputDatasetBaseModel
9
10
  from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
@@ -1,4 +1,5 @@
1
1
  """Input dataset models for the inputDatasetPartDocument from the metadata store api."""
2
+
2
3
  import json
3
4
  from datetime import datetime
4
5
  from typing import Any
@@ -6,10 +7,10 @@ from typing import Any
6
7
  from pydantic import BaseModel
7
8
  from pydantic import ConfigDict
8
9
  from pydantic import Field
9
- from pydantic import field_serializer
10
- from pydantic import field_validator
11
10
  from pydantic import Json
12
11
  from pydantic import PlainSerializer
12
+ from pydantic import field_serializer
13
+ from pydantic import field_validator
13
14
  from pydantic.alias_generators import to_camel
14
15
  from typing_extensions import Annotated
15
16
 
@@ -1,11 +1,11 @@
1
1
  """Data structures for messages placed on the interservice bus."""
2
+
2
3
  from typing import Type
3
4
 
4
5
  from pydantic import Field
5
6
  from talus import MessageBodyBase
6
7
  from talus import PublishMessageBase
7
8
 
8
-
9
9
  ########################
10
10
  # Message Body Schemas #
11
11
  ########################
@@ -1,4 +1,5 @@
1
1
  """Binding between a queue and a message to be published."""
2
+
2
3
  from talus import Binding
3
4
  from talus import Queue
4
5
 
@@ -7,7 +8,6 @@ from dkist_processing_common.models.message import CatalogFrameMessage
7
8
  from dkist_processing_common.models.message import CatalogObjectMessage
8
9
  from dkist_processing_common.models.message import CreateQualityReportMessage
9
10
 
10
-
11
11
  catalog_frame_queue = Queue(
12
12
  name="catalog.frame.q", arguments=common_configurations.isb_queue_arguments
13
13
  )
@@ -1,4 +1,5 @@
1
1
  """Controlled list of quality metric codes."""
2
+
2
3
  from enum import StrEnum
3
4
 
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Base class for parameter-parsing object."""
2
+
2
3
  import logging
3
4
  from contextlib import contextmanager
4
5
  from datetime import datetime
@@ -18,7 +19,6 @@ from dkist_processing_common.models.input_dataset import InputDatasetFilePointer
18
19
  from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
19
20
  from dkist_processing_common.models.tags import Tag
20
21
 
21
-
22
22
  logger = logging.getLogger(__name__)
23
23
 
24
24
 
@@ -1,4 +1,5 @@
1
1
  """Support classes used to create a quality report."""
2
+
2
3
  from typing import Any
3
4
 
4
5
  from pydantic import BaseModel
@@ -1,5 +1,7 @@
1
1
  """Components of the Tag model. Stem + Optional Suffix = Tag."""
2
+
2
3
  from enum import Enum
4
+ from enum import StrEnum
3
5
 
4
6
  from dkist_processing_common.models.task_name import TaskName
5
7
 
@@ -7,7 +9,7 @@ from dkist_processing_common.models.task_name import TaskName
7
9
  EXP_TIME_ROUND_DIGITS: int = 6
8
10
 
9
11
 
10
- class StemName(str, Enum):
12
+ class StemName(StrEnum):
11
13
  """Controlled list of Tag Stems."""
12
14
 
13
15
  output = "OUTPUT"
@@ -1,8 +1,9 @@
1
1
  """Controlled list of common IP task tag names."""
2
- from enum import Enum
3
2
 
3
+ from enum import StrEnum
4
4
 
5
- class TaskName(str, Enum):
5
+
6
+ class TaskName(StrEnum):
6
7
  """Controlled list of task tag names."""
7
8
 
8
9
  observe = "OBSERVE"
@@ -1,10 +1,11 @@
1
1
  """Support classes for manipulating wavelengths."""
2
+
2
3
  import astropy.units as u
3
4
  from pydantic import BaseModel
4
5
  from pydantic import ConfigDict
6
+ from pydantic import ValidationInfo
5
7
  from pydantic import field_validator
6
8
  from pydantic import model_validator
7
- from pydantic import ValidationInfo
8
9
 
9
10
 
10
11
  class WavelengthRange(BaseModel):
@@ -1,4 +1,5 @@
1
1
  """Classes supporting Calibration Sequence steps."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from datetime import datetime
@@ -100,7 +101,7 @@ class CSStepFlower(Stem):
100
101
  """
101
102
 
102
103
  def __init__(self, max_cs_step_time_sec: float):
103
- super().__init__(stem_name=StemName.cs_step.value)
104
+ super().__init__(stem_name=StemName.cs_step)
104
105
  self.max_cs_step_time_sec = max_cs_step_time_sec
105
106
 
106
107
  def setter(self, fits_obj: L0FitsAccess) -> CSStep | Type[SpilledDirt]:
@@ -147,7 +148,7 @@ class NumCSStepBud(Stem):
147
148
  """
148
149
 
149
150
  def __init__(self, max_cs_step_time_sec: float):
150
- super().__init__(stem_name=BudName.num_cs_steps.value)
151
+ super().__init__(stem_name=BudName.num_cs_steps)
151
152
  self.max_cs_step_time_sec = max_cs_step_time_sec
152
153
 
153
154
  def setter(self, fits_obj: L0FitsAccess) -> CSStep | Type[SpilledDirt]:
@@ -1,5 +1,7 @@
1
1
  """Classes supporting the Data Set Parameters Set (DSPS) Repeat parameter."""
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.flower_pot import SpilledDirt
4
6
  from dkist_processing_common.models.tags import StemName
5
7
  from dkist_processing_common.models.task_name import TaskName
@@ -15,9 +17,9 @@ class TotalDspsRepeatsBud(TaskUniqueBud):
15
17
 
16
18
  def __init__(self):
17
19
  super().__init__(
18
- constant_name=BudName.num_dsps_repeats.value,
19
- metadata_key="num_dsps_repeats",
20
- 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,
21
23
  )
22
24
 
23
25
 
@@ -26,7 +28,7 @@ class DspsRepeatNumberFlower(SingleValueSingleKeyFlower):
26
28
 
27
29
  def __init__(self):
28
30
  super().__init__(
29
- 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
30
32
  )
31
33
 
32
34
  def setter(self, fits_obj: L0FitsAccess):
@@ -1,5 +1,7 @@
1
1
  """Experiment Id parser."""
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.parsers.id_bud import ContributingIdsBud
4
6
  from dkist_processing_common.parsers.id_bud import IdBud
5
7
 
@@ -8,7 +10,9 @@ class ExperimentIdBud(IdBud):
8
10
  """Class to create a Bud for the experiment_id."""
9
11
 
10
12
  def __init__(self):
11
- 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
+ )
12
16
 
13
17
 
14
18
  class ContributingExperimentIdsBud(ContributingIdsBud):
@@ -16,5 +20,5 @@ class ContributingExperimentIdsBud(ContributingIdsBud):
16
20
 
17
21
  def __init__(self):
18
22
  super().__init__(
19
- stem_name=BudName.contributing_experiment_ids.value, metadata_key="experiment_id"
23
+ stem_name=BudName.contributing_experiment_ids, metadata_key=MetadataKey.experiment_id
20
24
  )
@@ -1,4 +1,6 @@
1
1
  """Base classes for ID bud parsing."""
2
+
3
+ from enum import StrEnum
2
4
  from typing import Type
3
5
 
4
6
  from dkist_processing_common.models.flower_pot import SpilledDirt
@@ -11,19 +13,21 @@ from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
11
13
  class IdBud(TaskUniqueBud):
12
14
  """Base class for ID buds."""
13
15
 
14
- def __init__(self, constant_name, metadata_key):
16
+ def __init__(self, constant_name: str, metadata_key: str | StrEnum):
15
17
  super().__init__(
16
18
  constant_name=constant_name,
17
19
  metadata_key=metadata_key,
18
- ip_task_types=TaskName.observe.value,
20
+ ip_task_types=TaskName.observe,
19
21
  )
20
22
 
21
23
 
22
24
  class ContributingIdsBud(Stem):
23
25
  """Base class for contributing ID buds."""
24
26
 
25
- def __init__(self, stem_name, metadata_key):
27
+ def __init__(self, stem_name: str, metadata_key: str | StrEnum):
26
28
  super().__init__(stem_name=stem_name)
29
+ if isinstance(metadata_key, StrEnum):
30
+ metadata_key = metadata_key.name
27
31
  self.metadata_key = metadata_key
28
32
 
29
33
  def setter(self, fits_obj: L0FitsAccess) -> str | Type[SpilledDirt]:
@@ -1,6 +1,8 @@
1
1
  """By-frame 214 L0 header keywords that are not instrument specific."""
2
+
2
3
  from astropy.io import fits
3
4
 
5
+ from dkist_processing_common.models.fits_access import MetadataKey
4
6
  from dkist_processing_common.parsers.l1_fits_access import L1FitsAccess
5
7
 
6
8
 
@@ -25,6 +27,6 @@ class L0FitsAccess(L1FitsAccess):
25
27
  auto_squeeze: bool = True,
26
28
  ):
27
29
  super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
28
- self.ip_task_type: str = self.header["IPTASK"]
29
- self.ip_start_time: str = self.header["DKIST011"]
30
- 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)