@opentrons/shared-data 0.0.1-alpha.20 → 0.0.1-alpha.21

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 (151) hide show
  1. package/package.json +1 -1
  2. package/js/__tests__/__snapshots__/pipettes.test.ts.snap +0 -5969
  3. package/js/__tests__/deckSchemas.test.ts +0 -87
  4. package/js/__tests__/errors.test.js +0 -21
  5. package/js/__tests__/getAreSlotsAdjacent.test.ts +0 -128
  6. package/js/__tests__/getWellNamePerMultiTip.test.ts +0 -162
  7. package/js/__tests__/labwareDefQuirks.test.ts +0 -37
  8. package/js/__tests__/labwareDefSchemaV1.test.ts +0 -82
  9. package/js/__tests__/labwareDefSchemaV2.test.ts +0 -298
  10. package/js/__tests__/moduleAccessors.test.ts +0 -50
  11. package/js/__tests__/moduleSpecsSchema.test.ts +0 -77
  12. package/js/__tests__/pipetteSchemaV2.test.ts +0 -107
  13. package/js/__tests__/pipetteSpecSchemas.test.ts +0 -48
  14. package/js/__tests__/pipettes.test.ts +0 -58
  15. package/js/__tests__/protocolSchemaV4.test.ts +0 -137
  16. package/js/__tests__/protocolSchemaV5.test.ts +0 -137
  17. package/js/__tests__/protocolSchemaV6.test.ts +0 -132
  18. package/js/__tests__/protocolSchemaV7.test.ts +0 -64
  19. package/js/__tests__/protocolValidation.test.ts +0 -83
  20. package/js/__tests__/sortWells.test.ts +0 -40
  21. package/js/__tests__/splitWellsOnColumn.test.ts +0 -21
  22. package/js/__tests__/validateErrors.test.js +0 -34
  23. package/js/constants.ts +0 -338
  24. package/js/cypressUtils.ts +0 -35
  25. package/js/errors.ts +0 -33
  26. package/js/fixtures.ts +0 -161
  27. package/js/getLabware.ts +0 -154
  28. package/js/gripper.ts +0 -29
  29. package/js/helpers/__tests__/getAdapterName.test.ts +0 -31
  30. package/js/helpers/__tests__/getDeckDefFromLoadedLabware.test.ts +0 -12
  31. package/js/helpers/__tests__/getVectorDifference.test.ts +0 -25
  32. package/js/helpers/__tests__/getVectorSum.test.ts +0 -21
  33. package/js/helpers/__tests__/labwareInference.test.ts +0 -84
  34. package/js/helpers/__tests__/orderWells.test.ts +0 -87
  35. package/js/helpers/__tests__/parseProtocolData.test.ts +0 -165
  36. package/js/helpers/__tests__/volume.test.ts +0 -108
  37. package/js/helpers/__tests__/wellSets.test.ts +0 -352
  38. package/js/helpers/get96Channel384WellPlateWells.ts +0 -44
  39. package/js/helpers/getAddressableAreasInProtocol.ts +0 -59
  40. package/js/helpers/getLoadedLabwareDefinitionsByUri.ts +0 -30
  41. package/js/helpers/getModuleVizDims.ts +0 -118
  42. package/js/helpers/getOccludedSlotCountForModule.ts +0 -13
  43. package/js/helpers/getVectorDifference.ts +0 -12
  44. package/js/helpers/getVectorSum.ts +0 -12
  45. package/js/helpers/getWellNamePerMultiTip.ts +0 -86
  46. package/js/helpers/getWellTotalVolume.ts +0 -16
  47. package/js/helpers/index.ts +0 -347
  48. package/js/helpers/labwareInference.ts +0 -92
  49. package/js/helpers/orderWells.ts +0 -69
  50. package/js/helpers/parseProtocolData.ts +0 -141
  51. package/js/helpers/volume.ts +0 -35
  52. package/js/helpers/wellIsRect.ts +0 -6
  53. package/js/helpers/wellSets.ts +0 -155
  54. package/js/index.ts +0 -14
  55. package/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.ts.snap +0 -3
  56. package/js/labwareTools/__tests__/__snapshots__/createLabware.test.ts.snap +0 -3
  57. package/js/labwareTools/__tests__/createDefaultDisplayName.test.ts +0 -111
  58. package/js/labwareTools/__tests__/createIrregularLabware.test.ts +0 -226
  59. package/js/labwareTools/__tests__/createLabware.test.ts +0 -147
  60. package/js/labwareTools/index.ts +0 -452
  61. package/js/modules.ts +0 -97
  62. package/js/pipettes.ts +0 -89
  63. package/js/protocols.ts +0 -290
  64. package/js/schema.ts +0 -80
  65. package/js/scripts/generateDeckLayersFromSVG.js +0 -46
  66. package/js/titleCase.ts +0 -30
  67. package/js/types.ts +0 -600
  68. package/python/.coveragerc +0 -4
  69. package/python/.flake8 +0 -32
  70. package/python/Config.in +0 -10
  71. package/python/Makefile +0 -134
  72. package/python/Pipfile +0 -28
  73. package/python/Pipfile.lock +0 -645
  74. package/python/buildroot.mk +0 -30
  75. package/python/mypy.ini +0 -41
  76. package/python/opentrons_shared_data/__init__.py +0 -17
  77. package/python/opentrons_shared_data/_version.py +0 -20
  78. package/python/opentrons_shared_data/command/__init__.py +0 -52
  79. package/python/opentrons_shared_data/deck/__init__.py +0 -96
  80. package/python/opentrons_shared_data/deck/dev_types.py +0 -180
  81. package/python/opentrons_shared_data/errors/__init__.py +0 -30
  82. package/python/opentrons_shared_data/errors/categories.py +0 -60
  83. package/python/opentrons_shared_data/errors/codes.py +0 -108
  84. package/python/opentrons_shared_data/errors/exceptions.py +0 -915
  85. package/python/opentrons_shared_data/gripper/__init__.py +0 -63
  86. package/python/opentrons_shared_data/gripper/constants.py +0 -6
  87. package/python/opentrons_shared_data/gripper/gripper_definition.py +0 -101
  88. package/python/opentrons_shared_data/labware/__init__.py +0 -22
  89. package/python/opentrons_shared_data/labware/constants.py +0 -7
  90. package/python/opentrons_shared_data/labware/dev_types.py +0 -140
  91. package/python/opentrons_shared_data/labware/labware_definition.py +0 -328
  92. package/python/opentrons_shared_data/load.py +0 -57
  93. package/python/opentrons_shared_data/module/__init__.py +0 -74
  94. package/python/opentrons_shared_data/module/dev_types.py +0 -141
  95. package/python/opentrons_shared_data/pipette/__init__.py +0 -107
  96. package/python/opentrons_shared_data/pipette/dev_types.py +0 -177
  97. package/python/opentrons_shared_data/pipette/file_operation_helpers.py +0 -37
  98. package/python/opentrons_shared_data/pipette/load_data.py +0 -261
  99. package/python/opentrons_shared_data/pipette/model_constants.py +0 -123
  100. package/python/opentrons_shared_data/pipette/mutable_configurations.py +0 -431
  101. package/python/opentrons_shared_data/pipette/pipette_definition.py +0 -503
  102. package/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py +0 -240
  103. package/python/opentrons_shared_data/pipette/scripts/__init__.py +0 -0
  104. package/python/opentrons_shared_data/pipette/scripts/build_json_script.py +0 -469
  105. package/python/opentrons_shared_data/pipette/scripts/update_configuration_files.py +0 -450
  106. package/python/opentrons_shared_data/pipette/types.py +0 -193
  107. package/python/opentrons_shared_data/protocol/__init__.py +0 -13
  108. package/python/opentrons_shared_data/protocol/constants.py +0 -98
  109. package/python/opentrons_shared_data/protocol/dev_types.py +0 -436
  110. package/python/opentrons_shared_data/protocol/models/__init__.py +0 -48
  111. package/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py +0 -145
  112. package/python/opentrons_shared_data/protocol/models/protocol_schema_v7.py +0 -95
  113. package/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py +0 -110
  114. package/python/opentrons_shared_data/protocol/models/shared_models.py +0 -111
  115. package/python/opentrons_shared_data/py.typed +0 -0
  116. package/python/opentrons_shared_data/robot/__init__.py +0 -33
  117. package/python/opentrons_shared_data/robot/dev_types.py +0 -37
  118. package/python/pytest.ini +0 -2
  119. package/python/setup.cfg +0 -5
  120. package/python/setup.py +0 -179
  121. package/python/tests/__init__.py +0 -1
  122. package/python/tests/conftest.py +0 -0
  123. package/python/tests/deck/__init__.py +0 -0
  124. package/python/tests/deck/test_list_names.py +0 -12
  125. package/python/tests/deck/test_position.py +0 -113
  126. package/python/tests/deck/test_typechecks.py +0 -28
  127. package/python/tests/errors/__init__.py +0 -1
  128. package/python/tests/errors/test_error_completeness.py +0 -19
  129. package/python/tests/errors/test_exceptions.py +0 -74
  130. package/python/tests/gripper/__init__.py +0 -0
  131. package/python/tests/gripper/test_definition.py +0 -30
  132. package/python/tests/gripper/test_load.py +0 -16
  133. package/python/tests/labware/__init__.py +0 -13
  134. package/python/tests/labware/test_typechecks.py +0 -21
  135. package/python/tests/module/__init__.py +0 -20
  136. package/python/tests/module/test_load.py +0 -49
  137. package/python/tests/module/test_typechecks.py +0 -34
  138. package/python/tests/pipette/__init__.py +0 -0
  139. package/python/tests/pipette/test_load_data.py +0 -103
  140. package/python/tests/pipette/test_mutable_configurations.py +0 -266
  141. package/python/tests/pipette/test_pipette_definition.py +0 -183
  142. package/python/tests/pipette/test_pipette_load_name_conversions.py +0 -222
  143. package/python/tests/pipette/test_typechecks.py +0 -50
  144. package/python/tests/pipette/test_validate_schema.py +0 -37
  145. package/python/tests/protocol/__init__.py +0 -15
  146. package/python/tests/protocol/test_protocol_schema_v6.py +0 -107
  147. package/python/tests/protocol/test_protocol_schema_v7.py +0 -34
  148. package/python/tests/protocol/test_protocol_schema_v8.py +0 -20
  149. package/python/tests/protocol/test_typechecks.py +0 -36
  150. package/python/tests/robot/__init__.py +0 -0
  151. package/python/tests/robot/test_typechecks.py +0 -20
