Skip to content

Commit b8a4057

Browse files
feat: Migrate revvity_kaleido to use SeriesData.get_unread (#1060)
1 parent 3911b79 commit b8a4057

11 files changed

Lines changed: 36312 additions & 3110 deletions

src/allotropy/parsers/revvity_kaleido/kaleido_structure.py

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import logging
77
from pathlib import Path
88
import re
9+
from typing import Any
910

1011
import pandas as pd
1112

@@ -235,6 +236,7 @@ class MeasurementInfo:
235236
measurement_time: str
236237
protocol_signature: str
237238
measurement_signature: str
239+
custom_info: dict[str, Any]
238240

239241
@staticmethod
240242
def create(
@@ -253,14 +255,15 @@ def create(
253255
reader.lines_as_df(lines, index_col=0, names=range(max_num_cols)),
254256
msg=f"Unable to parser data for {section_name} section.",
255257
).T.dropna(how="all")
256-
df.columns = df.columns.astype(str).str.strip(":")
258+
df.columns = df.columns.astype(str).str.lower().str.strip(":")
257259
data = df_to_series_data(df, index=0)
258260

259261
return MeasurementInfo(
260-
instrument_serial_number=data[str, "Instrument Serial Number"],
261-
measurement_time=data[str, "Measurement Started"],
262-
protocol_signature=data[str, "Protocol Signature"],
263-
measurement_signature=data[str, "Measurement Signature"],
262+
instrument_serial_number=data[str, "instrument serial number"],
263+
measurement_time=data[str, "measurement started"],
264+
protocol_signature=data[str, "protocol signature"],
265+
measurement_signature=data[str, "measurement signature"],
266+
custom_info=data.get_unread(skip={"software version", "barcode", "nan"}),
264267
)
265268

266269

@@ -350,15 +353,21 @@ def create(
350353
class Measurements:
351354
channels: list[Channel]
352355
number_of_averages: float | None
356+
detector_distance_luminiscence: float | None
353357
detector_distance: float | None
354358
scan_position: ScanPositionSettingPlateReader | None
355359
emission_wavelength: float | None
356360
excitation_wavelength: float | None
357361
focus_height: float | None
362+
device_control_custom_info: dict[str, Any]
363+
custom_info: dict[str, Any] | None = None
358364

359365
@staticmethod
360366
def create(
361-
reader: CsvReader, section_title: str, next_section_title: str
367+
reader: CsvReader,
368+
section_title: str,
369+
next_section_title: str,
370+
measurement_info: MeasurementInfo,
362371
) -> Measurements:
363372
assert_not_none(
364373
reader.drop_until_inclusive(f"^{section_title}"),
@@ -382,7 +391,8 @@ def create(
382391
return Measurements(
383392
channels=Measurements.create_channels(data),
384393
number_of_averages=data.get(float, "number of flashes"),
385-
detector_distance=data.get(
394+
detector_distance=data.get(float, "measurement height [mm]"),
395+
detector_distance_luminiscence=data.get(
386396
float, "distance between plate and detector [mm]"
387397
),
388398
scan_position=(
@@ -400,21 +410,47 @@ def create(
400410
else try_float_or_none(excitation_wavelength.removesuffix("nm"))
401411
),
402412
focus_height=data.get(float, "focus height [µm]"),
413+
device_control_custom_info={
414+
"number of flashes integrated": data.get(
415+
float, "number of flashes integrated"
416+
),
417+
"flash power": data.get(float, "flash power"),
418+
"measurement mode setting": data.get(str, "measurement mode"),
419+
"start plate repeat each [s]": measurement_info.custom_info.get(
420+
"start plate repeat each [s]", None
421+
),
422+
"number of plate repeats": measurement_info.custom_info.get(
423+
"number of plate repeats", None
424+
),
425+
"sequence executed by": measurement_info.custom_info.get(
426+
"sequence executed by", None
427+
),
428+
"filter set": data.get(str, "filter set"),
429+
"generate bright field": data.get(bool, "generate bright field"),
430+
"digital phase contrast": data.get(bool, "digital phase contrast"),
431+
"generate dpc": data.get(bool, "generate dpc"),
432+
},
433+
custom_info=data.get_unread(
434+
skip={
435+
"tech",
436+
"operation",
437+
"nan",
438+
}
439+
),
403440
)
404441

405442
@staticmethod
406443
def create_channels(data: SeriesData) -> list[Channel]:
407444
# Get all channel keys
408-
values = {
409-
key: data.series.get(key)
410-
for key in [
411-
"channel",
412-
"excitation wavelength [nm]",
413-
"excitation power [%]",
414-
"exposure time [ms]",
415-
"additional focus offset [mm]",
416-
]
417-
}
445+
channel_keys = [
446+
"channel",
447+
"excitation wavelength [nm]",
448+
"excitation power [%]",
449+
"exposure time [ms]",
450+
"additional focus offset [mm]",
451+
]
452+
values = {key: data.series.get(key) for key in channel_keys}
453+
data.mark_read(set(channel_keys))
418454
if values["channel"] is None:
419455
return []
420456

@@ -479,6 +515,7 @@ def create_metadata(data: Data, file_path: str) -> Metadata:
479515
model_number=constants.MODEL_NUMBER,
480516
product_manufacturer=constants.PRODUCT_MANUFACTURER,
481517
equipment_serial_number=data.measurement_info.instrument_serial_number,
518+
custom_info=data.measurement_info.custom_info,
482519
)
483520

484521

@@ -513,7 +550,9 @@ def _create_measurement(data: Data, well_position: str, well_value: str) -> Meas
513550
well_plate_identifier=data.results.barcode,
514551
sample_role_type=data.platemap.get_sample_role_type(well_position),
515552
number_of_averages=data.measurements.number_of_averages,
516-
detector_distance_setting=data.measurements.detector_distance,
553+
detector_distance_setting=data.measurements.detector_distance_luminiscence
554+
if experiment_type is ExperimentType.LUMINESCENCE
555+
else data.measurements.detector_distance,
517556
scan_position_setting=data.measurements.scan_position,
518557
detector_wavelength_setting=detector_wavelength_setting,
519558
excitation_wavelength_setting=data.measurements.excitation_wavelength,
@@ -526,12 +565,23 @@ def _create_measurement(data: Data, well_position: str, well_value: str) -> Meas
526565
luminescence=measurement_value
527566
if experiment_type is ExperimentType.LUMINESCENCE
528567
else None,
568+
device_control_custom_info=data.measurements.device_control_custom_info,
569+
custom_info=data.measurements.custom_info,
529570
)
530571

531572

532573
def _create_optical_measurement(
533574
data: Data, well_position: str, channel: Channel
534575
) -> Measurement:
576+
device_control_custom_info = {
577+
**data.measurements.device_control_custom_info,
578+
"additional focus offset [mm]": channel.additional_focus_offset,
579+
}
580+
if channel.name != "BRIGHTFIELD":
581+
to_remove = ["generate bright field", "digital phase contrast", "generate dpc"]
582+
for key in to_remove:
583+
device_control_custom_info.pop(key, None)
584+
535585
return Measurement(
536586
type_=data.background_info.experiment_type.measurement_type,
537587
identifier=random_uuid_str(),
@@ -550,6 +600,8 @@ def _create_optical_measurement(
550600
illumination_setting_unit="%",
551601
transmitted_light_setting=channel.transmitted_light,
552602
fluorescent_tag_setting=channel.fluorescent_tag,
603+
device_control_custom_info=device_control_custom_info,
604+
custom_info=data.measurements.custom_info,
553605
)
554606

555607

@@ -566,6 +618,16 @@ def _create_measurements(
566618

567619

568620
def create_measurement_groups(data: Data) -> list[MeasurementGroup]:
621+
protocol_owner = data.measurement_info.custom_info.pop("protocol owner", None)
622+
measurement_finished = data.measurement_info.custom_info.pop(
623+
"measurement finished", None
624+
)
625+
protocol_name = data.measurement_info.custom_info.pop("protocol name", None)
626+
627+
data.measurement_info.custom_info.pop("sequence executed by", None)
628+
data.measurement_info.custom_info.pop("number of plate repeats", None)
629+
data.measurement_info.custom_info.pop("start plate repeat each [s]", None)
630+
569631
return [
570632
MeasurementGroup(
571633
measurement_time=data.measurement_info.measurement_time,
@@ -596,6 +658,11 @@ def create_measurement_groups(data: Data) -> list[MeasurementGroup]:
596658
]
597659
if data.background_info.experiment_type is ExperimentType.OPTICAL_IMAGING
598660
else None,
661+
custom_info={
662+
"protocol owner": protocol_owner,
663+
"measurement finished": measurement_finished,
664+
"protocol name": protocol_name,
665+
},
599666
)
600667
for well_position, well_value in data.iter_wells()
601668
]

src/allotropy/parsers/revvity_kaleido/kaleido_structure_v2.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,20 @@ def read_barcode(cls, reader: CsvReader) -> str:
2727

2828

2929
def create_data_v2(version: str, reader: CsvReader) -> Data:
30+
background_info = BackgroundInfo.create(reader)
31+
results = ResultsV2.create(reader)
32+
analysis_result = AnalysisResult.create_results(
33+
reader, "Measurement Basic Information"
34+
)
35+
measurement_info = MeasurementInfo.create(
36+
reader, "Measurement Basic Information", "Plate Type"
37+
)
3038
return Data(
3139
version,
32-
BackgroundInfo.create(reader),
33-
ResultsV2.create(reader),
34-
AnalysisResult.create_results(reader, "Measurement Basic Information"),
35-
MeasurementInfo.create(reader, "Measurement Basic Information", "Plate Type"),
40+
background_info,
41+
results,
42+
analysis_result,
43+
measurement_info,
3644
Platemap.create(reader),
37-
Measurements.create(reader, "Measurements", "Analysis"),
45+
Measurements.create(reader, "Measurements", "Analysis", measurement_info),
3846
)

src/allotropy/parsers/revvity_kaleido/kaleido_structure_v3.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,23 @@ def read_barcode(cls, reader: CsvReader) -> str:
3030

3131

3232
def create_data_v3(version: str, reader: CsvReader) -> Data:
33+
background_info = BackgroundInfo.create(reader)
34+
results = ResultsV3.create(reader)
35+
analysis_result = AnalysisResult.create_results(reader, "Measurement Information")
36+
measurement_info = MeasurementInfo.create(
37+
reader, "Measurement Information", "Plate Type Information"
38+
)
3339
return Data(
3440
version,
35-
BackgroundInfo.create(reader),
36-
ResultsV3.create(reader),
37-
AnalysisResult.create_results(reader, "Measurement Information"),
38-
MeasurementInfo.create(
39-
reader, "Measurement Information", "Plate Type Information"
40-
),
41+
background_info,
42+
results,
43+
analysis_result,
44+
measurement_info,
4145
Platemap.create(reader),
4246
Measurements.create(
43-
reader, "Details of Measurement Sequence", "Post Processing Sequence"
47+
reader,
48+
"Details of Measurement Sequence",
49+
"Post Processing Sequence",
50+
measurement_info,
4451
),
4552
)

0 commit comments

Comments
 (0)