dkist-processing-cryonirsp 1.3.4__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.

Potentially problematic release.


This version of dkist-processing-cryonirsp might be problematic. Click here for more details.

Files changed (111) hide show
  1. changelog/.gitempty +0 -0
  2. dkist_processing_cryonirsp/__init__.py +11 -0
  3. dkist_processing_cryonirsp/config.py +12 -0
  4. dkist_processing_cryonirsp/models/__init__.py +1 -0
  5. dkist_processing_cryonirsp/models/constants.py +248 -0
  6. dkist_processing_cryonirsp/models/exposure_conditions.py +26 -0
  7. dkist_processing_cryonirsp/models/parameters.py +296 -0
  8. dkist_processing_cryonirsp/models/tags.py +168 -0
  9. dkist_processing_cryonirsp/models/task_name.py +14 -0
  10. dkist_processing_cryonirsp/parsers/__init__.py +1 -0
  11. dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +111 -0
  12. dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +30 -0
  13. dkist_processing_cryonirsp/parsers/exposure_conditions.py +163 -0
  14. dkist_processing_cryonirsp/parsers/map_repeats.py +40 -0
  15. dkist_processing_cryonirsp/parsers/measurements.py +55 -0
  16. dkist_processing_cryonirsp/parsers/modstates.py +31 -0
  17. dkist_processing_cryonirsp/parsers/optical_density_filters.py +40 -0
  18. dkist_processing_cryonirsp/parsers/polarimetric_check.py +120 -0
  19. dkist_processing_cryonirsp/parsers/scan_step.py +412 -0
  20. dkist_processing_cryonirsp/parsers/time.py +80 -0
  21. dkist_processing_cryonirsp/parsers/wavelength.py +26 -0
  22. dkist_processing_cryonirsp/tasks/__init__.py +19 -0
  23. dkist_processing_cryonirsp/tasks/assemble_movie.py +202 -0
  24. dkist_processing_cryonirsp/tasks/bad_pixel_map.py +96 -0
  25. dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +279 -0
  26. dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +55 -0
  27. dkist_processing_cryonirsp/tasks/ci_science.py +169 -0
  28. dkist_processing_cryonirsp/tasks/cryonirsp_base.py +67 -0
  29. dkist_processing_cryonirsp/tasks/dark.py +98 -0
  30. dkist_processing_cryonirsp/tasks/gain.py +251 -0
  31. dkist_processing_cryonirsp/tasks/instrument_polarization.py +447 -0
  32. dkist_processing_cryonirsp/tasks/l1_output_data.py +44 -0
  33. dkist_processing_cryonirsp/tasks/linearity_correction.py +582 -0
  34. dkist_processing_cryonirsp/tasks/make_movie_frames.py +302 -0
  35. dkist_processing_cryonirsp/tasks/mixin/__init__.py +1 -0
  36. dkist_processing_cryonirsp/tasks/mixin/beam_access.py +52 -0
  37. dkist_processing_cryonirsp/tasks/mixin/corrections.py +177 -0
  38. dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +193 -0
  39. dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +309 -0
  40. dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +297 -0
  41. dkist_processing_cryonirsp/tasks/parse.py +281 -0
  42. dkist_processing_cryonirsp/tasks/quality_metrics.py +271 -0
  43. dkist_processing_cryonirsp/tasks/science_base.py +511 -0
  44. dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +270 -0
  45. dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +484 -0
  46. dkist_processing_cryonirsp/tasks/sp_geometric.py +585 -0
  47. dkist_processing_cryonirsp/tasks/sp_science.py +299 -0
  48. dkist_processing_cryonirsp/tasks/sp_solar_gain.py +475 -0
  49. dkist_processing_cryonirsp/tasks/trial_output_data.py +61 -0
  50. dkist_processing_cryonirsp/tasks/write_l1.py +1033 -0
  51. dkist_processing_cryonirsp/tests/__init__.py +1 -0
  52. dkist_processing_cryonirsp/tests/conftest.py +456 -0
  53. dkist_processing_cryonirsp/tests/header_models.py +592 -0
  54. dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
  55. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +541 -0
  56. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +615 -0
  57. dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +96 -0
  58. dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +592 -0
  59. dkist_processing_cryonirsp/tests/test_assemble_movie.py +144 -0
  60. dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +517 -0
  61. dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +115 -0
  62. dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +106 -0
  63. dkist_processing_cryonirsp/tests/test_ci_science.py +355 -0
  64. dkist_processing_cryonirsp/tests/test_corrections.py +126 -0
  65. dkist_processing_cryonirsp/tests/test_cryo_base.py +202 -0
  66. dkist_processing_cryonirsp/tests/test_cryo_constants.py +76 -0
  67. dkist_processing_cryonirsp/tests/test_dark.py +287 -0
  68. dkist_processing_cryonirsp/tests/test_gain.py +278 -0
  69. dkist_processing_cryonirsp/tests/test_instrument_polarization.py +531 -0
  70. dkist_processing_cryonirsp/tests/test_linearity_correction.py +245 -0
  71. dkist_processing_cryonirsp/tests/test_make_movie_frames.py +111 -0
  72. dkist_processing_cryonirsp/tests/test_parameters.py +266 -0
  73. dkist_processing_cryonirsp/tests/test_parse.py +1439 -0
  74. dkist_processing_cryonirsp/tests/test_quality.py +203 -0
  75. dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +112 -0
  76. dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py +155 -0
  77. dkist_processing_cryonirsp/tests/test_sp_geometric.py +319 -0
  78. dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +121 -0
  79. dkist_processing_cryonirsp/tests/test_sp_science.py +483 -0
  80. dkist_processing_cryonirsp/tests/test_sp_solar.py +198 -0
  81. dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +79 -0
  82. dkist_processing_cryonirsp/tests/test_trial_output_data.py +251 -0
  83. dkist_processing_cryonirsp/tests/test_workflows.py +9 -0
  84. dkist_processing_cryonirsp/tests/test_write_l1.py +436 -0
  85. dkist_processing_cryonirsp/workflows/__init__.py +2 -0
  86. dkist_processing_cryonirsp/workflows/ci_l0_processing.py +77 -0
  87. dkist_processing_cryonirsp/workflows/sp_l0_processing.py +84 -0
  88. dkist_processing_cryonirsp/workflows/trial_workflows.py +190 -0
  89. dkist_processing_cryonirsp-1.3.4.dist-info/METADATA +194 -0
  90. dkist_processing_cryonirsp-1.3.4.dist-info/RECORD +111 -0
  91. dkist_processing_cryonirsp-1.3.4.dist-info/WHEEL +5 -0
  92. dkist_processing_cryonirsp-1.3.4.dist-info/top_level.txt +4 -0
  93. docs/Makefile +134 -0
  94. docs/bad_pixel_calibration.rst +47 -0
  95. docs/beam_angle_calculation.rst +53 -0
  96. docs/beam_boundary_computation.rst +88 -0
  97. docs/changelog.rst +7 -0
  98. docs/ci_science_calibration.rst +33 -0
  99. docs/conf.py +52 -0
  100. docs/index.rst +21 -0
  101. docs/l0_to_l1_cryonirsp_ci-full-trial.rst +10 -0
  102. docs/l0_to_l1_cryonirsp_ci.rst +10 -0
  103. docs/l0_to_l1_cryonirsp_sp-full-trial.rst +10 -0
  104. docs/l0_to_l1_cryonirsp_sp.rst +10 -0
  105. docs/linearization.rst +43 -0
  106. docs/make.bat +170 -0
  107. docs/requirements.txt +1 -0
  108. docs/requirements_table.rst +8 -0
  109. docs/scientific_changelog.rst +10 -0
  110. docs/sp_science_calibration.rst +59 -0
  111. licenses/LICENSE.rst +11 -0
