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.
Files changed (122) hide show
  1. changelog/280.misc.rst +1 -0
  2. changelog/282.feature.2.rst +2 -0
  3. changelog/282.feature.rst +2 -0
  4. changelog/284.feature.rst +1 -0
  5. changelog/285.feature.rst +2 -0
  6. changelog/285.misc.rst +2 -0
  7. changelog/286.feature.rst +2 -0
  8. changelog/287.misc.rst +1 -0
  9. dkist_processing_common/__init__.py +1 -0
  10. dkist_processing_common/_util/constants.py +1 -0
  11. dkist_processing_common/_util/graphql.py +1 -0
  12. dkist_processing_common/_util/scratch.py +9 -9
  13. dkist_processing_common/_util/tags.py +1 -0
  14. dkist_processing_common/codecs/array.py +20 -0
  15. dkist_processing_common/codecs/asdf.py +9 -3
  16. dkist_processing_common/codecs/basemodel.py +22 -0
  17. dkist_processing_common/codecs/bytes.py +1 -0
  18. dkist_processing_common/codecs/fits.py +37 -9
  19. dkist_processing_common/codecs/iobase.py +1 -0
  20. dkist_processing_common/codecs/json.py +1 -0
  21. dkist_processing_common/codecs/path.py +1 -0
  22. dkist_processing_common/codecs/quality.py +1 -1
  23. dkist_processing_common/codecs/str.py +1 -0
  24. dkist_processing_common/config.py +64 -25
  25. dkist_processing_common/manual.py +6 -8
  26. dkist_processing_common/models/constants.py +373 -37
  27. dkist_processing_common/models/dkist_location.py +27 -0
  28. dkist_processing_common/models/fits_access.py +48 -0
  29. dkist_processing_common/models/flower_pot.py +231 -9
  30. dkist_processing_common/models/fried_parameter.py +41 -0
  31. dkist_processing_common/models/graphql.py +66 -75
  32. dkist_processing_common/models/input_dataset.py +117 -0
  33. dkist_processing_common/models/message.py +1 -1
  34. dkist_processing_common/models/message_queue_binding.py +1 -1
  35. dkist_processing_common/models/metric_code.py +2 -0
  36. dkist_processing_common/models/parameters.py +65 -28
  37. dkist_processing_common/models/quality.py +50 -5
  38. dkist_processing_common/models/tags.py +23 -21
  39. dkist_processing_common/models/task_name.py +3 -2
  40. dkist_processing_common/models/telemetry.py +28 -0
  41. dkist_processing_common/models/wavelength.py +3 -1
  42. dkist_processing_common/parsers/average_bud.py +46 -0
  43. dkist_processing_common/parsers/cs_step.py +13 -12
  44. dkist_processing_common/parsers/dsps_repeat.py +6 -4
  45. dkist_processing_common/parsers/experiment_id_bud.py +12 -4
  46. dkist_processing_common/parsers/id_bud.py +42 -27
  47. dkist_processing_common/parsers/l0_fits_access.py +5 -3
  48. dkist_processing_common/parsers/l1_fits_access.py +51 -23
  49. dkist_processing_common/parsers/lookup_bud.py +125 -0
  50. dkist_processing_common/parsers/near_bud.py +21 -20
  51. dkist_processing_common/parsers/observing_program_id_bud.py +24 -0
  52. dkist_processing_common/parsers/proposal_id_bud.py +13 -5
  53. dkist_processing_common/parsers/quality.py +2 -0
  54. dkist_processing_common/parsers/retarder.py +32 -0
  55. dkist_processing_common/parsers/single_value_single_key_flower.py +6 -1
  56. dkist_processing_common/parsers/task.py +8 -6
  57. dkist_processing_common/parsers/time.py +178 -72
  58. dkist_processing_common/parsers/unique_bud.py +21 -22
  59. dkist_processing_common/parsers/wavelength.py +5 -3
  60. dkist_processing_common/tasks/__init__.py +3 -2
  61. dkist_processing_common/tasks/assemble_movie.py +4 -3
  62. dkist_processing_common/tasks/base.py +59 -60
  63. dkist_processing_common/tasks/l1_output_data.py +54 -53
  64. dkist_processing_common/tasks/mixin/globus.py +24 -27
  65. dkist_processing_common/tasks/mixin/interservice_bus.py +1 -0
  66. dkist_processing_common/tasks/mixin/metadata_store.py +108 -243
  67. dkist_processing_common/tasks/mixin/object_store.py +22 -0
  68. dkist_processing_common/tasks/mixin/quality/__init__.py +1 -0
  69. dkist_processing_common/tasks/mixin/quality/_base.py +8 -1
  70. dkist_processing_common/tasks/mixin/quality/_metrics.py +166 -14
  71. dkist_processing_common/tasks/output_data_base.py +4 -3
  72. dkist_processing_common/tasks/parse_l0_input_data.py +277 -15
  73. dkist_processing_common/tasks/quality_metrics.py +9 -9
  74. dkist_processing_common/tasks/teardown.py +7 -7
  75. dkist_processing_common/tasks/transfer_input_data.py +67 -69
  76. dkist_processing_common/tasks/trial_catalog.py +77 -17
  77. dkist_processing_common/tasks/trial_output_data.py +16 -17
  78. dkist_processing_common/tasks/write_l1.py +102 -72
  79. dkist_processing_common/tests/conftest.py +32 -173
  80. dkist_processing_common/tests/mock_metadata_store.py +271 -0
  81. dkist_processing_common/tests/test_assemble_movie.py +4 -4
  82. dkist_processing_common/tests/test_assemble_quality.py +32 -4
  83. dkist_processing_common/tests/test_base.py +5 -19
  84. dkist_processing_common/tests/test_codecs.py +103 -12
  85. dkist_processing_common/tests/test_constants.py +15 -0
  86. dkist_processing_common/tests/test_dkist_location.py +15 -0
  87. dkist_processing_common/tests/test_fits_access.py +56 -19
  88. dkist_processing_common/tests/test_flower_pot.py +147 -5
  89. dkist_processing_common/tests/test_fried_parameter.py +27 -0
  90. dkist_processing_common/tests/test_input_dataset.py +78 -361
  91. dkist_processing_common/tests/test_interservice_bus.py +1 -0
  92. dkist_processing_common/tests/test_interservice_bus_mixin.py +1 -1
  93. dkist_processing_common/tests/test_manual_processing.py +33 -0
  94. dkist_processing_common/tests/test_output_data_base.py +5 -7
  95. dkist_processing_common/tests/test_parameters.py +71 -22
  96. dkist_processing_common/tests/test_parse_l0_input_data.py +115 -32
  97. dkist_processing_common/tests/test_publish_catalog_messages.py +2 -24
  98. dkist_processing_common/tests/test_quality.py +1 -0
  99. dkist_processing_common/tests/test_quality_mixin.py +255 -23
  100. dkist_processing_common/tests/test_scratch.py +2 -1
  101. dkist_processing_common/tests/test_stems.py +511 -168
  102. dkist_processing_common/tests/test_submit_dataset_metadata.py +3 -7
  103. dkist_processing_common/tests/test_tags.py +1 -0
  104. dkist_processing_common/tests/test_task_name.py +1 -1
  105. dkist_processing_common/tests/test_task_parsing.py +17 -7
  106. dkist_processing_common/tests/test_teardown.py +28 -24
  107. dkist_processing_common/tests/test_transfer_input_data.py +270 -125
  108. dkist_processing_common/tests/test_transfer_l1_output_data.py +2 -3
  109. dkist_processing_common/tests/test_trial_catalog.py +83 -8
  110. dkist_processing_common/tests/test_trial_output_data.py +46 -73
  111. dkist_processing_common/tests/test_workflow_task_base.py +8 -10
  112. dkist_processing_common/tests/test_write_l1.py +298 -76
  113. dkist_processing_common-12.1.0rc1.dist-info/METADATA +265 -0
  114. dkist_processing_common-12.1.0rc1.dist-info/RECORD +134 -0
  115. {dkist_processing_common-10.5.4.dist-info → dkist_processing_common-12.1.0rc1.dist-info}/WHEEL +1 -1
  116. docs/conf.py +1 -0
  117. docs/index.rst +1 -1
  118. docs/landing_page.rst +13 -0
  119. dkist_processing_common/tasks/mixin/input_dataset.py +0 -166
  120. dkist_processing_common-10.5.4.dist-info/METADATA +0 -175
  121. dkist_processing_common-10.5.4.dist-info/RECORD +0 -112
  122. {dkist_processing_common-10.5.4.dist-info → dkist_processing_common-12.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,15 @@
1
+ import pytest
2
+ from astropy.coordinates import EarthLocation
3
+
4
+ from dkist_processing_common.models.dkist_location import location_of_dkist
5
+
6
+
7
+ @pytest.mark.flaky(max_reruns=10)
8
+ def test_location_of_dkist():
9
+ """
10
+ Given: function for retrieving the dkist location on earth
11
+ When: Call function
12
+ Then: result is the same as what is in the astropy online database
13
+ """
14
+ itrs = location_of_dkist
15
+ assert itrs == EarthLocation.of_site("dkist")
@@ -1,8 +1,11 @@
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
 
5
7
  from dkist_processing_common.models.fits_access import FitsAccessBase
8
+ from dkist_processing_common.models.fits_access import MetadataKey
6
9
  from dkist_processing_common.parsers.l0_fits_access import L0FitsAccess
7
10
  from dkist_processing_common.parsers.l1_fits_access import L1FitsAccess
8
11
 
@@ -22,19 +25,21 @@ def hdu_with_complete_common_header(complete_common_header):
22
25
  def hdu_with_special_keys(hdu_with_complete_common_header, request):
23
26
  """
24
27
  An HDU with data and a header that includes variations on the special keys that make headers
25
- different from dict's.
28
+ different from dicts.
29
+
30
+ The base complete_common_header already has history and comment cards so this fixture adds extras.
26
31
  """
27
32
  add_special = request.param
28
33
  hdu = hdu_with_complete_common_header
29
34
  if add_special == "history":
30
35
  hdu.header.add_history("test history")
31
- return hdu
36
+ return hdu, request.param
32
37
  if add_special == "comment":
33
- hdu.header.add_history("test comment")
34
- return hdu
38
+ hdu.header.add_comment("test comment")
39
+ return hdu, request.param
35
40
  if add_special == "continue":
36
41
  hdu.header["LONG_VAL"] = " " * 100
37
- return hdu
42
+ return hdu, request.param
38
43
 
39
44
 
40
45
  @pytest.fixture()
@@ -91,14 +96,15 @@ def hdu_with_no_data(complete_common_header):
91
96
 
92
97
 
93
98
  @pytest.fixture()
94
- def hdu_with_incomplete_common_header(tmp_path):
99
+ def hdu_with_incomplete_common_header(complete_common_header):
95
100
  """
96
- An HDU with data and a header missing one of the expected common by-frame keywords
101
+ An HDU with data and a header missing two of the expected common by-frame keywords
97
102
  """
103
+ incomplete_header = complete_common_header
104
+ incomplete_header.pop(MetadataKey.elevation)
105
+ incomplete_header.pop(MetadataKey.azimuth)
98
106
  data = np.arange(9).reshape(3, 3)
99
- hdu = fits.PrimaryHDU(data)
100
- hdu.header["TELEVATN"] = 6.28
101
- hdu.header["TAZIMUTH"] = 3.14
107
+ hdu = fits.PrimaryHDU(data, header=incomplete_header)
102
108
  return hdu
103
109
 
104
110
 
@@ -125,19 +131,45 @@ def fits_file_path_with_data_in_imagehdu(tmp_path, complete_common_header):
125
131
  return file_path
126
132
 
127
133
 
134
+ class MetadataKeyWithNaxisKeys(StrEnum):
135
+ naxis = "NAXIS"
136
+ naxis1 = "NAXIS1"
137
+ naxis2 = "NAXIS2"
138
+
139
+
128
140
  class FitsAccessWithNaxisKeys(FitsAccessBase):
129
141
  def __init__(self, hdu, name):
130
142
  super().__init__(hdu, name)
131
- self.naxis = self.header["NAXIS"]
132
- self.naxis1 = self.header["NAXIS1"]
133
- self.naxis2 = self.header["NAXIS2"]
143
+ self.naxis = self.header[MetadataKeyWithNaxisKeys.naxis]
144
+ self.naxis1 = self.header[MetadataKeyWithNaxisKeys.naxis1]
145
+ self.naxis2 = self.header[MetadataKeyWithNaxisKeys.naxis2]
146
+
147
+
148
+ def test_metadata_keys_in_access_bases(hdu_with_no_data):
149
+ """
150
+ Given: a set of metadata key names in the MetadataKey sting enumeration
151
+ When: the FITS access classes define a set of attributes/properties
152
+ Then: the sets are the same and the values of the attributes/properties are correct
153
+ """
154
+ all_metadata_key_names = {mk.name for mk in MetadataKey}
155
+ hdu = hdu_with_no_data
156
+ fits_obj = L0FitsAccess(hdu=hdu)
157
+ fits_access_defined_attributes = {
158
+ k for k, v in fits_obj.__dict__.items() if k not in ["_hdu", "name", "auto_squeeze"]
159
+ }
160
+ l0_access_properties = {k for k, v in L0FitsAccess.__dict__.items() if isinstance(v, property)}
161
+ l1_access_properties = {k for k, v in L1FitsAccess.__dict__.items() if isinstance(v, property)}
162
+ fits_access_properties = l0_access_properties | l1_access_properties
163
+ assert fits_access_defined_attributes | fits_access_properties == all_metadata_key_names
164
+ for key in fits_access_defined_attributes:
165
+ assert getattr(fits_obj, key) == fits_obj.header[MetadataKey[key]]
134
166
 
135
167
 
136
168
  def test_from_single_hdu(hdu_with_complete_common_header):
137
169
  """
138
170
  Given: an HDU with expected, common by-frame keywords
139
171
  When: loading the HDU with the L0FitsAccess class
140
- Then: all values for common keywords are exposed as properties on the fits_obj class
172
+ Then: values for common keywords are exposed as properties on the fits_obj class
141
173
  """
142
174
  fits_obj = L0FitsAccess(hdu_with_complete_common_header)
143
175
  assert fits_obj.elevation == 6.28
@@ -152,7 +184,7 @@ def test_l1_only_fits_access(hdu_with_complete_l1_only_header):
152
184
  """
153
185
  Given: an HDU with 214 L1-only headers
154
186
  When: loading the HDU with the L1FitsAccess class
155
- Then: no errors are raised and all values are exposed
187
+ Then: no errors are raised and values are exposed
156
188
  """
157
189
  fits_obj = L1FitsAccess(hdu_with_complete_l1_only_header)
158
190
  assert fits_obj.elevation == 6.28
@@ -220,7 +252,7 @@ def test_as_subclass(hdu_with_complete_common_header):
220
252
  class InstFitsAccess(L0FitsAccess):
221
253
  def __init__(self, hdu, name):
222
254
  super().__init__(hdu, name)
223
- self.foo: str = self.header["INST_FOO"]
255
+ self.foo: str = self.header["VISP_012"]
224
256
 
225
257
  fits_obj = InstFitsAccess(hdu_with_complete_common_header, name="foo")
226
258
  assert fits_obj.foo == "bar"
@@ -282,11 +314,16 @@ def test_header_dict(hdu_with_special_keys):
282
314
  """
283
315
  Given: A FitsAccess object with data and a header including special header keys
284
316
  When: Accessing the header_dict method
285
- Then: The object's header is successfully exported as a dict of the same length as the header
317
+ Then: The object's header is successfully exported as a dict of the same length as the header when accounting for multiple HISTORY or COMMENT cards
286
318
  """
287
- fits_obj = FitsAccessBase(hdu_with_special_keys)
319
+ hdu, added_header_key = hdu_with_special_keys
320
+ fits_obj = FitsAccessBase(hdu)
288
321
  assert isinstance(fits_obj.header_dict, dict)
289
- assert len(fits_obj.header_dict) == len(fits_obj.header)
322
+ if added_header_key == "continue":
323
+ assert len(fits_obj.header_dict) == len(fits_obj.header)
324
+ if added_header_key in ["history", "comment"]:
325
+ # HISTORY and COMMENT keys get "squeezed"
326
+ assert len(fits_obj.header_dict) == len(fits_obj.header) - 1
290
327
 
291
328
 
292
329
  @pytest.mark.parametrize(
@@ -4,11 +4,13 @@ from typing import Hashable
4
4
  import pytest
5
5
 
6
6
  from dkist_processing_common.models.flower_pot import FlowerPot
7
+ from dkist_processing_common.models.flower_pot import ListStem
8
+ from dkist_processing_common.models.flower_pot import SetStem
7
9
  from dkist_processing_common.models.flower_pot import SpilledDirt
8
10
  from dkist_processing_common.models.flower_pot import Stem
9
11
 
10
12
 
11
- @pytest.fixture()
13
+ @pytest.fixture
12
14
  def simple_flower():
13
15
  class Flower(Stem):
14
16
  def setter(self, value: Any) -> Any:
@@ -22,7 +24,7 @@ def simple_flower():
22
24
  return Flower(stem_name="simple_flower")
23
25
 
24
26
 
25
- @pytest.fixture()
27
+ @pytest.fixture
26
28
  def simple_flower_pot(simple_flower):
27
29
  flower_pot = FlowerPot()
28
30
  flower_pot.stems += [simple_flower]
@@ -30,16 +32,66 @@ def simple_flower_pot(simple_flower):
30
32
  return flower_pot
31
33
 
32
34
 
33
- @pytest.fixture()
35
+ @pytest.fixture
34
36
  def simple_key_values():
35
37
  return {f"thing{i}": i for i in range(5)}
36
38
 
37
39
 
40
+ @pytest.fixture
41
+ def stem_bud():
42
+ class Bud(Stem):
43
+ def setter(self, value: int) -> int:
44
+ return value % 3
45
+
46
+ def getter(self, key: str) -> int:
47
+ return len(set(self.key_to_petal_dict.values()))
48
+
49
+ return Bud(stem_name="StemBud")
50
+
51
+
52
+ @pytest.fixture
53
+ def setstem_bud():
54
+ # Computes the same result as `stem_bud`
55
+ class SetBud(SetStem):
56
+ def setter(self, value: int) -> int:
57
+ return value % 3
58
+
59
+ def getter(self) -> int:
60
+ return len(self.value_set)
61
+
62
+ return SetBud(stem_name="SetStemBud")
63
+
64
+
65
+ @pytest.fixture
66
+ def liststem_bud():
67
+ # Highlights the difference between using a `set` and a `list` in these more efficient buds
68
+ class ListBud(ListStem):
69
+ def setter(self, value: int) -> int:
70
+ return value % 3
71
+
72
+ def getter(self) -> int:
73
+ return len(self.value_list)
74
+
75
+ return ListBud(stem_name="ListStemBud")
76
+
77
+
78
+ @pytest.fixture
79
+ def simple_bud_pot(stem_bud, setstem_bud, liststem_bud):
80
+ bud_pot = FlowerPot()
81
+ bud_pot.stems += [stem_bud, liststem_bud, setstem_bud]
82
+ return bud_pot
83
+
84
+
85
+ @pytest.fixture
86
+ def bud_key_values():
87
+ return {f"DOESN'T_MATTER_THAT'S_THE_POINT_{i}": i for i in [0, 1, 3, 4]}
88
+
89
+
38
90
  def test_simple_flower_pot(simple_flower_pot, simple_key_values):
39
91
  """
40
92
  Given: A FlowerPot with a simple Flower
41
- When: Updating rock and flower with key: value pairs
42
- Then: The rock and flower are correctly updated
93
+ When: Updating flower with key: value pairs
94
+ Then: The flower are correctly updated
43
95
  """
44
96
  assert len(simple_flower_pot) == 1
45
97
 
@@ -51,6 +103,8 @@ def test_simple_flower_pot(simple_flower_pot, simple_key_values):
51
103
 
52
104
  petals = sorted(list(flower.petals), key=lambda x: x.value)
53
105
  assert len(petals) == 2
106
+ assert flower.bud.value == petals[0].value
107
+ assert flower.bud.keys == petals[0].keys
54
108
  assert petals[0].value == 0
55
109
  assert petals[0].keys == ["thing0", "thing2", "thing4"]
56
110
  assert petals[1].value == 1
@@ -67,8 +121,12 @@ def test_cached_petal(simple_flower):
67
121
  value1 = 4
68
122
  simple_flower.update(key1, value1)
69
123
  assert len(simple_flower.petals) == 1
124
+
125
+ # Assert twice to hit the cache
70
126
  assert simple_flower.petals[0].value == value1 % 2 # % 2 because of simple_flower's `setter`
71
127
  assert simple_flower.petals[0].keys == [key1]
128
+ assert simple_flower.petals[0].value == value1 % 2
129
+ assert simple_flower.petals[0].keys == [key1]
72
130
 
73
131
  key2 = "thing2"
74
132
  value2 = 3
@@ -79,6 +137,10 @@ def test_cached_petal(simple_flower):
79
137
  assert sorted_petals[0].keys == [key1]
80
138
  assert sorted_petals[1].value == value2 % 2
81
139
  assert sorted_petals[1].keys == [key2]
140
+ assert sorted_petals[0].value == value1 % 2
141
+ assert sorted_petals[0].keys == [key1]
142
+ assert sorted_petals[1].value == value2 % 2
143
+ assert sorted_petals[1].keys == [key2]
82
144
 
83
145
 
84
146
  def test_spilled_dirt_flower(simple_flower):
@@ -103,3 +165,83 @@ def test_unhashable_dirt(simple_flower_pot):
103
165
  value = "never gonna get here"
104
166
  with pytest.raises(TypeError):
105
167
  simple_flower_pot.add_dirt(unhashable_key, value)
168
+
169
+
170
+ def test_buds(simple_bud_pot, bud_key_values):
171
+ """
172
+ Given: A Flower pot with two Buds that compute the same thing; one a `Stem` and one a `SetStem`, and a `ListStem` bud
173
+ When: Updating the pot with key: value pairs
174
+ Then: The computed buds are correct and the `Stem` and `SetStem` buds match
175
+ """
176
+ assert len(simple_bud_pot) == 3
177
+
178
+ assert simple_bud_pot[0].stem_name == "StemBud"
179
+ assert simple_bud_pot[1].stem_name == "ListStemBud"
180
+ assert simple_bud_pot[2].stem_name == "SetStemBud"
181
+
182
+ for k, m in bud_key_values.items():
183
+ simple_bud_pot.add_dirt(k, m)
184
+
185
+ assert simple_bud_pot[0].bud.value == 2
186
+ assert simple_bud_pot[1].bud.value == 4
187
+ assert simple_bud_pot[2].bud.value == simple_bud_pot[0].bud.value
188
+
189
+ assert len(simple_bud_pot[0].petals) == 1
190
+ assert simple_bud_pot[0].petals[0].value == 2
191
+ assert simple_bud_pot[0].petals[0].keys == [
192
+ f"DOESN'T_MATTER_THAT'S_THE_POINT_{i}" for i in [0, 1, 3, 4]
193
+ ]
194
+
195
+ with pytest.raises(
196
+ AttributeError,
197
+ match="ListBud subclasses ListStem and therefore does not define the `petals` property",
198
+ ):
199
+ _ = simple_bud_pot[1].petals
200
+
201
+ with pytest.raises(
202
+ AttributeError,
203
+ match="SetBud subclasses SetStem and therefore does not define the `petals` property",
204
+ ):
205
+ _ = simple_bud_pot[2].petals
206
+
207
+
208
+ def test_liststem_cached_bud(liststem_bud):
209
+ """
210
+ Given: A `ListStem` instance and some different input values
211
+ When: Computing the `bud` property after each value is ingested
212
+ Then: The `bud` value correctly updates based on the state of the `ListStem` object
213
+ """
214
+ key = "Who cares"
215
+ value1 = 3
216
+ liststem_bud.update(key, value1)
217
+
218
+ # Assert twice so we hit the cache
219
+ assert liststem_bud.bud.value == 1
220
+ assert liststem_bud.bud.value == 1
221
+
222
+ value2 = 1
223
+ liststem_bud.update(key, value2)
224
+
225
+ assert liststem_bud.bud.value == 2
226
+ assert liststem_bud.bud.value == 2
227
+
228
+
229
+ def test_setstem_cached_bud(setstem_bud):
230
+ """
231
+ Given: A `SetStem` instance and some different input values
232
+ When: Computing the `bud` property after each value is ingested
233
+ Then: The `bud` value correctly updates based on the state of the `SetStem` object
234
+ """
235
+ key = "Who cares"
236
+ value1 = 3
237
+ setstem_bud.update(key, value1)
238
+
239
+ # Assert twice so we hit the cache
240
+ assert setstem_bud.bud.value == 1
241
+ assert setstem_bud.bud.value == 1
242
+
243
+ value2 = 1
244
+ setstem_bud.update(key, value2)
245
+
246
+ assert setstem_bud.bud.value == 2
247
+ assert setstem_bud.bud.value == 2
@@ -0,0 +1,27 @@
1
+ import pytest
2
+
3
+ from dkist_processing_common.models.fried_parameter import r0_valid
4
+
5
+
6
+ @pytest.mark.parametrize(
7
+ "r0, ao_lock, oob_shift, should_r0_exist",
8
+ [
9
+ pytest.param(0.2, True, 17, True, id="AO_LOCK_True_good_R0_good_oob"),
10
+ pytest.param(1, True, 17, False, id="AO_LOCK_True_bad_R0_good_oob"),
11
+ pytest.param(0.2, False, 17, False, id="AO_LOCK_False_good_R0_good_oob"),
12
+ pytest.param(1, False, 17, False, id="AO_LOCK_False_bad_R0_good_oob"),
13
+ pytest.param(0.2, True, 150, False, id="AO_LOCK_True_good_R0_bad_oob"),
14
+ pytest.param(1, True, 150, False, id="AO_LOCK_True_bad_R0_bad_oob"),
15
+ pytest.param(0.2, False, 150, False, id="AO_LOCK_False_good_R0_bad_oob"),
16
+ pytest.param(1, False, 150, False, id="AO_LOCK_False_bad_R0_bad_oob"),
17
+ pytest.param(0.2, None, 17, False, id="AO_LOCK_missing"),
18
+ pytest.param(0.2, True, None, True, id="OOBSHIFT_missing"),
19
+ ],
20
+ )
21
+ def test_check_r0_valid(r0, ao_lock, oob_shift, should_r0_exist):
22
+ """
23
+ :Given: values for r0, the ao_lock status, and the ao out of bound shift value
24
+ :When: checking for a valid state to use r0
25
+ :Then: valid conditions are marked True, invalid conditions marked False
26
+ """
27
+ assert r0_valid(r0, ao_lock, oob_shift) == should_r0_exist