Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SUPPORTED_INSTRUMENT_SOFTWARE.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The parsers follow maturation levels of: Recommended, Candidate Release, Working
|Revvity Kaleido|Recommended|BENCHLING/2023/09
|Tecan Magellan|Recommended|REC/2024/06
|Thermo Fisher Scientific SkanIt|Recommended|REC/2025/03
|Unchained Labs Lunatic|Recommended|REC/2024/06
|Unchained Labs Lunatic & Stunner|Recommended|REC/2025/03
.3+|Solution Analyzer|Beckman Coulter PharmSpec|Recommended|REC/2024/09
|NovaBio Flex2|Recommended|BENCHLING/2024/09
|Roche Cedex BioHT|Recommended|REC/2024/09
Expand Down
2 changes: 2 additions & 0 deletions src/allotropy/allotrope/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"~": "_TILDE_",
"?": "_QMARK_",
"^": "_CARET_",
"=": "_EQUALS_",
"@": "_AT_",
# NOTE: this MUST be at the end, or it will break other key replacements.
" ": "_",
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
MeasurementAggregateDocument,
MeasurementDocument,
Model,
PeakItem,
PeakList,
PlateReaderAggregateDocument,
PlateReaderDocumentItem,
ProcessedDataAggregateDocument,
Expand All @@ -27,6 +29,7 @@
from allotropy.allotrope.models.shared.components.plate_reader import SampleRoleType
from allotropy.allotrope.models.shared.definitions.custom import (
TQuantityValueDegreeCelsius,
TQuantityValueKiloDalton,
TQuantityValueMilliAbsorbanceUnit,
TQuantityValueMillimeter,
TQuantityValueMilliSecond,
Expand Down Expand Up @@ -120,6 +123,7 @@ class ErrorDocument:
class ProcessedDataDocument:
identifier: str | None = None
concentration_factor: float | None = None
peak_list_custom_info: list[dict[str, Any]] | None = None


@dataclass
Expand Down Expand Up @@ -164,6 +168,7 @@ class Measurement:
wavelength_identifier: str | None = None
analytical_method_identifier: str | None = None
experimental_data_identifier: str | None = None
integration_time: float | None = None

auto_focus_enabled_setting: bool | None = None
exposure_duration_setting: float | None = None
Expand Down Expand Up @@ -323,6 +328,9 @@ def _get_ultraviolet_absorbance_measurement_document(
TQuantityValueNanometer,
measurement.electronic_absorbance_reference_wavelength_setting,
),
integration_time=quantity_or_none(
TQuantityValueSecondTime, measurement.integration_time
),
device_type=measurement.device_type,
detection_type=measurement.detection_type,
detector_wavelength_setting=quantity_or_none(
Expand Down Expand Up @@ -386,12 +394,30 @@ def _get_ultraviolet_absorbance_measurement_document(
processed_data_document=[
ProcessedDataDocumentItem(
processed_data_identifier=measurement.processed_data_document.identifier,
data_processing_document={
"concentration factor": quantity_or_none(
TQuantityValueNanogramPerMicroliter,
measurement.processed_data_document.concentration_factor,
)
},
data_processing_document=(
{
"concentration factor": quantity_or_none(
TQuantityValueNanogramPerMicroliter,
measurement.processed_data_document.concentration_factor,
)
}
if measurement.processed_data_document.concentration_factor
is not None
else None
),
peak_list=PeakList(
peak=[
add_custom_information_document(
PeakItem(),
self._process_peak_custom_info(peak_custom_info),
)
for peak_custom_info in (
measurement.processed_data_document.peak_list_custom_info
)
]
)
if measurement.processed_data_document.peak_list_custom_info
else None,
)
]
)
Expand Down Expand Up @@ -834,3 +860,25 @@ def _get_error_aggregate_document(
if error_documents
else None
)

def _process_peak_custom_info(
self, peak_custom_info: dict[str, Any]
) -> dict[str, Any]:
"""Process peak custom info by applying proper units to measured values."""
processed_info: dict[str, Any] = {}

for key, value in peak_custom_info.items():
if key == "peak mean diameter":
processed_info[key] = quantity_or_none(TQuantityValueNanometer, value)
elif key == "peak mode diameter":
processed_info[key] = quantity_or_none(TQuantityValueNanometer, value)
elif key == "peak est. MW":
processed_info[key] = quantity_or_none(TQuantityValueKiloDalton, value)
elif key == "peak intensity":
processed_info[key] = quantity_or_none(TQuantityValuePercent, value)
elif key == "peak mass":
processed_info[key] = quantity_or_none(TQuantityValuePercent, value)
else:
processed_info[key] = value

return processed_info
14 changes: 13 additions & 1 deletion src/allotropy/calcdocs/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class CalculatedDataConfig:
source_configs: tuple[CalculatedDataConfig | MeasurementConfig, ...]
unit: str | None = None
description: str | None = None
# Optional key to pull a dynamic description from the element data
description_value_key: str | None = None
required: bool = False

def iter_data_sources(
Expand Down Expand Up @@ -71,13 +73,23 @@ def _get_calc_doc_inner(
if not data_sources:
return None

if self.name == "B22 goodness of fit":
pass
return CalculatedDocument(
uuid=random_uuid_str(),
name=self.name,
value=value,
data_sources=data_sources,
unit=self.unit,
description=self.description,
description=(
self.description
if self.description is not None
else (
elements[0].get_str_or_none(self.description_value_key)
if self.description_value_key
else None
)
),
)

def get_cache_key(self, keys: Keys) -> str:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from allotropy.allotrope.schema_mappers.adm.plate_reader.rec._2024._06.plate_reader import (
from allotropy.allotrope.schema_mappers.adm.plate_reader.rec._2025._03.plate_reader import (
Measurement,
)
from allotropy.calcdocs.extractor import Element, Extractor
Expand All @@ -19,6 +19,7 @@ def to_element(cls, measurement: Measurement) -> Element:
"uuid": measurement.identifier,
"wavelength id": measurement.wavelength_identifier,
"absorbance": measurement.absorbance,
"detection type": measurement.detection_type,
**custom_info,
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,15 @@ def sort_elements(self, elements: list[Element]) -> dict[str, list[Element]]:
if uuid := element.get_str("uuid"):
items[uuid].append(element)
return dict(items)


class DetectionTypeView(View):
def __init__(self, sub_view: View | None = None):
super().__init__(name="detection_type", sub_view=sub_view)

def sort_elements(self, elements: list[Element]) -> dict[str, list[Element]]:
items = defaultdict(list)
for element in elements:
if detection_type := element.get_str("detection type"):
items[detection_type].append(element)
return dict(items)
6 changes: 3 additions & 3 deletions src/allotropy/parser_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@
ThermoFisherVisionliteParser,
)
from allotropy.parsers.thermo_skanit.thermo_skanit_parser import ThermoSkanItParser
from allotropy.parsers.unchained_labs_lunatic.unchained_labs_lunatic_parser import (
UnchainedLabsLunaticParser,
from allotropy.parsers.unchained_labs_lunatic_stunner.unchained_labs_lunatic_stunner_parser import (
UnchainedLabsLunaticStunnerParser,
)
from allotropy.parsers.utils.timestamp_parser import TimestampParser
from allotropy.parsers.vendor_parser import VendorParser
Expand Down Expand Up @@ -273,7 +273,7 @@ def get_parser(
Vendor.THERMO_FISHER_QUBIT_FLEX: ThermoFisherQubitFlexParser,
Vendor.THERMO_FISHER_VISIONLITE: ThermoFisherVisionliteParser,
Vendor.THERMO_SKANIT: ThermoSkanItParser,
Vendor.UNCHAINED_LABS_LUNATIC: UnchainedLabsLunaticParser,
Vendor.UNCHAINED_LABS_LUNATIC: UnchainedLabsLunaticStunnerParser,
}


Expand Down
78 changes: 0 additions & 78 deletions src/allotropy/parsers/unchained_labs_lunatic/constants.py

This file was deleted.

This file was deleted.

Loading
Loading