@@ -0,0 +1,79 @@
1
+ import pytest
2
+ from dkist_processing_common._util.scratch import WorkflowFileSystem
3
+ from dkist_processing_common.codecs.bytes import bytes_decoder
4
+ from dkist_processing_common.codecs.quality import quality_data_encoder
5
+ from dkist_processing_common.tasks import CreateTrialQualityReport
6
+
7
+ from dkist_processing_cryonirsp.models.tags import CryonirspTag
8
+ from dkist_processing_cryonirsp.tests.conftest import CryonirspConstantsDb
9
+
10
+
11
+ @pytest.fixture()
12
+ def quality_data_warning_only() -> list[dict]:
13
+ """Simple quality data that can be used to create a quality report in pdf format"""
14
+ return [
15
+ {
16
+ "name": "Range checks",
17
+ "description": "This metric is checking that certain input and calculated parameters"
18
+ " fall within a valid data range. If no parameters are listed here,"
19
+ " all pipeline parameters were measured to be in range",
20
+ "statement": "This is a test quality report with no data",
21
+ "plot_data": None,
22
+ "histogram_data": None,
23
+ "table_data": None,
24
+ "modmat_data": None,
25
+ "efficiency_data": None,
26
+ "raincloud_data": None,
27
+ "warnings": ["warning 1", "warning 2"],
28
+ }
29
+ ]
30
+
31
+
32
+ @pytest.fixture
33
+ def create_trial_quality_report_task(
34
+ tmp_path,
35
+ recipe_run_id,
36
+ init_cryonirsp_constants_db,
37
+ quality_data_warning_only,
38
+ ) -> CreateTrialQualityReport:
39
+ constants_db = CryonirspConstantsDb()
40
+ init_cryonirsp_constants_db(recipe_run_id, constants_db)
41
+ with CreateTrialQualityReport(
42
+ recipe_run_id=recipe_run_id,
43
+ workflow_name="workflow_name",
44
+ workflow_version="workflow_version",
45
+ ) as task:
46
+ task.scratch = WorkflowFileSystem(
47
+ recipe_run_id=recipe_run_id,
48
+ scratch_base_path=tmp_path,
49
+ )
50
+ task.write(
51
+ quality_data_warning_only,
52
+ tags=CryonirspTag.quality_data(),
53
+ encoder=quality_data_encoder,
54
+ relative_path=f"{task.constants.dataset_id}_quality_data.json",
55
+ )
56
+
57
+ yield task
58
+ task._purge()
59
+
60
+
61
+ def test_create_trial_quality_report(create_trial_quality_report_task):
62
+ """
63
+ :Given: An instance of CreateTrialQualityReport with tagged quality data
64
+ :When: CreateTrialQualityReport is run
65
+ :Then: A quality report pdf file gets created and tagged
66
+ """
67
+ task = create_trial_quality_report_task
68
+ # When
69
+ task()
70
+ # Then
71
+ paths = list(task.read(tags=[CryonirspTag.output(), CryonirspTag.quality_report()]))
72
+ assert len(paths) == 1
73
+ quality_report = next(
74
+ task.read(
75
+ tags=[CryonirspTag.output(), CryonirspTag.quality_report()], decoder=bytes_decoder
76
+ )
77
+ )
78
+ assert isinstance(quality_report, bytes)
79
+ assert b"%PDF" == quality_report[:4]
@@ -0,0 +1,251 @@
1
+ import json
2
+ from uuid import uuid4
3
+
4
+ import pytest
5
+ from dkist_processing_common._util.scratch import WorkflowFileSystem
6
+ from dkist_processing_common.codecs.json import json_encoder
7
+ from dkist_processing_common.codecs.quality import quality_data_encoder
8
+ from dkist_processing_common.codecs.str import str_encoder
9
+ from dkist_processing_common.models.task_name import TaskName
10
+ from dkist_processing_common.tests.conftest import FakeGQLClient
11
+ from pydantic import BaseModel
12
+
13
+ from dkist_processing_cryonirsp.models.tags import CryonirspTag
14
+ from dkist_processing_cryonirsp.models.task_name import CryonirspTaskName
15
+ from dkist_processing_cryonirsp.tasks import TransferCryoTrialData
16
+
17
+
18
+ @pytest.fixture
19
+ def recipe_run_configuration(
20
+ debug_switch,
21
+ intermediate_switch,
22
+ output_switch,
23
+ dataset_inventory_switch,
24
+ asdf_switch,
25
+ quality_report_switch,
26
+ quality_data_switch,
27
+ tag_lists,
28
+ ):
29
+ """Mock Recipe Run endpoint for trial output configuration flags"""
30
+
31
+ class GQLClientWithConfiguration(FakeGQLClient):
32
+ def execute_gql_query(self, **kwargs):
33
+ response = super().execute_gql_query(**kwargs)
34
+ response[0].configuration = json.dumps(
35
+ {
36
+ "trial_transfer_debug_frames": bool(debug_switch),
37
+ "trial_transfer_intermediate_frames": bool(intermediate_switch),
38
+ "trial_transfer_output_frames": bool(output_switch),
39
+ "trial_transfer_output_dataset_inventory": bool(dataset_inventory_switch),
40
+ "trial_transfer_output_asdf": bool(asdf_switch),
41
+ "trial_transfer_output_quality_report": bool(quality_report_switch),
42
+ "trial_transfer_output_quality_data": bool(quality_data_switch),
43
+ "trial_transfer_tag_lists": tag_lists,
44
+ }
45
+ )
46
+ return response
47
+
48
+ return GQLClientWithConfiguration
49
+
50
+
51
+ intermediate_task_names = [
52
+ TaskName.dark.value,
53
+ TaskName.lamp_gain.value,
54
+ TaskName.geometric_angle.value,
55
+ TaskName.geometric_offsets.value,
56
+ TaskName.geometric_spectral_shifts.value,
57
+ TaskName.solar_gain.value,
58
+ TaskName.demodulation_matrices.value,
59
+ CryonirspTaskName.spectral_corrected_solar_array.value,
60
+ ]
61
+
62
+ tag_lists = [[CryonirspTag.movie()], ["FOO", "BAR"]]
63
+
64
+
65
+ def write_debug_frames_to_task(task: TransferCryoTrialData) -> int:
66
+ num_debug = 3
67
+ for _ in range(num_debug):
68
+ task.write(
69
+ data="123", encoder=str_encoder, tags=[CryonirspTag.frame(), CryonirspTag.debug()]
70
+ )
71
+
72
+ return num_debug
73
+
74
+
75
+ def write_intermediate_frames_to_task(task: TransferCryoTrialData) -> int:
76
+ for task_name in intermediate_task_names:
77
+ task.write(
78
+ data=task_name,
79
+ encoder=str_encoder,
80
+ tags=[CryonirspTag.frame(), CryonirspTag.intermediate(), CryonirspTag.task(task_name)],
81
+ )
82
+
83
+ return len(intermediate_task_names)
84
+
85
+
86
+ def write_dummy_output_frames_to_task(task: TransferCryoTrialData) -> int:
87
+ num_output = 2
88
+ for i in range(num_output):
89
+ task.write(
90
+ data=f"output_{i}",
91
+ encoder=str_encoder,
92
+ tags=[CryonirspTag.frame(), CryonirspTag.output()],
93
+ )
94
+
95
+ return num_output
96
+
97
+
98
+ def write_specific_tags_to_task(task: TransferCryoTrialData) -> int:
99
+ for tags in tag_lists:
100
+ task.write(data="foo", encoder=str_encoder, tags=tags)
101
+
102
+ return len(tag_lists)
103
+
104
+
105
+ def write_dataset_inventory_to_task(task: TransferCryoTrialData) -> int:
106
+ dataset_inventory_obj: dict = {f"dsi_key": uuid4().hex}
107
+ task.write(
108
+ data=dataset_inventory_obj,
109
+ encoder=json_encoder,
110
+ tags=[CryonirspTag.output(), CryonirspTag.dataset_inventory()],
111
+ )
112
+ return 1
113
+
114
+
115
+ def write_asdf_to_task(task: TransferCryoTrialData) -> int:
116
+ asdf_obj = uuid4().hex
117
+ task.write(
118
+ data=asdf_obj, encoder=str_encoder, tags=[CryonirspTag.output(), CryonirspTag.asdf()]
119
+ )
120
+ return 1
121
+
122
+
123
+ def write_quality_report_to_task(task: TransferCryoTrialData) -> int:
124
+ quality_report_obj = uuid4().hex.encode("utf-8")
125
+ task.write(data=quality_report_obj, tags=[CryonirspTag.output(), CryonirspTag.quality_report()])
126
+ return 1
127
+
128
+
129
+ def write_quality_data_to_task(task: TransferCryoTrialData) -> int:
130
+ quality_data_obj: list[dict] = [{f"quality_key": uuid4().hex}]
131
+ task.write(
132
+ data=quality_data_obj, encoder=quality_data_encoder, tags=CryonirspTag.quality_data()
133
+ )
134
+ return 1
135
+
136
+
137
+ def write_unused_frames_to_task(task: TransferCryoTrialData) -> int:
138
+ task.write(data="bad", encoder=str_encoder, tags=["FOO"])
139
+ task.write(
140
+ data="intermediate we don't care about",
141
+ encoder=str_encoder,
142
+ tags=[CryonirspTag.frame(), CryonirspTag.intermediate(), CryonirspTag.task("NOT_A_KEEPER")],
143
+ )
144
+ return 1
145
+
146
+
147
+ class AvailableOutputFiles(BaseModel):
148
+ """Number of files of each type available for potential output"""
149
+
150
+ num_debug: int
151
+ num_intermediate: int
152
+ num_output: int
153
+ num_specific: int
154
+ num_dataset_inventory: int
155
+ num_asdf: int
156
+ num_quality_report: int
157
+ num_quality_data: int
158
+
159
+
160
+ @pytest.fixture
161
+ def transfer_task_with_files(recipe_run_id, recipe_run_configuration, tmp_path, mocker):
162
+ mocker.patch(
163
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient",
164
+ new=recipe_run_configuration,
165
+ )
166
+ proposal_id = "test_proposal_id"
167
+ with TransferCryoTrialData(
168
+ recipe_run_id=recipe_run_id,
169
+ workflow_name="workflow_name",
170
+ workflow_version="workflow_version",
171
+ ) as task:
172
+ task.scratch = WorkflowFileSystem(
173
+ recipe_run_id=recipe_run_id,
174
+ scratch_base_path=tmp_path,
175
+ )
176
+ task.constants._update({"PROPOSAL_ID": proposal_id})
177
+ try:
178
+ write_unused_frames_to_task(task)
179
+ available_output_files = AvailableOutputFiles(
180
+ num_debug=write_debug_frames_to_task(task),
181
+ num_intermediate=write_intermediate_frames_to_task(task),
182
+ num_output=write_dummy_output_frames_to_task(task),
183
+ num_specific=write_specific_tags_to_task(task),
184
+ num_dataset_inventory=write_dataset_inventory_to_task(task),
185
+ num_asdf=write_asdf_to_task(task),
186
+ num_quality_report=write_quality_report_to_task(task),
187
+ num_quality_data=write_quality_data_to_task(task),
188
+ )
189
+ yield task, available_output_files
190
+
191
+ finally:
192
+ task._purge()
193
+
194
+
195
+ @pytest.mark.parametrize(
196
+ "debug_switch, intermediate_switch, output_switch, dataset_inventory_switch, asdf_switch, quality_report_switch, quality_data_switch, tag_lists",
197
+ [
198
+ pytest.param(0, 0, 0, 0, 0, 0, 0, [], id="none"),
199
+ pytest.param(1, 0, 0, 0, 0, 0, 0, [], id="debug_only"),
200
+ pytest.param(0, 1, 0, 0, 0, 0, 0, [], id="intermediate_only"),
201
+ pytest.param(0, 0, 1, 0, 0, 0, 0, [], id="output_only"),
202
+ pytest.param(0, 0, 0, 1, 0, 0, 0, [], id="dataset_inventory_only"),
203
+ pytest.param(0, 0, 0, 0, 1, 0, 0, [], id="asdf_only"),
204
+ pytest.param(0, 0, 0, 0, 0, 1, 0, [], id="quality_report_only"),
205
+ pytest.param(0, 0, 0, 0, 0, 0, 1, [], id="quality_data_only"),
206
+ pytest.param(0, 0, 0, 0, 0, 0, 0, tag_lists, id="specific_only"),
207
+ pytest.param(1, 1, 1, 1, 1, 1, 1, tag_lists, id="all"),
208
+ pytest.param(1, 1, 0, 0, 0, 0, 0, tag_lists, id="combo_debug_intermediate_specific"),
209
+ pytest.param(1, 1, 1, 0, 0, 0, 0, [], id="combo_debug_intermediate_output"),
210
+ pytest.param(0, 1, 1, 0, 0, 0, 0, tag_lists, id="combo_intermediate_output_specific"),
211
+ pytest.param(1, 0, 1, 0, 0, 0, 0, tag_lists, id="combo_debug_output_specific"),
212
+ ],
213
+ )
214
+ def test_build_transfer_list(
215
+ transfer_task_with_files,
216
+ debug_switch,
217
+ intermediate_switch,
218
+ output_switch,
219
+ dataset_inventory_switch,
220
+ asdf_switch,
221
+ quality_report_switch,
222
+ quality_data_switch,
223
+ tag_lists,
224
+ ):
225
+ """
226
+ Given: A TransferCryoTrialData task with a recipe run configuration (RRC) and a collection of frames
227
+ When: Building the transfer list
228
+ Then: Only the files requested by the RRC switches are collected for transfer
229
+ """
230
+ task, available_output_files = transfer_task_with_files
231
+
232
+ expected_num = 0
233
+ if debug_switch:
234
+ expected_num += available_output_files.num_debug
235
+ if intermediate_switch:
236
+ expected_num += available_output_files.num_intermediate
237
+ if output_switch:
238
+ expected_num += available_output_files.num_output
239
+ if dataset_inventory_switch:
240
+ expected_num += available_output_files.num_dataset_inventory
241
+ if asdf_switch:
242
+ expected_num += available_output_files.num_asdf
243
+ if quality_report_switch:
244
+ expected_num += available_output_files.num_quality_report
245
+ if quality_data_switch:
246
+ expected_num += available_output_files.num_quality_data
247
+ if tag_lists:
248
+ expected_num += available_output_files.num_specific
249
+
250
+ transfer_list = task.build_transfer_list()
251
+ assert len(transfer_list) == expected_num
@@ -0,0 +1,9 @@
1
+ """Test integrity of workflows."""
2
+ from dkist_processing_core.build_utils import validate_workflows
3
+
4
+ from dkist_processing_cryonirsp import workflows
5
+
6
+
7
+ def test_workflow_integrity():
8
+ """Validate workflow to ensure acyclic-ness and export compilation"""
9
+ validate_workflows(workflows)