dkist-processing-common 10.8.1rc1__tar.gz → 10.8.3__tar.gz
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.
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/CHANGELOG.rst +33 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/PKG-INFO +2 -2
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/fits.py +6 -12
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/manual.py +5 -3
- dkist_processing_common-10.8.3/dkist_processing_common/models/fried_parameter.py +41 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/graphql.py +3 -13
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/parameters.py +28 -65
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/quality.py +1 -0
- dkist_processing_common-10.8.3/dkist_processing_common/tasks/mixin/input_dataset.py +166 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/metadata_store.py +4 -7
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/quality/_metrics.py +19 -14
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/quality_metrics.py +1 -1
- dkist_processing_common-10.8.3/dkist_processing_common/tasks/transfer_input_data.py +129 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/write_l1.py +29 -3
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/conftest.py +7 -24
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_codecs.py +0 -38
- dkist_processing_common-10.8.3/dkist_processing_common/tests/test_fried_parameter.py +27 -0
- dkist_processing_common-10.8.3/dkist_processing_common/tests/test_input_dataset.py +509 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_parameters.py +22 -71
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_quality_mixin.py +32 -22
- dkist_processing_common-10.8.3/dkist_processing_common/tests/test_transfer_input_data.py +170 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_write_l1.py +143 -10
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common.egg-info/PKG-INFO +2 -2
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common.egg-info/SOURCES.txt +3 -6
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common.egg-info/requires.txt +1 -1
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/pyproject.toml +1 -1
- dkist_processing_common-10.8.1rc1/changelog/235.feature.rst +0 -3
- dkist_processing_common-10.8.1rc1/changelog/235.misc.1.rst +0 -2
- dkist_processing_common-10.8.1rc1/changelog/235.misc.rst +0 -1
- dkist_processing_common-10.8.1rc1/dkist_processing_common/codecs/array.py +0 -19
- dkist_processing_common-10.8.1rc1/dkist_processing_common/codecs/basemodel.py +0 -21
- dkist_processing_common-10.8.1rc1/dkist_processing_common/models/input_dataset.py +0 -113
- dkist_processing_common-10.8.1rc1/dkist_processing_common/tasks/transfer_input_data.py +0 -120
- dkist_processing_common-10.8.1rc1/dkist_processing_common/tests/test_input_dataset.py +0 -280
- dkist_processing_common-10.8.1rc1/dkist_processing_common/tests/test_transfer_input_data.py +0 -256
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/.gitignore +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/.pre-commit-config.yaml +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/.readthedocs.yml +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/.snyk +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/README.rst +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/bitbucket-pipelines.yml +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/changelog/.gitempty +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/_util/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/_util/constants.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/_util/graphql.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/_util/scratch.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/_util/tags.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/asdf.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/bytes.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/iobase.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/json.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/path.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/quality.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/codecs/str.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/config.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/fonts/Lato-Regular.ttf +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/fonts/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/constants.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/fits_access.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/flower_pot.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/message.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/message_queue_binding.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/metric_code.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/quality.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/tags.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/task_name.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/models/wavelength.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/cs_step.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/dsps_repeat.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/experiment_id_bud.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/id_bud.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/l0_fits_access.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/l1_fits_access.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/near_bud.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/proposal_id_bud.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/retarder.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/single_value_single_key_flower.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/task.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/time.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/unique_bud.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/parsers/wavelength.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/assemble_movie.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/base.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/l1_output_data.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/globus.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/interservice_bus.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/object_store.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/quality/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/mixin/quality/_base.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/output_data_base.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/parse_l0_input_data.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/teardown.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/trial_catalog.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tasks/trial_output_data.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/__init__.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_assemble_movie.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_assemble_quality.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_base.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_constants.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_cs_step.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_fits_access.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_flower_pot.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_interservice_bus.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_interservice_bus_mixin.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_manual_processing.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_output_data_base.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_parse_l0_input_data.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_publish_catalog_messages.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_quality.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_scratch.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_stems.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_submit_dataset_metadata.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_tags.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_task_name.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_task_parsing.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_teardown.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_transfer_l1_output_data.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_trial_catalog.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_trial_output_data.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common/tests/test_workflow_task_base.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common.egg-info/dependency_links.txt +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/dkist_processing_common.egg-info/top_level.txt +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/Makefile +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/changelog.rst +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/conf.py +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/index.rst +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/landing_page.rst +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/make.bat +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/docs/requirements.txt +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/licenses/LICENSE.rst +0 -0
- {dkist_processing_common-10.8.1rc1 → dkist_processing_common-10.8.3}/setup.cfg +0 -0
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
v10.8.3 (2025-05-28)
|
|
2
|
+
====================
|
|
3
|
+
|
|
4
|
+
Features
|
|
5
|
+
--------
|
|
6
|
+
|
|
7
|
+
- Further constrain when the ATMOS_R0 header key is written in L1 data based on the following metrics:
|
|
8
|
+
|
|
9
|
+
* the ATMOS_R0 value in the L0 data must be less than or equal to 30cm
|
|
10
|
+
* the AO_LOCK value in the L0 data must be True
|
|
11
|
+
* the OOBSHIFT value in the L0 data must be less than 100
|
|
12
|
+
|
|
13
|
+
The wavefront sensor has about 1500 subapertures from which x and y shifts are measured. When the adaptive optics loop is locked, we count how many of these shifts exceed a threshold considered to be out of bounds. The r0 measurement gets less accurate as more of these shifts are out of bounds and so this is another way to detect outliers in the r0 value. (`#250 <https://bitbucket.org/dkistdc/dkist-processing-common/pull-requests/250>`__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
v10.8.2 (2025-05-27)
|
|
17
|
+
====================
|
|
18
|
+
|
|
19
|
+
Bugfixes
|
|
20
|
+
--------
|
|
21
|
+
|
|
22
|
+
- Prevent the `WAVEBAND` key from being populated with a spectral line that falls outside the wavelength bounds of the data. (`#251 <https://bitbucket.org/dkistdc/dkist-processing-common/pull-requests/251>`__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
v10.8.1 (2025-05-22)
|
|
26
|
+
====================
|
|
27
|
+
|
|
28
|
+
Misc
|
|
29
|
+
----
|
|
30
|
+
|
|
31
|
+
- Update `dkist-processing-core` to v5.1.1. (`#253 <https://bitbucket.org/dkistdc/dkist-processing-common/pull-requests/253>`__)
|
|
32
|
+
|
|
33
|
+
|
|
1
34
|
v10.8.0 (2025-05-15)
|
|
2
35
|
====================
|
|
3
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dkist-processing-common
|
|
3
|
-
Version: 10.8.
|
|
3
|
+
Version: 10.8.3
|
|
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
|
|
@@ -17,7 +17,7 @@ Requires-Dist: asdf<4.0.0,>=3.5.0
|
|
|
17
17
|
Requires-Dist: astropy>=7.0.0
|
|
18
18
|
Requires-Dist: dkist-fits-specifications<5.0,>=4.0.0
|
|
19
19
|
Requires-Dist: dkist-header-validator<6.0,>=5.0.0
|
|
20
|
-
Requires-Dist: dkist-processing-core==5.1.
|
|
20
|
+
Requires-Dist: dkist-processing-core==5.1.1
|
|
21
21
|
Requires-Dist: dkist-processing-pac<4.0,>=3.1
|
|
22
22
|
Requires-Dist: dkist-service-configuration<3.0,>=2.0.2
|
|
23
23
|
Requires-Dist: dkist-spectral-lines<4.0,>=3.0.0
|
|
@@ -30,15 +30,15 @@ def fits_hdulist_encoder(hdu_list: fits.HDUList) -> bytes:
|
|
|
30
30
|
return iobase_encoder(file_obj)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def fits_hdu_decoder(path: Path
|
|
33
|
+
def fits_hdu_decoder(path: Path) -> fits.PrimaryHDU | fits.CompImageHDU:
|
|
34
34
|
"""Read a Path with `fits` to produce an `HDUList`."""
|
|
35
35
|
hdu_list = fits.open(path, checksum=True)
|
|
36
|
-
return _extract_hdu(hdu_list
|
|
36
|
+
return _extract_hdu(hdu_list)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def fits_array_decoder(path: Path,
|
|
39
|
+
def fits_array_decoder(path: Path, auto_squeeze: bool = True) -> np.ndarray:
|
|
40
40
|
"""Read a Path with `fits` and return the `.data` property."""
|
|
41
|
-
hdu = fits_hdu_decoder(path
|
|
41
|
+
hdu = fits_hdu_decoder(path)
|
|
42
42
|
data = hdu.data
|
|
43
43
|
|
|
44
44
|
# This conditional is explicitly to catch summit data with a dummy first axis for WCS
|
|
@@ -56,14 +56,8 @@ def fits_access_decoder(
|
|
|
56
56
|
return fits_access_class(hdu=hdu, name=str(path), **fits_access_kwargs)
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def _extract_hdu(hdul: fits.HDUList
|
|
60
|
-
"""
|
|
61
|
-
Return the fits hdu associated with the data in the hdu list.
|
|
62
|
-
|
|
63
|
-
Only search down the hdu index for the data if the hdu index is not explicitly provided.
|
|
64
|
-
"""
|
|
65
|
-
if hdu is not None:
|
|
66
|
-
return hdul[hdu]
|
|
59
|
+
def _extract_hdu(hdul: fits.HDUList) -> fits.PrimaryHDU | fits.CompImageHDU:
|
|
60
|
+
"""Return the fits hdu associated with the data in the hdu list."""
|
|
67
61
|
if hdul[0].data is not None:
|
|
68
62
|
return hdul[0]
|
|
69
63
|
return hdul[1]
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
import shutil
|
|
5
|
+
from dataclasses import asdict
|
|
6
|
+
from io import BytesIO
|
|
5
7
|
from pathlib import Path
|
|
6
8
|
from typing import Callable
|
|
7
9
|
from unittest.mock import patch
|
|
8
10
|
|
|
9
11
|
from dkist_processing_core.task import TaskBase
|
|
10
12
|
|
|
11
|
-
from dkist_processing_common.codecs.
|
|
13
|
+
from dkist_processing_common.codecs.json import json_encoder
|
|
12
14
|
from dkist_processing_common.models.graphql import RecipeRunProvenanceMutation
|
|
13
15
|
from dkist_processing_common.models.tags import Tag
|
|
14
16
|
from dkist_processing_common.tasks.base import WorkflowTaskBase
|
|
@@ -180,8 +182,8 @@ def writing_metadata_store_record_provenance(self, is_task_manual: bool, library
|
|
|
180
182
|
workflowVersion=self.workflow_version,
|
|
181
183
|
)
|
|
182
184
|
self.write(
|
|
183
|
-
data=params,
|
|
184
|
-
encoder=
|
|
185
|
+
data=params.model_dump(),
|
|
186
|
+
encoder=json_encoder,
|
|
185
187
|
tags=["PROVENANCE_RECORD"],
|
|
186
188
|
relative_path=f"{self.task_name}_provenance.json",
|
|
187
189
|
overwrite=True,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Helper methods to handle fried parameter / r0 validity."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def r0_valid(
|
|
5
|
+
r0: float | None = None,
|
|
6
|
+
ao_lock: bool | None = None,
|
|
7
|
+
num_out_of_bounds_ao_values: int | None = None,
|
|
8
|
+
) -> bool:
|
|
9
|
+
"""
|
|
10
|
+
Determine if the r0 value should be considered valid based on the following conditions.
|
|
11
|
+
|
|
12
|
+
* ATMOS_R0 does not exist in the header.
|
|
13
|
+
* the value of ATMOS_R0 is greater than 0.3m
|
|
14
|
+
* the AO is not locked
|
|
15
|
+
* the value of OOBSHIFT is greater than 100
|
|
16
|
+
|
|
17
|
+
When the adaptive optics system is not locked, the ATMOS_R0 keyword is still filled with the output of the
|
|
18
|
+
Fried parameter calculation. The inputs are not valid in this instance and the value should be removed.
|
|
19
|
+
|
|
20
|
+
Sometimes, due to timing differences between the calculation of the Fried parameter and the AO lock status being
|
|
21
|
+
updated, non-physical values can be recorded for ATMOS_R0 right on the edge of an AO_LOCK state change. To
|
|
22
|
+
combat this, any remaining R0 values greater than 30cm (which is beyond the realm of physical possibility for
|
|
23
|
+
solar observations) are also removed.
|
|
24
|
+
|
|
25
|
+
In addition, the number of AO out-of-bound values is given in the keyword OOBSHIFT and the AO team advises
|
|
26
|
+
that values under 100 are when the r0 value is considered reliable. If the OOBSHIFT key doesn't exist, this check
|
|
27
|
+
should be ignored.
|
|
28
|
+
"""
|
|
29
|
+
if r0 is None:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
if r0 > 0.3:
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
if ao_lock is not True:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
if num_out_of_bounds_ao_values is not None and num_out_of_bounds_ao_values > 100:
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
return True
|
|
@@ -3,9 +3,6 @@ from pydantic import BaseModel
|
|
|
3
3
|
from pydantic import field_validator
|
|
4
4
|
from pydantic import Json
|
|
5
5
|
|
|
6
|
-
from dkist_processing_common.models.input_dataset import InputDatasetBaseModel
|
|
7
|
-
from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
|
|
8
|
-
|
|
9
6
|
|
|
10
7
|
class RecipeRunMutation(BaseModel):
|
|
11
8
|
"""Recipe run mutation record."""
|
|
@@ -40,19 +37,13 @@ class InputDatasetPartTypeResponse(BaseModel):
|
|
|
40
37
|
inputDatasetPartTypeName: str
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
class InputDatasetPartResponse(
|
|
40
|
+
class InputDatasetPartResponse(BaseModel):
|
|
44
41
|
"""Response class for the input dataset part entity."""
|
|
45
42
|
|
|
46
43
|
inputDatasetPartId: int
|
|
47
|
-
|
|
48
|
-
inputDatasetPartDocument: Json[list]
|
|
44
|
+
inputDatasetPartDocument: Json[list[dict]]
|
|
49
45
|
inputDatasetPartType: InputDatasetPartTypeResponse
|
|
50
46
|
|
|
51
|
-
@field_validator("inputDatasetPartDocument", mode="after")
|
|
52
|
-
@classmethod
|
|
53
|
-
def _use_frame_or_parameter_model(cls, value_list): # not needed for gqlclient v2
|
|
54
|
-
return InputDatasetPartDocumentList(doc_list=value_list)
|
|
55
|
-
|
|
56
47
|
|
|
57
48
|
class InputDatasetInputDatasetPartResponse(BaseModel):
|
|
58
49
|
"""Response class for the join entity between input datasets and input dataset parts."""
|
|
@@ -112,12 +103,11 @@ class RecipeRunResponse(BaseModel):
|
|
|
112
103
|
recipeInstance: RecipeInstanceResponse
|
|
113
104
|
recipeInstanceId: int
|
|
114
105
|
recipeRunProvenances: list[RecipeRunProvenanceResponse]
|
|
115
|
-
# configuration: Json[RecipeRunConfiguration] | None # will work in gqlclient v2
|
|
116
106
|
configuration: Json[dict] | None
|
|
117
107
|
|
|
118
108
|
@field_validator("configuration", mode="after")
|
|
119
109
|
@classmethod
|
|
120
|
-
def _use_recipe_run_configuration_model(cls, value):
|
|
110
|
+
def _use_recipe_run_configuration_model(cls, value):
|
|
121
111
|
if value is None:
|
|
122
112
|
return RecipeRunConfiguration()
|
|
123
113
|
return RecipeRunConfiguration.model_validate(value)
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
"""Base class for parameter-parsing object."""
|
|
2
2
|
import logging
|
|
3
|
-
from contextlib import contextmanager
|
|
4
3
|
from datetime import datetime
|
|
5
|
-
from pathlib import Path
|
|
6
4
|
from typing import Any
|
|
7
|
-
from typing import Callable
|
|
8
5
|
from typing import Literal
|
|
9
6
|
|
|
10
7
|
import numpy as np
|
|
11
8
|
import scipy.interpolate as spi
|
|
9
|
+
from astropy.io import fits
|
|
12
10
|
|
|
13
|
-
from dkist_processing_common.
|
|
14
|
-
from dkist_processing_common.codecs.array import array_decoder
|
|
15
|
-
from dkist_processing_common.codecs.basemodel import basemodel_decoder
|
|
16
|
-
from dkist_processing_common.codecs.fits import fits_array_decoder
|
|
17
|
-
from dkist_processing_common.models.input_dataset import InputDatasetFilePointer
|
|
18
|
-
from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
|
|
19
|
-
from dkist_processing_common.models.tags import Tag
|
|
20
|
-
|
|
11
|
+
from dkist_processing_common.tasks.mixin.input_dataset import InputDatasetParameterValue
|
|
21
12
|
|
|
22
13
|
logger = logging.getLogger(__name__)
|
|
23
14
|
|
|
@@ -33,9 +24,9 @@ class ParameterBase:
|
|
|
33
24
|
|
|
34
25
|
To use in an instrument pipeline a subclass is required. Here's a simple, but complete example::
|
|
35
26
|
|
|
36
|
-
class InstParameters(ParameterBase)
|
|
37
|
-
def __init__(self,
|
|
38
|
-
super().__init__(
|
|
27
|
+
class InstParameters(ParameterBase)
|
|
28
|
+
def __init__(self, input_dataset_parameters, some_other_parameter):
|
|
29
|
+
super().__init__(input_dataset_parameters)
|
|
39
30
|
self._thing = self._some_function(some_other_parameters)
|
|
40
31
|
|
|
41
32
|
@property
|
|
@@ -43,7 +34,7 @@ class ParameterBase:
|
|
|
43
34
|
return self._find_most_recent_past_value("some_parameter_name")
|
|
44
35
|
|
|
45
36
|
@property
|
|
46
|
-
def
|
|
37
|
+
def complicate_parameter(self):
|
|
47
38
|
return self._some_complicated_parsing_function("complicated_parameter_name", another_argument)
|
|
48
39
|
|
|
49
40
|
|
|
@@ -64,16 +55,15 @@ class ParameterBase:
|
|
|
64
55
|
workflow_version=workflow_version,
|
|
65
56
|
)
|
|
66
57
|
|
|
67
|
-
self.parameters = InstParameters(
|
|
58
|
+
self.parameters = InstParameters(self.input_dataset_parameters) #<------ This is the important line
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
arguments can be passed if the subclass requires them.
|
|
60
|
+
Note that the first argument to the ConstantsSubclass with *always* be self.input_dataset_parameters, but
|
|
61
|
+
additional argument can be passed if the subclass requires them.
|
|
72
62
|
|
|
73
63
|
Parameters
|
|
74
64
|
----------
|
|
75
|
-
|
|
76
|
-
The
|
|
65
|
+
input_dataset_parameters
|
|
66
|
+
The input parameters
|
|
77
67
|
|
|
78
68
|
obs_ip_start_time
|
|
79
69
|
A string containing the start date of the Observe IP task type frames. Must be in isoformat.
|
|
@@ -84,53 +74,25 @@ class ParameterBase:
|
|
|
84
74
|
|
|
85
75
|
def __init__(
|
|
86
76
|
self,
|
|
87
|
-
|
|
77
|
+
input_dataset_parameters: dict[str, list[InputDatasetParameterValue]],
|
|
88
78
|
obs_ip_start_time: str | None = None,
|
|
89
79
|
**kwargs,
|
|
90
80
|
):
|
|
91
|
-
self.scratch = scratch
|
|
92
|
-
input_dataset_parameter_model = self._get_parameters_doc_from_file()
|
|
93
|
-
input_dataset_parameters = {}
|
|
94
|
-
if input_dataset_parameter_model is not None:
|
|
95
|
-
input_dataset_parameters = {
|
|
96
|
-
p.parameter_name: p.parameter_values for p in input_dataset_parameter_model.doc_list
|
|
97
|
-
}
|
|
98
81
|
self.input_dataset_parameters = input_dataset_parameters
|
|
99
|
-
|
|
100
82
|
if obs_ip_start_time is not None:
|
|
101
83
|
# Specifically `not None` because we want to error normally on badly formatted strings (including "").
|
|
102
84
|
self._obs_ip_start_datetime = datetime.fromisoformat(obs_ip_start_time)
|
|
103
85
|
else:
|
|
104
86
|
logger.info(
|
|
105
87
|
"WARNING: "
|
|
106
|
-
"The task containing this parameters object did not provide an obs ip start time
|
|
107
|
-
"
|
|
88
|
+
"The task containing this parameters object did not provide an obs ip start time. "
|
|
89
|
+
"This really only makes sense for Parsing tasks."
|
|
108
90
|
)
|
|
109
91
|
|
|
110
92
|
for parent_class in self.__class__.__bases__:
|
|
111
93
|
if hasattr(parent_class, "is_param_mixin"):
|
|
112
94
|
parent_class.__init__(self, **kwargs)
|
|
113
95
|
|
|
114
|
-
def _read_parameter_file(
|
|
115
|
-
self, tag: str, decoder: Callable[[Path], Any], **decoder_kwargs
|
|
116
|
-
) -> Any:
|
|
117
|
-
"""Read any file in the task scratch instance."""
|
|
118
|
-
paths = list(self.scratch.find_all(tags=tag))
|
|
119
|
-
if len(paths) == 0:
|
|
120
|
-
logger.info(f"WARNING: There is no parameter file for {tag = }")
|
|
121
|
-
if len(paths) == 1:
|
|
122
|
-
return decoder(paths[0], **decoder_kwargs)
|
|
123
|
-
if len(paths) > 1:
|
|
124
|
-
raise ValueError(f"There is more than one parameter file for {tag = }: {paths}")
|
|
125
|
-
|
|
126
|
-
def _get_parameters_doc_from_file(self) -> InputDatasetPartDocumentList:
|
|
127
|
-
"""Get parameters doc saved at the TransferL0Data task."""
|
|
128
|
-
tag = Tag.input_dataset_parameters()
|
|
129
|
-
parameters_from_file = self._read_parameter_file(
|
|
130
|
-
tag=tag, decoder=basemodel_decoder, model=InputDatasetPartDocumentList
|
|
131
|
-
)
|
|
132
|
-
return parameters_from_file
|
|
133
|
-
|
|
134
96
|
def _find_most_recent_past_value(
|
|
135
97
|
self,
|
|
136
98
|
parameter_name: str,
|
|
@@ -151,19 +113,20 @@ class ParameterBase:
|
|
|
151
113
|
)
|
|
152
114
|
return result
|
|
153
115
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _load_param_value_from_fits(param_dict: dict, hdu: int = 0) -> np.ndarray:
|
|
118
|
+
"""Load a numpy array from a parameter pointing to a FITS file."""
|
|
119
|
+
file_path = param_dict["param_path"]
|
|
120
|
+
|
|
121
|
+
hdul = fits.open(file_path)
|
|
122
|
+
return hdul[hdu].data
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _load_param_value_from_numpy_save(param_dict: dict) -> np.ndarray:
|
|
126
|
+
"""Return the data associated with a parameter file saved in numpy format."""
|
|
127
|
+
file_path = param_dict["param_path"]
|
|
128
|
+
result = np.load(file_path)
|
|
129
|
+
return result
|
|
167
130
|
|
|
168
131
|
|
|
169
132
|
class _ParamMixinBase:
|
|
@@ -31,3 +31,4 @@ class L1QualityFitsAccess(L1FitsAccess):
|
|
|
31
31
|
self.light_level: float = self.header["LIGHTLVL"]
|
|
32
32
|
self.health_status: str = self.header["DSHEALTH"]
|
|
33
33
|
self.ao_status: int = self.header.get("AO_LOCK", None)
|
|
34
|
+
self.num_out_of_bounds_ao_values: int = self.header.get("OOBSHIFT", None)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Mixin for a WorkflowDataTaskBase subclass which implements input data set access functionality."""
|
|
2
|
+
import json
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from itertools import chain
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from dkist_processing_common.models.tags import Tag
|
|
10
|
+
from dkist_processing_common.tasks.base import tag_type_hint
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
frames_part_type_hint = list[dict[str, str | list[str]]] | None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class InputDatasetParameterValue:
|
|
18
|
+
"""Data structure for a de-serialized input dataset parameter value."""
|
|
19
|
+
|
|
20
|
+
parameter_value_id: int
|
|
21
|
+
parameter_value: Any = None
|
|
22
|
+
parameter_value_start_date: datetime | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class InputDatasetObject:
|
|
27
|
+
"""Data structure for a de-serialized input dataset frame."""
|
|
28
|
+
|
|
29
|
+
bucket: str
|
|
30
|
+
object_key: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class InputDatasetMixin:
|
|
34
|
+
"""Mixin for WorkflowDataTaskBase that accesses downloaded input dataset part documents."""
|
|
35
|
+
|
|
36
|
+
def _input_dataset_part_document(self, tags: tag_type_hint):
|
|
37
|
+
"""Get the input dataset document part and deserialize it."""
|
|
38
|
+
paths: list[Path] = list(self.read(tags=tags))
|
|
39
|
+
if not paths:
|
|
40
|
+
return
|
|
41
|
+
if len(paths) > 1:
|
|
42
|
+
raise ValueError(
|
|
43
|
+
f"There are more than one input dataset part documents to parse for {tags=}"
|
|
44
|
+
)
|
|
45
|
+
p = paths[0]
|
|
46
|
+
with p.open(mode="rb") as f:
|
|
47
|
+
return json.load(f)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def input_dataset_observe_frames_part_document(self) -> frames_part_type_hint:
|
|
51
|
+
"""Get the 'observe frames' part of the input dataset."""
|
|
52
|
+
return self._input_dataset_part_document(tags=Tag.input_dataset_observe_frames())
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def input_dataset_calibration_frames_part_document(self) -> frames_part_type_hint:
|
|
56
|
+
"""Get the 'calibration frames' part of the input dataset."""
|
|
57
|
+
return self._input_dataset_part_document(tags=Tag.input_dataset_calibration_frames())
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def input_dataset_parameters_part_document(
|
|
61
|
+
self,
|
|
62
|
+
) -> list[dict[str, str | list[dict[str, int | str]]]] | None:
|
|
63
|
+
"""Get the 'parameters' part of the input dataset."""
|
|
64
|
+
return self._input_dataset_part_document(tags=Tag.input_dataset_parameters())
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def input_dataset_frames(self) -> list[InputDatasetObject]:
|
|
68
|
+
"""Get the list of frames for this input dataset."""
|
|
69
|
+
result = []
|
|
70
|
+
observe_frames = self.input_dataset_observe_frames_part_document or []
|
|
71
|
+
calibration_frames = self.input_dataset_calibration_frames_part_document or []
|
|
72
|
+
for frame_set in chain(observe_frames, calibration_frames):
|
|
73
|
+
for key in frame_set.get("object_keys", list()):
|
|
74
|
+
result.append(InputDatasetObject(bucket=frame_set["bucket"], object_key=key))
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def input_dataset_parameters(self) -> dict[str, list[InputDatasetParameterValue]]:
|
|
79
|
+
"""Get the input dataset parameters."""
|
|
80
|
+
parameters = self.input_dataset_parameters_part_document or []
|
|
81
|
+
result = dict()
|
|
82
|
+
for p in parameters:
|
|
83
|
+
result.update(self._input_dataset_parse_parameter(p))
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def input_dataset_parameter_objects(self) -> list[InputDatasetObject]:
|
|
88
|
+
"""Parse the parameter object locations out of the set of all parameters."""
|
|
89
|
+
result = []
|
|
90
|
+
for value_list in self.input_dataset_parameters.values():
|
|
91
|
+
for value in value_list:
|
|
92
|
+
param_value = value.parameter_value
|
|
93
|
+
if isinstance(param_value, dict) and param_value.get("is_file", False):
|
|
94
|
+
result.append(
|
|
95
|
+
InputDatasetObject(
|
|
96
|
+
bucket=param_value["bucket"], object_key=param_value["objectKey"]
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
def _input_dataset_parse_parameter(
|
|
102
|
+
self, parameter: dict
|
|
103
|
+
) -> dict[str, list[InputDatasetParameterValue]]:
|
|
104
|
+
name: str = parameter["parameterName"]
|
|
105
|
+
raw_values: list[dict] = parameter["parameterValues"]
|
|
106
|
+
values = self._input_dataset_parse_parameter_values(raw_values=raw_values)
|
|
107
|
+
return {name: values}
|
|
108
|
+
|
|
109
|
+
def _input_dataset_parse_parameter_values(
|
|
110
|
+
self, raw_values: list[dict[str, Any]]
|
|
111
|
+
) -> list[InputDatasetParameterValue]:
|
|
112
|
+
values = list()
|
|
113
|
+
for v in raw_values:
|
|
114
|
+
parsed_value = InputDatasetParameterValue(parameter_value_id=v["parameterValueId"])
|
|
115
|
+
parsed_value.parameter_value = self._input_dataset_parse_parameter_value(
|
|
116
|
+
raw_parameter_value=v["parameterValue"]
|
|
117
|
+
)
|
|
118
|
+
if d := v.get("parameterValueStartDate"):
|
|
119
|
+
parsed_value.parameter_value_start_date = datetime.fromisoformat(d)
|
|
120
|
+
else:
|
|
121
|
+
parsed_value.parameter_value_start_date = datetime(1, 1, 1)
|
|
122
|
+
values.append(parsed_value)
|
|
123
|
+
return values
|
|
124
|
+
|
|
125
|
+
def _input_dataset_parse_parameter_value(self, raw_parameter_value: str) -> Any:
|
|
126
|
+
"""Return the json decoding of the parameter value."""
|
|
127
|
+
return json.loads(raw_parameter_value, object_hook=self._decode_parameter_value)
|
|
128
|
+
|
|
129
|
+
def _decode_parameter_value(self, param_dict: dict):
|
|
130
|
+
"""Decode a parameter value."""
|
|
131
|
+
if "__file__" in param_dict:
|
|
132
|
+
return self._convert_parameter_file_value_to_path(param_dict)
|
|
133
|
+
# Nothing to do here, so return control back to json.loads()
|
|
134
|
+
return param_dict
|
|
135
|
+
|
|
136
|
+
def _convert_parameter_file_value_to_path(self, param_dict: dict):
|
|
137
|
+
"""
|
|
138
|
+
Decode a parameter file value.
|
|
139
|
+
|
|
140
|
+
Note: for parameters that are files, the string passed to json.loads() looks like this:
|
|
141
|
+
'{ "__file__":
|
|
142
|
+
{
|
|
143
|
+
"bucket": "data",
|
|
144
|
+
"objectKey": "parameters/parameter_name/uuid.dat"
|
|
145
|
+
}
|
|
146
|
+
}'
|
|
147
|
+
|
|
148
|
+
In this hook, we remove the outer __file__ dict and return the inner dict with the addition of
|
|
149
|
+
a key and value for the file path. Because the file path is dependent on the existence of a tag,
|
|
150
|
+
if no tag is found, None is returned for the path. This use case will occur when we construct
|
|
151
|
+
the list of files to be transferred and tagged by the TransferL0Data task.
|
|
152
|
+
"""
|
|
153
|
+
file_dict = param_dict["__file__"]
|
|
154
|
+
object_key = file_dict["objectKey"]
|
|
155
|
+
parameter_tag = Tag.parameter(Path(object_key).name)
|
|
156
|
+
paths = list(self.read(tags=parameter_tag))
|
|
157
|
+
num_paths = len(paths)
|
|
158
|
+
if num_paths == 1:
|
|
159
|
+
param_path = paths[0]
|
|
160
|
+
elif num_paths == 0:
|
|
161
|
+
param_path = None
|
|
162
|
+
else:
|
|
163
|
+
raise ValueError(f"Found multiple paths for {parameter_tag = }.")
|
|
164
|
+
file_dict["param_path"] = param_path
|
|
165
|
+
file_dict["is_file"] = True
|
|
166
|
+
return file_dict
|
|
@@ -210,19 +210,16 @@ class MetadataStoreMixin:
|
|
|
210
210
|
self, part_type: Literal["observe_frames", "calibration_frames", "parameters"]
|
|
211
211
|
) -> InputDatasetPartResponse:
|
|
212
212
|
"""Get the input dataset part by input dataset part type name."""
|
|
213
|
-
|
|
214
|
-
input_dataset_part = None
|
|
213
|
+
part_type_dict = {}
|
|
215
214
|
parts = (
|
|
216
215
|
self.metadata_store_input_dataset_recipe_run.recipeInstance.inputDataset.inputDatasetInputDatasetParts
|
|
217
216
|
)
|
|
218
217
|
for part in parts:
|
|
219
218
|
part_type_name = part.inputDatasetPart.inputDatasetPartType.inputDatasetPartTypeName
|
|
220
|
-
if part_type_name in
|
|
219
|
+
if part_type_name in part_type_dict.keys():
|
|
221
220
|
raise ValueError(f"Multiple input dataset parts found for {part_type_name=}.")
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
input_dataset_part = part.inputDatasetPart
|
|
225
|
-
return input_dataset_part
|
|
221
|
+
part_type_dict[part_type_name] = part.inputDatasetPart
|
|
222
|
+
return part_type_dict.get(part_type)
|
|
226
223
|
|
|
227
224
|
@property
|
|
228
225
|
def metadata_store_input_dataset_observe_frames(self) -> InputDatasetPartResponse:
|
|
@@ -20,6 +20,7 @@ from dkist_processing_pac.fitter.fitting_core import compare_I
|
|
|
20
20
|
from dkist_processing_pac.fitter.polcal_fitter import PolcalFitter
|
|
21
21
|
from pandas import DataFrame
|
|
22
22
|
|
|
23
|
+
from dkist_processing_common.models.fried_parameter import r0_valid
|
|
23
24
|
from dkist_processing_common.models.metric_code import MetricCode
|
|
24
25
|
from dkist_processing_common.models.quality import EfficiencyHistograms
|
|
25
26
|
from dkist_processing_common.models.quality import ModulationMatrixHistograms
|
|
@@ -203,7 +204,7 @@ class _SimplePlotQualityMixin:
|
|
|
203
204
|
return warnings
|
|
204
205
|
|
|
205
206
|
def quality_store_ao_status_and_fried_parameter(
|
|
206
|
-
self, datetimes: list[str], values: list[list[bool
|
|
207
|
+
self, datetimes: list[str], values: list[list[bool | float]]
|
|
207
208
|
):
|
|
208
209
|
"""
|
|
209
210
|
Collect and store datetime / value pairs for the boolean AO status and Fried parameter.
|
|
@@ -213,23 +214,27 @@ class _SimplePlotQualityMixin:
|
|
|
213
214
|
Because of how L1Metric.has_metric works, empty lists will not be passed to this method.
|
|
214
215
|
However, because of how L1Metric.store_metric works, one or both values can be None.
|
|
215
216
|
"""
|
|
216
|
-
|
|
217
|
-
ao_not_none = [ao for ao in
|
|
217
|
+
ao_lock_values = [value[0] for value in values]
|
|
218
|
+
ao_not_none = [ao for ao in ao_lock_values if ao is not None]
|
|
218
219
|
if len(ao_not_none) != 0:
|
|
219
220
|
self._record_values(values=ao_not_none, tags=Tag.quality(MetricCode.ao_status))
|
|
220
221
|
fried_values = [value[1] for value in values]
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
]
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
222
|
+
ao_oob_values = [value[2] for value in values]
|
|
223
|
+
fried_values_to_plot = []
|
|
224
|
+
datetimes_to_plot = []
|
|
225
|
+
# For each set of input data, check if the r0 is considered valid based on all data
|
|
226
|
+
for i in range(len(fried_values)):
|
|
227
|
+
if r0_valid(
|
|
228
|
+
r0=fried_values[i],
|
|
229
|
+
ao_lock=ao_lock_values[i],
|
|
230
|
+
num_out_of_bounds_ao_values=ao_oob_values[i],
|
|
231
|
+
):
|
|
232
|
+
fried_values_to_plot.append(fried_values[i])
|
|
233
|
+
datetimes_to_plot.append(datetimes[i])
|
|
234
|
+
if len(fried_values_to_plot) != 0:
|
|
230
235
|
self._record_2d_plot_values(
|
|
231
|
-
x_values=
|
|
232
|
-
y_values=
|
|
236
|
+
x_values=datetimes_to_plot,
|
|
237
|
+
y_values=fried_values_to_plot,
|
|
233
238
|
tags=Tag.quality(MetricCode.fried_parameter),
|
|
234
239
|
)
|
|
235
240
|
|
|
@@ -296,7 +296,7 @@ class QualityL1Metrics(WorkflowTaskBase, QualityMixin):
|
|
|
296
296
|
L1Metric(storage_method=self.quality_store_health_status, value_source="health_status"),
|
|
297
297
|
L1Metric(
|
|
298
298
|
storage_method=self.quality_store_ao_status_and_fried_parameter,
|
|
299
|
-
value_source=["ao_status", "fried_parameter"],
|
|
299
|
+
value_source=["ao_status", "fried_parameter", "num_out_of_bounds_ao_values"],
|
|
300
300
|
),
|
|
301
301
|
]
|
|
302
302
|
|