@@ -1,431 +0,0 @@
1
- import logging
2
- import json
3
- import re
4
- from pathlib import Path
5
- from typing import Optional, List, Dict, Any, cast
6
-
7
- from .pipette_definition import PipetteConfigurations, PipetteModelVersionType
8
- from .model_constants import (
9
- MUTABLE_CONFIGS_V1,
10
- VALID_QUIRKS,
11
- _MAP_KEY_TO_V2,
12
- _MIN_MAX_LOOKUP,
13
- _TYPE_LOOKUP,
14
- _UNITS_LOOKUP,
15
- )
16
- from .load_data import load_definition, load_serial_lookup_table
17
- from .types import (
18
- MutableConfig,
19
- Quirks,
20
- QuirkConfig,
21
- TypeOverrides,
22
- OverrideType,
23
- LiquidClasses,
24
- )
25
- from .pipette_load_name_conversions import (
26
- convert_pipette_model,
27
- convert_to_pipette_name_type,
28
- )
29
- from .file_operation_helpers import (
30
- MutableConfigurationEncoder,
31
- MutableConfigurationDecoder,
32
- )
33
- from .dev_types import PipetteModel, PipetteName
34
-
35
-
36
- log = logging.getLogger(__name__)
37
-
38
- PIPETTE_SERIAL_MODEL_LOOKUP = load_serial_lookup_table()
39
- SERIAL_STUB_REGEX = re.compile(r"P[0-9]{1,3}[KSMHV]{1,2}V[0-9]{2}")
40
- LIQUID_CLASS = LiquidClasses.default
41
-
42
-
43
- def _edit_non_quirk(
44
- mutable_config_key: str, new_mutable_value: MutableConfig, base_dict: Dict[str, Any]
45
- ) -> None:
46
- def _do_edit_non_quirk(
47
- new_value: MutableConfig, existing: Dict[Any, Any], keypath: List[Any]
48
- ) -> None:
49
- thiskey: Any = keypath[0]
50
- if thiskey in [lc.name for lc in LiquidClasses]:
51
- thiskey = LiquidClasses[thiskey]
52
- if len(keypath) > 1:
53
- restkeys = keypath[1:]
54
- if thiskey == "##EACHTIP##":
55
- for key in existing.keys():
56
- _do_edit_non_quirk(new_value, existing[key], restkeys)
57
- else:
58
- _do_edit_non_quirk(new_value, existing[thiskey], restkeys)
59
- else:
60
- # This was the last key
61
- if thiskey == "##EACHTIP##":
62
- for key in existing.keys():
63
- existing[key] = new_value.value
64
- else:
65
- existing[thiskey] = new_value.value
66
-
67
- new_names = _MAP_KEY_TO_V2[mutable_config_key]
68
- _do_edit_non_quirk(new_mutable_value, base_dict, new_names)
69
-
70
-
71
- def _migrate_to_v2_configurations(
72
- base_configurations: PipetteConfigurations,
73
- v1_mutable_configs: OverrideType,
74
- ) -> PipetteConfigurations:
75
- """Helper function to migration V1 Configs to the V2 format.
76
-
77
- Given an input of v1 mutable configs, look up the equivalent keyed
78
- value of that configuration."""
79
- quirks_list = []
80
- dict_of_base_model = base_configurations.dict(by_alias=True)
81
- for c, v in v1_mutable_configs.items():
82
- if isinstance(v, str):
83
- # ignore the saved model
84
- continue
85
- if isinstance(v, bool):
86
- # ignore any accidental top level quirks.
87
- continue
88
- if c == "quirks" and isinstance(v, dict):
89
- quirks_list.extend([b.name for b in v.values() if b.value])
90
- elif isinstance(v, MutableConfig):
91
- _edit_non_quirk(c, v, dict_of_base_model)
92
-
93
- dict_of_base_model["quirks"] = list(
94
- set(dict_of_base_model["quirks"]).union(set(quirks_list))
95
- )
96
-
97
- # re-serialization is not great for this nested enum so we need
98
- # to perform this workaround.
99
- dict_of_base_model["liquid_properties"][LIQUID_CLASS]["supportedTips"] = {
100
- k.name: v
101
- for k, v in dict_of_base_model["liquid_properties"][LIQUID_CLASS][
102
- "supportedTips"
103
- ].items()
104
- }
105
- dict_of_base_model["liquid_properties"] = {
106
- k.name: v for k, v in dict_of_base_model["liquid_properties"].items()
107
- }
108
- dict_of_base_model["plungerPositionsConfigurations"] = {
109
- k.name: v
110
- for k, v in dict_of_base_model["plungerPositionsConfigurations"].items()
111
- }
112
- return PipetteConfigurations.parse_obj(dict_of_base_model)
113
-
114
-
115
- def _load_available_overrides(
116
- pipette_serial_number: str, pipette_override_path: Path
117
- ) -> OverrideType:
118
- """Load the available overrides from disk."""
119
- pipette_override = pipette_override_path / f"{pipette_serial_number}.json"
120
- try:
121
- with open(pipette_override, "r") as f:
122
- return json.load(f, cls=MutableConfigurationDecoder)
123
- except json.JSONDecodeError as e:
124
- log.warning(f"pipette override for {pipette_serial_number} is corrupt: {e}")
125
- pipette_override.unlink()
126
- raise FileNotFoundError()
127
-
128
-
129
- def _list_all_mutable_configs(
130
- on_disk_overrides: OverrideType, base_configurations: Dict[str, Any]
131
- ) -> OverrideType:
132
- """Get all available mutable configurations"""
133
- all_mutable_configs = set([*MUTABLE_CONFIGS_V1, *VALID_QUIRKS]) - set(["quirks"])
134
- on_disk_configs = set(
135
- [
136
- v.name
137
- for v in on_disk_overrides.values()
138
- if isinstance(v, MutableConfig) or isinstance(v, QuirkConfig)
139
- ]
140
- )
141
-
142
- missing_configurations = all_mutable_configs.difference(on_disk_configs)
143
-
144
- default_configurations: OverrideType = {"quirks": {}}
145
- for c in missing_configurations:
146
- if c in VALID_QUIRKS:
147
- if Quirks(c) in base_configurations["quirks"]:
148
- default_configurations["quirks"][c] = QuirkConfig.validate_and_build( # type: ignore
149
- c, True
150
- )
151
- else:
152
- default_configurations[c] = _find_default(c, base_configurations)
153
- default_configurations.update(on_disk_overrides)
154
- return default_configurations
155
-
156
-
157
- def _get_default_value_for(config: Dict[str, Any], keypath: List[str]) -> Any:
158
- def _do_get_default_value_for(
159
- remaining_config: Dict[Any, Any], keypath: List[str]
160
- ) -> Any:
161
- first: Any = keypath[0]
162
- if first in [lc.name for lc in LiquidClasses]:
163
- first = LiquidClasses[first]
164
- if len(keypath) > 1:
165
- rest = keypath[1:]
166
- if first == "##EACHTIP##":
167
- tip_list = list(remaining_config.keys())
168
- tip_list.sort(key=lambda o: o.value)
169
- return _do_get_default_value_for(remaining_config[tip_list[-1]], rest)
170
- else:
171
- return _do_get_default_value_for(remaining_config[first], rest)
172
- else:
173
- if first == "###EACHTIP##":
174
- tip_list = list(remaining_config.keys())
175
- tip_list.sort(key=lambda o: o.value)
176
- return remaining_config[tip_list[-1]]
177
- elif first == "currentByTipCount":
178
- # return the value for the most tips at a time
179
- cbt = remaining_config[first]
180
- return cbt[next(reversed(sorted(cbt.keys())))]
181
- else:
182
- return remaining_config[first]
183
-
184
- return _do_get_default_value_for(config, keypath)
185
-
186
-
187
- def _find_default(name: str, configs: Dict[str, Any]) -> MutableConfig:
188
- """Find the default value from the configs and return it as a mutable config."""
189
- keypath = _MAP_KEY_TO_V2[name]
190
- nested_name = keypath[-1]
191
-
192
- if name == "pickUpCurrent":
193
- min_max_dict = _MIN_MAX_LOOKUP["current"]
194
- type_lookup = _TYPE_LOOKUP["current"]
195
- units_lookup = _UNITS_LOOKUP["current"]
196
- else:
197
- min_max_dict = _MIN_MAX_LOOKUP[nested_name]
198
- type_lookup = _TYPE_LOOKUP[nested_name]
199
- units_lookup = _UNITS_LOOKUP[nested_name]
200
- default_value = _get_default_value_for(configs, keypath)
201
- return MutableConfig(
202
- value=default_value,
203
- default=default_value,
204
- min=min_max_dict["min"],
205
- max=min_max_dict["max"],
206
- type=type_lookup,
207
- units=units_lookup,
208
- name=name,
209
- )
210
-
211
-
212
- def known_pipettes(pipette_override_path: Path) -> List[str]:
213
- """List pipette IDs for which we have known overrides"""
214
- return [
215
- fi.stem
216
- for fi in pipette_override_path.iterdir()
217
- if fi.is_file() and ".json" in fi.suffixes
218
- ]
219
-
220
-
221
- def _load_full_mutable_configs(
222
- pipette_model: PipetteModelVersionType, overrides: OverrideType
223
- ) -> OverrideType:
224
- base_configs = load_definition(
225
- pipette_model.pipette_type,
226
- pipette_model.pipette_channels,
227
- pipette_model.pipette_version,
228
- )
229
- base_configs_dict = base_configs.dict(by_alias=True)
230
- full_mutable_configs = _list_all_mutable_configs(overrides, base_configs_dict)
231
-
232
- if not full_mutable_configs.get("name"):
233
- full_mutable_configs["name"] = str(
234
- convert_to_pipette_name_type(cast(PipetteName, str(pipette_model)))
235
- )
236
-
237
- if not full_mutable_configs.get("model"):
238
- full_mutable_configs["model"] = str(pipette_model)
239
-
240
- return full_mutable_configs
241
-
242
-
243
- def list_mutable_configs(
244
- pipette_serial_number: str, pipette_override_path: Path
245
- ) -> OverrideType:
246
- """
247
- Returns dict of mutable configs only.
248
- """
249
-
250
- mutable_configs: OverrideType = {}
251
-
252
- try:
253
- mutable_configs = _load_available_overrides(
254
- pipette_serial_number, pipette_override_path
255
- )
256
- except FileNotFoundError:
257
- log.info(f"Pipette id {pipette_serial_number} not found")
258
- # This is mimicing behavior from the original file
259
- # which returned an empty dict if no on disk value was found.
260
- return mutable_configs
261
-
262
- serial_key_match = SERIAL_STUB_REGEX.match(pipette_serial_number)
263
-
264
- if serial_key_match:
265
- serial_key = serial_key_match.group(0)
266
- else:
267
- serial_key = ""
268
- pipette_model = convert_pipette_model(
269
- cast(PipetteModel, PIPETTE_SERIAL_MODEL_LOOKUP[serial_key])
270
- )
271
- return _load_full_mutable_configs(pipette_model, mutable_configs)
272
-
273
-
274
- def list_mutable_configs_with_defaults(
275
- pipette_model: PipetteModelVersionType,
276
- pipette_serial_number: Optional[str],
277
- pipette_override_path: Path,
278
- ) -> OverrideType:
279
- """
280
- Returns dict of mutable configs only, with their defaults.
281
- """
282
- mutable_configs: OverrideType = {}
283
- if pipette_serial_number:
284
- try:
285
- mutable_configs = _load_available_overrides(
286
- pipette_serial_number, pipette_override_path
287
- )
288
- except FileNotFoundError:
289
- pass
290
- return _load_full_mutable_configs(pipette_model, mutable_configs)
291
-
292
-
293
- def load_with_mutable_configurations(
294
- pipette_model: PipetteModelVersionType,
295
- pipette_override_path: Path,
296
- pipette_serial_number: Optional[str] = None,
297
- ) -> PipetteConfigurations:
298
- """
299
- Load pipette config data with any overrides available.
300
-
301
- This function reads from disk each time, so changes to the overrides
302
- will be picked up in subsequent calls.
303
-
304
- :param str pipette_model: The pipette model name (i.e. "p10_single_v1.3")
305
- for which to load configuration
306
- :param pipette_serial_number: An (optional) unique ID for the pipette to locate
307
- config overrides. If the ID is not specified, the system
308
- assumes this is a simulated pipette and does not
309
- save settings. If the ID is specified but no overrides
310
- corresponding to the ID are found, the system creates a
311
- new overrides file for it.
312
- :type pipette_serial_number: str or None
313
- :raises KeyError: if ``pipette_model`` is not in the top-level keys of
314
- the pipetteModelSpecs.json file (and therefore not in
315
- :py:attr:`configs`)
316
-
317
- :returns PipetteConfig: The configuration, loaded and checked
318
- """
319
- base_configurations = load_definition(
320
- pipette_model.pipette_type,
321
- pipette_model.pipette_channels,
322
- pipette_model.pipette_version,
323
- )
324
- # Load overrides if we have a pipette id
325
- if pipette_serial_number:
326
- try:
327
- override = _load_available_overrides(
328
- pipette_serial_number, pipette_override_path
329
- )
330
- except FileNotFoundError:
331
- pass
332
- else:
333
- base_configurations = _migrate_to_v2_configurations(
334
- base_configurations, override
335
- )
336
- # the ulPerMm functions are structured in pipetteModelSpecs.json as
337
- # a list sorted from oldest to newest. That means the latest functions
338
- # are always the last element and, as of right now, the older ones are
339
- # the first element (for models that only have one function, the first
340
- # and last elements are the same, which is fine). If we add more in the
341
- # future, we’ll have to change this code to select items more
342
- # intelligently
343
- # TODO (lc 06/27) Handle versioned ul per mm
344
-
345
- return base_configurations
346
-
347
-
348
- def _add_new_overrides_to_existing( # noqa: C901
349
- base_configs_dict: Dict[str, Any],
350
- existing_overrides: OverrideType,
351
- overrides: TypeOverrides,
352
- ) -> OverrideType:
353
- """Helper function to add new overrides to the existing ones"""
354
- # FIXME remove the validation here for the file save and rely
355
- # on the validation in the robot server. We unfortunately have
356
- # to keep the validation here until we decide to fully wipe/migrate
357
- # files saved on disk because some of them have unexpected
358
- # data entries.
359
- if not existing_overrides.get("quirks"):
360
- existing_overrides["quirks"] = {}
361
- for key, value in overrides.items():
362
- # If an existing override is saved as null from endpoint, remove from
363
- # overrides file
364
- if value is None and existing_overrides.get(key):
365
- del existing_overrides[key]
366
- elif isinstance(value, bool):
367
- if key in VALID_QUIRKS:
368
- existing_overrides["quirks"][key] = QuirkConfig.validate_and_build( # type: ignore
369
- key, value
370
- )
371
- elif key not in MUTABLE_CONFIGS_V1:
372
- # Unfortunately, some of the files got corrupted,
373
- # so we have to check that the key doesn't exist
374
- # in mutable configs before throwing an error.
375
- raise ValueError(
376
- f"{value} is invalid for {key} or {key} is not a supported quirk."
377
- )
378
- elif existing_overrides.get(key):
379
- del existing_overrides[key]
380
- elif value:
381
- if existing_overrides.get(key):
382
- existing_overrides[key].validate_and_add(value) # type: ignore
383
- else:
384
- new_mutable_config = _find_default(key, base_configs_dict)
385
- new_mutable_config.validate_and_add(value)
386
- existing_overrides[key] = new_mutable_config
387
- return existing_overrides
388
-
389
-
390
- def save_overrides(
391
- pipette_serial_number: str, overrides: TypeOverrides, pipette_override_path: Path
392
- ) -> None:
393
- """
394
- Save overrides for the pipette.
395
-
396
- :param pipette_id: The pipette id
397
- :param overrides: The incoming values
398
- :return: None
399
- """
400
- # need to load defaults
401
- serial_key_match = SERIAL_STUB_REGEX.match(pipette_serial_number)
402
- if serial_key_match:
403
- serial_key = serial_key_match.group(0)
404
- else:
405
- serial_key = ""
406
- pipette_model = convert_pipette_model(
407
- cast(PipetteModel, PIPETTE_SERIAL_MODEL_LOOKUP[serial_key])
408
- )
409
-
410
- base_configs = load_definition(
411
- pipette_model.pipette_type,
412
- pipette_model.pipette_channels,
413
- pipette_model.pipette_version,
414
- )
415
- base_configs_dict = base_configs.dict(by_alias=True)
416
- try:
417
- existing_overrides = _load_available_overrides(
418
- pipette_serial_number, pipette_override_path
419
- )
420
- except FileNotFoundError:
421
- existing_overrides = {"quirks": {}}
422
-
423
- updated_overrides = _add_new_overrides_to_existing(
424
- base_configs_dict, existing_overrides, overrides
425
- )
426
-
427
- if not updated_overrides.get("model"):
428
- updated_overrides["model"] = str(pipette_model)
429
-
430
- with open(pipette_override_path / f"{pipette_serial_number}.json", "w") as file:
431
- json.dump(updated_overrides, file, cls=MutableConfigurationEncoder)