Skip to content

Commit ed6a1ff

Browse files
fix: Support bare UV curve name in Cytiva Unicorn parser (#1200)
## Summary - Fixes parsing failure for UNICORN 7.7 exports where the UV detector curve is named just "UV" instead of "UV 1_280" - Makes absorbance measurements optional (returns `None` gracefully) instead of raising on missing curves — consistent with all other measurement types - Adds anonymized test file exercising the single-UV-detector format ## Context Files from UNICORN 7.7.0.4016 system failed with: > "Expected non-null value for Unable to find curve data for absorbance measurement." The file is a properly exported "Entire Result(s)" (has Result.xml, Chrom.1.Xml, SystemData, etc.) — it simply has a single UV detector whose curve is named "UV" without a wavelength suffix. The 30-file count (vs 40+) is because this system has fewer detector modules, not because it's a partial export. ## Test plan - [x] Existing `unicorn_1.zip` test passes unchanged (UV 1_280 format) - [x] New `unicorn_single_uv.zip` test covers bare "UV" curve name - [x] Vendor discovery tests pass - [x] Linting + mypy pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7f4c250 commit ed6a1ff

3 files changed

Lines changed: 1055 additions & 18 deletions

File tree

src/allotropy/parsers/cytiva_unicorn/structure/measurements/absorbance.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
StrictXmlElement,
3838
)
3939
from allotropy.parsers.utils.uuids import random_uuid_str
40-
from allotropy.parsers.utils.values import assert_not_none, quantity_or_none
40+
from allotropy.parsers.utils.values import quantity_or_none
4141

4242

4343
class AbsorbanceMeasurement(UnicornMeasurement):
@@ -56,25 +56,24 @@ def create_or_none(
5656
handler: UnicornZipHandler,
5757
elements: list[StrictXmlElement],
5858
static_docs: StaticDocs,
59-
) -> UnicornMeasurement:
60-
element = assert_not_none(
61-
cls.filter_curve_or_none(elements, cls.get_curve_regex()),
62-
"Unable to find curve data for absorbance measurement.",
59+
) -> UnicornMeasurement | None:
60+
element = cls.filter_curve_or_none(elements, cls.get_curve_regex())
61+
if element is None:
62+
return None
63+
data_cube = cls.get_data_cube_or_none(
64+
handler,
65+
element,
66+
DataCubeComponent(
67+
type_=FieldComponentDatatype.float,
68+
concept="absorbance",
69+
unit="mAU",
70+
),
6371
)
72+
if data_cube is None:
73+
return None
6474
measurement = cls.get_measurement(
6575
static_docs=static_docs,
66-
chromatogram_data_cube=assert_not_none(
67-
cls.get_data_cube_or_none(
68-
handler,
69-
element,
70-
DataCubeComponent(
71-
type_=FieldComponentDatatype.float,
72-
concept="absorbance",
73-
unit="mAU",
74-
),
75-
),
76-
msg="Unable to find information to create absorbance data cubes.",
77-
),
76+
chromatogram_data_cube=data_cube,
7877
device_control_docs=[
7978
DeviceControlDoc(
8079
device_type=DEVICE_TYPE,
@@ -93,7 +92,7 @@ def create_or_none(
9392
class AbsorbanceMeasurement1(AbsorbanceMeasurement):
9493
@classmethod
9594
def get_curve_regex(cls) -> str:
96-
return r"^UV 1_\d+$"
95+
return r"^UV( 1_\d+)?$"
9796

9897
@classmethod
9998
def get_peaks_custom_info(

0 commit comments

Comments
 (0)