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
@@ -5,7 +5,6 @@ import pytest
5
5
  from dkist_processing_common._util.scratch import WorkflowFileSystem
6
6
  from dkist_processing_common.models.tags import Tag
7
7
  from dkist_processing_common.tasks import TransferL1Data
8
- from dkist_processing_common.tests.conftest import FakeGQLClient
9
8
 
10
9
 
11
10
  def fake_list_objects(self, bucket, prefix=None):
@@ -37,7 +36,7 @@ def transfer_l1_data_task(recipe_run_id, tmp_path, fake_constants_db):
37
36
  task._purge()
38
37
 
39
38
 
40
- def test_transfer_l1_data(transfer_l1_data_task, mocker):
39
+ def test_transfer_l1_data(transfer_l1_data_task, mocker, fake_gql_client):
41
40
  """
42
41
  Given: A task with frames and movies tagged as output
43
42
  When: Transfering the L1 data
@@ -45,7 +44,7 @@ def test_transfer_l1_data(transfer_l1_data_task, mocker):
45
44
  """
46
45
  # Yeah, we mock a whole bunch of stuff here, but this test at least confirms that the setup to these calls is correct
47
46
  mocker.patch(
48
- "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
47
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=fake_gql_client
49
48
  )
50
49
  mocker.patch(
51
50
  "dkist_processing_common.tasks.mixin.globus.GlobusMixin.globus_transfer_scratch_to_object_store"
@@ -1,4 +1,5 @@
1
1
  """Tests for the tasks.trial_catalog module."""
2
+
2
3
  from pathlib import Path
3
4
  from string import ascii_uppercase
4
5
  from uuid import uuid4
@@ -7,19 +8,34 @@ import astropy.units as u
7
8
  import pytest
8
9
  from astropy.io import fits
9
10
  from dkist_data_simulator.spec214.vbi import SimpleVBIDataset
10
- from hashids import Hashids
11
+ from sqids import Sqids
11
12
 
12
13
  from dkist_processing_common._util.scratch import WorkflowFileSystem
13
14
  from dkist_processing_common.codecs.asdf import asdf_decoder
15
+ from dkist_processing_common.codecs.basemodel import basemodel_encoder
14
16
  from dkist_processing_common.codecs.bytes import bytes_decoder
15
17
  from dkist_processing_common.codecs.fits import fits_hdulist_encoder
16
18
  from dkist_processing_common.codecs.json import json_decoder
17
19
  from dkist_processing_common.codecs.quality import quality_data_encoder
20
+ from dkist_processing_common.models.input_dataset import InputDatasetParameter
21
+ from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
18
22
  from dkist_processing_common.models.tags import Tag
19
23
  from dkist_processing_common.tasks import CreateTrialAsdf
20
24
  from dkist_processing_common.tasks import CreateTrialDatasetInventory
21
25
  from dkist_processing_common.tasks import CreateTrialQualityReport
22
- from dkist_processing_common.tests.conftest import FakeGQLClientNoRecipeConfiguration
26
+ from dkist_processing_common.tests.mock_metadata_store import input_dataset_parameters_part_factory
27
+
28
+
29
+ @pytest.fixture()
30
+ def mock_input_dataset_parts() -> InputDatasetPartDocumentList:
31
+ """An InputDatasetPartDocumentList with two parameters, each with one value and a date."""
32
+ raw = input_dataset_parameters_part_factory(
33
+ parameter_count=2,
34
+ parameter_value_count=1,
35
+ has_date=True,
36
+ has_file=False,
37
+ )
38
+ return InputDatasetPartDocumentList.model_validate({"doc_list": raw})
23
39
 
24
40
 
25
41
  @pytest.fixture()
@@ -41,17 +57,40 @@ def scratch_with_l1_frames(recipe_run_id, tmp_path) -> WorkflowFileSystem:
41
57
  scratch.write(
42
58
  file_obj, tags=[Tag.output(), Tag.frame()], relative_path=f"{uuid4().hex}.dat"
43
59
  )
60
+
61
+ return scratch
62
+
63
+
64
+ @pytest.fixture()
65
+ def scratch_with_l1_frames_and_parameters(
66
+ scratch_with_l1_frames, mock_input_dataset_parts
67
+ ) -> WorkflowFileSystem:
68
+ """Scratch instance for a recipe run id with tagged L1 frames and input parameters."""
69
+ scratch = scratch_with_l1_frames
70
+
71
+ # Write validated Pydantic model bytes expected by InputDatasetPartDocumentList
72
+ file_obj = basemodel_encoder(mock_input_dataset_parts)
73
+ scratch.write(
74
+ file_obj,
75
+ tags=Tag.input_dataset_parameters(),
76
+ relative_path=f"{uuid4().hex}.json",
77
+ )
44
78
  return scratch
45
79
 
46
80
 
47
81
  @pytest.fixture()
48
82
  def create_trial_dataset_inventory_task(
49
- recipe_run_id, tmp_path, scratch_with_l1_frames, fake_constants_db, mocker
83
+ recipe_run_id,
84
+ tmp_path,
85
+ scratch_with_l1_frames,
86
+ fake_constants_db,
87
+ mocker,
88
+ fake_gql_client,
50
89
  ) -> CreateTrialDatasetInventory:
51
90
  """An instance of CreateTrialDatasetInventory with L1 frames tagged in scratch."""
52
91
  mocker.patch(
53
92
  "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient",
54
- new=FakeGQLClientNoRecipeConfiguration,
93
+ new=fake_gql_client,
55
94
  )
56
95
  task = CreateTrialDatasetInventory(
57
96
  recipe_run_id=recipe_run_id,
@@ -80,6 +119,22 @@ def create_trial_asdf_task(
80
119
  task._purge()
81
120
 
82
121
 
122
+ @pytest.fixture(scope="function")
123
+ def create_trial_asdf_task_with_params(
124
+ recipe_run_id, tmp_path, scratch_with_l1_frames_and_parameters, fake_constants_db
125
+ ) -> CreateTrialAsdf:
126
+ """An instance of CreateTrialAsdf with L1 frames and input parameters tagged in scratch."""
127
+ task = CreateTrialAsdf(
128
+ recipe_run_id=recipe_run_id,
129
+ workflow_name="trial_asdf",
130
+ workflow_version="trial_asdf_version",
131
+ )
132
+ task.scratch = scratch_with_l1_frames_and_parameters
133
+ task.constants._update(fake_constants_db)
134
+ yield task
135
+ task._purge()
136
+
137
+
83
138
  @pytest.fixture()
84
139
  def create_trial_quality_report_task(
85
140
  recipe_run_id, tmp_path, fake_constants_db
@@ -138,25 +193,32 @@ def test_create_trial_dataset_inventory(create_trial_dataset_inventory_task):
138
193
  assert len(inventory) > 20 # a bunch
139
194
 
140
195
 
141
- def test_create_trial_asdf(create_trial_asdf_task, recipe_run_id):
196
+ @pytest.mark.parametrize("with_params", [False, True], ids=["no_params", "with_params"])
197
+ def test_create_trial_asdf(with_params, request, recipe_run_id, mock_input_dataset_parts):
142
198
  """
143
199
  :Given: An instance of CreateTrialAsdf with L1 frames tagged in scratch
144
200
  :When: CreateTrialAsdf is run
145
201
  :Then: An asdf file for the dataset is tagged in scratch
146
202
  """
147
- task = create_trial_asdf_task
203
+ task = request.getfixturevalue(
204
+ "create_trial_asdf_task_with_params" if with_params else "create_trial_asdf_task"
205
+ )
148
206
  # When
149
207
  task()
208
+
150
209
  # Then
151
210
  asdf_tags = [Tag.output(), Tag.asdf()]
152
211
  filepaths = list(task.scratch.find_all(tags=asdf_tags))
153
212
  assert len(filepaths) == 1
154
- dataset_id = Hashids(min_length=5, alphabet=ascii_uppercase).encode(recipe_run_id)
155
- assert filepaths[0].name == f"INSTRUMENT_L1_20240416T160000_{dataset_id}_user_tools.asdf"
213
+ dataset_id = Sqids(min_length=6, alphabet=ascii_uppercase).encode([recipe_run_id])
214
+ assert filepaths[0].name == f"INSTRUMENT_L1_20240416T160000_{dataset_id}_metadata.asdf"
215
+
156
216
  results = list(task.read(tags=asdf_tags, decoder=asdf_decoder))
157
217
  assert len(results) == 1
218
+
158
219
  tree = results[0]
159
220
  assert isinstance(tree, dict)
221
+
160
222
  for file_name in tree["dataset"].files.filenames:
161
223
  # This is a slightly better than check that `not Path(file_name).is_absolute()` because it confirms
162
224
  # we've correctly stripped the path of *all* parents (not just those that start at root).
@@ -164,6 +226,19 @@ def test_create_trial_asdf(create_trial_asdf_task, recipe_run_id):
164
226
  # `scratch.workflow_base_path`
165
227
  assert Path(file_name).name == file_name
166
228
 
229
+ # Only check parameters when present
230
+ ds = tree["dataset"]
231
+ assert "parameters" in ds.meta
232
+ parameters = ds.meta["parameters"]
233
+ assert isinstance(parameters, list)
234
+ if with_params:
235
+ assert parameters, f"ASDF tree must include input parameters: {parameters}"
236
+ assert len(parameters) == len(mock_input_dataset_parts.doc_list)
237
+ for param in parameters:
238
+ assert InputDatasetParameter.model_validate(param) in mock_input_dataset_parts.doc_list
239
+ else:
240
+ assert ds.meta["parameters"] == []
241
+
167
242
 
168
243
  def test_create_trial_quality_report(create_trial_quality_report_task):
169
244
  """
@@ -6,11 +6,13 @@ import pytest
6
6
  from pydantic.dataclasses import dataclass as validating_dataclass
7
7
 
8
8
  from dkist_processing_common._util.scratch import WorkflowFileSystem
9
- from dkist_processing_common.models.graphql import RecipeRunResponse
9
+ from dkist_processing_common.models.graphql import RecipeRunConfiguration
10
10
  from dkist_processing_common.models.tags import Tag
11
11
  from dkist_processing_common.tasks.mixin.globus import GlobusTransferItem
12
12
  from dkist_processing_common.tasks.trial_output_data import TransferTrialData
13
- from dkist_processing_common.tests.conftest import FakeGQLClient
13
+ from dkist_processing_common.tests.mock_metadata_store import RecipeRunResponseMapping
14
+ from dkist_processing_common.tests.mock_metadata_store import fake_gql_client_factory
15
+ from dkist_processing_common.tests.mock_metadata_store import make_default_recipe_run_response
14
16
 
15
17
 
16
18
  @pytest.fixture
@@ -19,48 +21,46 @@ def destination_bucket() -> str:
19
21
 
20
22
 
21
23
  @pytest.fixture
22
- def recipe_run_configuration(
24
+ def fake_gql_client_recipe_run_configuration(
23
25
  custom_root_name,
24
26
  custom_dir_name,
25
27
  destination_bucket,
26
28
  ):
27
- class GQLClientWithConfiguration(FakeGQLClient):
28
- def execute_gql_query(self, **kwargs):
29
- response = super().execute_gql_query(**kwargs)
30
- if isinstance(response, list):
31
- if isinstance(response[0], RecipeRunResponse):
32
- response[0].configuration = json.dumps(
33
- {
34
- "trial_root_directory_name": custom_root_name,
35
- "trial_directory_name": custom_dir_name,
36
- "destination_bucket": destination_bucket,
37
- }
38
- )
39
- return response
40
-
41
- return GQLClientWithConfiguration
29
+ recipe_run_response = make_default_recipe_run_response()
30
+ configuration = RecipeRunConfiguration(
31
+ trial_root_directory_name=custom_root_name,
32
+ trial_directory_name=custom_dir_name,
33
+ destination_bucket=destination_bucket,
34
+ )
35
+ recipe_run_response.configuration = configuration.model_dump_json()
36
+
37
+ new_response_mapping = RecipeRunResponseMapping(response=recipe_run_response)
38
+ FakeGQLClientWithConfiguration = fake_gql_client_factory(
39
+ response_mapping_override=new_response_mapping
40
+ )
41
+
42
+ return FakeGQLClientWithConfiguration
42
43
 
43
44
 
44
45
  @pytest.fixture
45
- def recipe_run_configuration_with_tag_lists(
46
+ def fake_gql_client_recipe_run_configuration_with_tag_lists(
46
47
  custom_root_name, custom_dir_name, destination_bucket, exclusive_tag_lists
47
48
  ):
48
- class GQLClientWithConfiguration(FakeGQLClient):
49
- def execute_gql_query(self, **kwargs):
50
- response = super().execute_gql_query(**kwargs)
51
- if isinstance(response, list):
52
- if isinstance(response[0], RecipeRunResponse):
53
- response[0].configuration = json.dumps(
54
- {
55
- "trial_root_directory_name": custom_root_name,
56
- "trial_directory_name": custom_dir_name,
57
- "destination_bucket": destination_bucket,
58
- "trial_exclusive_transfer_tag_lists": exclusive_tag_lists,
59
- }
60
- )
61
- return response
62
-
63
- return GQLClientWithConfiguration
49
+ recipe_run_response = make_default_recipe_run_response()
50
+ configuration = RecipeRunConfiguration(
51
+ trial_root_directory_name=custom_root_name,
52
+ trial_directory_name=custom_dir_name,
53
+ destination_bucket=destination_bucket,
54
+ trial_exclusive_transfer_tag_lists=exclusive_tag_lists,
55
+ )
56
+ recipe_run_response.configuration = configuration.model_dump_json()
57
+
58
+ new_response_mapping = RecipeRunResponseMapping(response=recipe_run_response)
59
+ FakeGQLClientWithConfiguration = fake_gql_client_factory(
60
+ response_mapping_override=new_response_mapping
61
+ )
62
+
63
+ return FakeGQLClientWithConfiguration
64
64
 
65
65
 
66
66
  @pytest.fixture
@@ -70,11 +70,11 @@ def trial_output_task() -> type[TransferTrialData]:
70
70
 
71
71
  @pytest.fixture
72
72
  def basic_trial_output_task(
73
- recipe_run_id, recipe_run_configuration, trial_output_task, tmp_path, mocker
73
+ recipe_run_id, fake_gql_client_recipe_run_configuration, trial_output_task, tmp_path, mocker
74
74
  ):
75
75
  mocker.patch(
76
76
  "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient",
77
- new=recipe_run_configuration,
77
+ new=fake_gql_client_recipe_run_configuration,
78
78
  )
79
79
  proposal_id = "test_proposal_id"
80
80
  with trial_output_task(
@@ -121,10 +121,10 @@ class OutputFileNames:
121
121
  def complete_trial_output_task(
122
122
  request, recipe_run_id, trial_output_task, tmp_path, mocker
123
123
  ) -> tuple[TransferTrialData, str, OutputFileObjects]:
124
- recipe_run = request.param
124
+ fake_gql_client = request.param
125
125
  mocker.patch(
126
126
  "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient",
127
- new=request.getfixturevalue(recipe_run),
127
+ new=request.getfixturevalue(fake_gql_client),
128
128
  )
129
129
  proposal_id = "test_proposal_id"
130
130
  with trial_output_task(
@@ -158,13 +158,12 @@ def complete_trial_output_task(
158
158
  task.write(asdf_file_obj, relative_path=asdf_file_name, tags=[Tag.output(), Tag.asdf()])
159
159
 
160
160
  # Write quality data
161
- # quality data is not tagged as OUTPUT
162
161
  quality_data_obj = uuid4().hex.encode("utf8")
163
162
  quality_data_name = "quality_data.json"
164
163
  task.write(
165
164
  quality_data_obj,
166
165
  relative_path=quality_data_name,
167
- tags=Tag.quality_data(),
166
+ tags=[Tag.output(), Tag.quality_data()],
168
167
  )
169
168
 
170
169
  # Write a quality report file
@@ -241,7 +240,9 @@ def test_format_object_key(
241
240
  )
242
241
 
243
242
 
244
- @pytest.mark.parametrize("complete_trial_output_task", ["recipe_run_configuration"], indirect=True)
243
+ @pytest.mark.parametrize(
244
+ "complete_trial_output_task", ["fake_gql_client_recipe_run_configuration"], indirect=True
245
+ )
245
246
  @pytest.mark.parametrize(
246
247
  "custom_root_name, custom_dir_name",
247
248
  [
@@ -289,7 +290,9 @@ def test_build_transfer_list(
289
290
 
290
291
 
291
292
  @pytest.mark.parametrize(
292
- "complete_trial_output_task", ["recipe_run_configuration_with_tag_lists"], indirect=True
293
+ "complete_trial_output_task",
294
+ ["fake_gql_client_recipe_run_configuration_with_tag_lists"],
295
+ indirect=True,
293
296
  )
294
297
  @pytest.mark.parametrize(
295
298
  "custom_root_name, custom_dir_name, exclusive_tag_lists, expected_output",
@@ -332,33 +335,3 @@ def test_build_transfer_list_with_exclusive_tag_lists(
332
335
  assert transfer_item.destination_path == expected_destination_path
333
336
  with transfer_item.source_path.open(mode="rb") as f:
334
337
  assert file_obj == f.read()
335
-
336
-
337
- @pytest.mark.parametrize(
338
- "complete_trial_output_task", ["recipe_run_configuration_with_tag_lists"], indirect=True
339
- )
340
- @pytest.mark.parametrize(
341
- "custom_root_name, custom_dir_name, exclusive_tag_lists",
342
- [
343
- pytest.param(
344
- None, None, [Tag.task("TASKY_MCTASKERSON")], id="Default trial dir and trial root names"
345
- )
346
- ],
347
- )
348
- def test_build_transfer_list_with_bad_exclusive_tag_lists(
349
- complete_trial_output_task,
350
- destination_bucket,
351
- custom_dir_name,
352
- custom_root_name,
353
- exclusive_tag_lists,
354
- ):
355
- """
356
- :Given: A Task based on TrialTransferDataBase
357
- :When: The exclusive tag list is a simple list instead of the required list of lists
358
- :Then: The correct value error is raised
359
- """
360
- task, proposal_id, output_file_objects, output_file_names = complete_trial_output_task
361
-
362
- with pytest.raises(ValueError) as ve:
363
- transfer_list = task.build_transfer_list()
364
- assert f"tag_lists={exclusive_tag_lists} must" in str(ve)
@@ -2,13 +2,12 @@ import json
2
2
  import logging
3
3
  import re
4
4
  import tomllib
5
- from dataclasses import asdict
5
+ from importlib.metadata import version
6
6
  from pathlib import Path
7
7
  from string import ascii_uppercase
8
8
 
9
9
  import pytest
10
- from hashids import Hashids
11
- from pkg_resources import get_distribution
10
+ from sqids import Sqids
12
11
 
13
12
  import dkist_processing_common
14
13
  from dkist_processing_common._util.scratch import WorkflowFileSystem
@@ -183,16 +182,15 @@ def test_dataset_id(workflow_data_task):
183
182
  Then: the dataset id hashed from the recipe run id is returned
184
183
  """
185
184
  task = workflow_data_task[0]
186
- assert task.constants.dataset_id == Hashids(min_length=5, alphabet=ascii_uppercase).encode(
187
- task.recipe_run_id
188
- )
185
+ expected_dataset_id = Sqids(min_length=6, alphabet=ascii_uppercase).encode([task.recipe_run_id])
186
+ assert len(expected_dataset_id) >= 6
187
+ assert task.constants.dataset_id == expected_dataset_id
189
188
 
190
189
 
191
190
  class ProvenanceTask(WorkflowTaskBase):
192
191
  record_provenance = True
193
192
 
194
- def run(self):
195
- ...
193
+ def run(self): ...
196
194
 
197
195
  # Because I couldn't figure out how to mock the mixin
198
196
  def metadata_store_record_provenance(self, is_task_manual: bool, library_versions: str):
@@ -204,7 +202,7 @@ class ProvenanceTask(WorkflowTaskBase):
204
202
  libraryVersions=library_versions,
205
203
  workflowVersion=self.workflow_version,
206
204
  )
207
- self.write(data=bytes(json.dumps(asdict(params)), "utf-8"), tags=["TEST_PROVENANCE"])
205
+ self.write(data=bytes(params.model_dump_json(), "utf-8"), tags=["TEST_PROVENANCE"])
208
206
 
209
207
 
210
208
  @pytest.fixture(scope="function")
@@ -272,7 +270,7 @@ def test_library_versions(provenance_task, package_dependencies):
272
270
  # installed packages.
273
271
  for package in package_dependencies:
274
272
  assert package in libraries
275
- assert libraries[package] == get_distribution(package).version
273
+ assert libraries[package] == version(package)
276
274
 
277
275
 
278
276
  def test_record_provenance(provenance_task):