Skip to content

Commit 572d2fb

Browse files
Merge branch 'main' into jherna/flowjo_migrate_get_unread
2 parents c9232f1 + 0736db2 commit 572d2fb

67 files changed

Lines changed: 263141 additions & 119104 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,47 @@ All notable changes to this packages will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.1.98] - 2025-07-08
9+
10+
### Added
11+
12+
- Bio-Rad Bio-Plex Manager - Add support for statistics dimensions (#1029)
13+
- Agilent Gen5 - Add Conc/Dil to Sample document custom fields (#1030)
14+
- Roche Cedex BioHT - Added support for mg/L (#1028)
15+
- Thermo Fisher Scientific SkanIt - Support multiplates (#1025)
16+
- Beckman Vi-Cell Blu - Added get_unread data to parser (#1023)
17+
- Molecular Devices SoftMax Pro - Enable Reduced-only file parsing (#1016)
18+
19+
## [0.1.97] - 2025-06-24
20+
21+
### Fixed
22+
23+
- Agilent OpenLab CDS - use bytes to read zip file instead of file name (#1021)
24+
25+
## [0.1.96] - 2025-06-23
26+
27+
### Fixed
28+
29+
- Molecular Devices SoftMax Pro - account for partial plate reads (#1013)
30+
31+
## [0.1.95] - 2025-06-19
32+
33+
### Added
34+
35+
- Luminex xPONENT - migrate to BENCHLING/2024/09 schema (#1007)
36+
37+
## [0.1.94] - 2025-06-18
38+
39+
### Added
40+
41+
- ChemoMetec Nucleoview - add support for tab separated files (#1015)
42+
- Molecular Devices SoftMax Pro - update ASM structure for spectrum data (#1012)
43+
- Molecular Devices SoftMax Pro - update adapter to plate reader schema REC 25/03 (#1011)
44+
45+
### Fixed
46+
47+
- Agilent Gen5 - fix spectrum data cube label (#1014)
48+
849
## [0.1.93] - 2025-06-10
950

1051
### Fixed

SUPPORTED_INSTRUMENT_SOFTWARE.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ The parsers follow maturation levels of: Recommended, Candidate Release, Working
3131
|Benchling Thermo Fisher Scientific Chromeleon|Recommended|BENCHLING/2023/09
3232
.2+|Liquid Handler|Beckman Coulter Biomek|Recommended|BENCHLING/2024/11
3333
|Beckman Echo Plate Reformat|Recommended|BENCHLING/2024/11
34-
.2+|Multi Analyte Profiling|Bio-Rad Bio-Plex Manager|Recommended|BENCHLING/2024/01
35-
|Luminex xPONENT|Recommended|BENCHLING/2024/01
34+
.2+|Multi Analyte Profiling|Bio-Rad Bio-Plex Manager|Recommended|BENCHLING/2024/09
35+
|Luminex xPONENT|Recommended|BENCHLING/2024/09
3636
.14+|Plate Reader|Agilent Gen5|Recommended|REC/2025/03
3737
|Agilent Gen5 Image|Recommended|BENCHLING/2023/09
3838
|BMG Labtech SMART Control|Recommended|REC/2024/06
@@ -45,7 +45,7 @@ The parsers follow maturation levels of: Recommended, Candidate Release, Working
4545
|PerkinElmer Envision|Recommended|REC/2024/06
4646
|Revvity Kaleido|Recommended|BENCHLING/2023/09
4747
|Tecan Magellan|Recommended|REC/2024/06
48-
|Thermo Fisher Scientific SkanIt|Recommended|BENCHLING/2023/09
48+
|Thermo Fisher Scientific SkanIt|Recommended|REC/2025/03
4949
|Unchained Labs Lunatic|Recommended|REC/2024/06
5050
.3+|Solution Analyzer|Beckman Coulter PharmSpec|Recommended|REC/2024/09
5151
|NovaBio Flex2|Recommended|BENCHLING/2024/09

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ authors = [
3434
{ name = "Stephen Worlow", email = "stephen.worlow@benchling.com" },
3535
{ name = "Brian Worth", email = "brian@benchling.com" },
3636
{ name = "Alex Reis", email = "alex@manifold.bio" },
37+
{ name = "Jane Dance", email = "jane.dance@abcellera.com" },
3738
]
3839
classifiers = [
3940
"License :: OSI Approved :: MIT License",

src/allotropy/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.1.93"
1+
__version__ = "0.1.98"

src/allotropy/allotrope/models/shared/definitions/definitions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class TStatisticDatumRole(Enum):
8484
frequency_of_grandparent_role = "frequency of grandparent role"
8585
frequency_of_total_role = "frequency of total role"
8686
sum_of_squares_role = "sum of squares role"
87+
mode_value_role = "mode value role"
88+
trimmed_mode_value_role = "trimmed mode value role"
89+
trimmed_count_role = "trimmed count role"
90+
trimmed_standard_deviation_role = "trimmed standard deviation role"
91+
trimmed_arithmetic_mean_role = "trimmed arithmetic mean role"
8792

8893

8994
class InvalidJsonFloat(Enum):

src/allotropy/allotrope/schema_mappers/adm/cell_counting/rec/_2024/_09/cell_counting.py

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ class Measurement:
107107
sample_custom_info: dict[str, Any] | None = None
108108
custom_info: dict[str, Any] | None = None
109109

110+
# Custom information documents
111+
device_control_custom_info: dict[str, Any] | None = None
112+
image_processing_custom_info: dict[str, Any] | None = None
113+
data_processing_custom_info: dict[str, Any] | None = None
114+
processed_data_custom_info: dict[str, Any] | None = None
115+
110116

111117
@dataclass(frozen=True)
112118
class MeasurementGroup:
@@ -223,21 +229,23 @@ def _get_technique_document(
223229
def _get_measurement_document(
224230
self, measurement: Measurement, metadata: Metadata
225231
) -> MeasurementDocument:
232+
device_control_doc = DeviceControlDocumentItem(
233+
device_type=metadata.device_type,
234+
detection_type=metadata.detection_type,
235+
sample_volume_setting=quantity_or_none(
236+
TQuantityValueMicroliter,
237+
measurement.sample_volume_setting,
238+
),
239+
)
240+
device_control_doc = add_custom_information_document(
241+
device_control_doc, measurement.device_control_custom_info or {}
242+
)
226243
measurement_document = MeasurementDocument(
227244
measurement_time=self.get_date_time(measurement.timestamp),
228245
measurement_identifier=measurement.measurement_identifier,
229246
sample_document=self._get_sample_document(measurement),
230247
device_control_aggregate_document=DeviceControlAggregateDocument(
231-
device_control_document=[
232-
DeviceControlDocumentItem(
233-
device_type=metadata.device_type,
234-
detection_type=metadata.detection_type,
235-
sample_volume_setting=quantity_or_none(
236-
TQuantityValueMicroliter,
237-
measurement.sample_volume_setting,
238-
),
239-
)
240-
]
248+
device_control_document=[device_control_doc]
241249
),
242250
processed_data_aggregate_document=self._get_processed_data_aggregate_document(
243251
measurement
@@ -247,12 +255,16 @@ def _get_measurement_document(
247255
),
248256
image_aggregate_document=ImageAggregateDocument(
249257
image_document=[
250-
ImageDocumentItem(
251-
experimental_data_identifier=measurement.experimental_data_identifier
258+
add_custom_information_document(
259+
ImageDocumentItem(
260+
experimental_data_identifier=measurement.experimental_data_identifier
261+
),
262+
measurement.image_processing_custom_info or {},
252263
)
253264
],
254265
)
255266
if measurement.experimental_data_identifier
267+
or measurement.image_processing_custom_info
256268
else None,
257269
)
258270
return add_custom_information_document(
@@ -322,20 +334,23 @@ def _get_processed_data_aggregate_document(
322334
TQuantityValueUnitless, measurement.debris_index
323335
),
324336
}
325-
data_processing_document = DataProcessingDocument(
326-
cell_type_processing_method=measurement.cell_type_processing_method,
327-
minimum_cell_diameter_setting=quantity_or_none(
328-
TQuantityValueMicrometer,
329-
measurement.minimum_cell_diameter_setting,
330-
),
331-
maximum_cell_diameter_setting=quantity_or_none(
332-
TQuantityValueMicrometer,
333-
measurement.maximum_cell_diameter_setting,
334-
),
335-
cell_density_dilution_factor=quantity_or_none(
336-
TQuantityValueUnitless,
337-
measurement.cell_density_dilution_factor,
337+
data_processing_document = add_custom_information_document(
338+
DataProcessingDocument(
339+
cell_type_processing_method=measurement.cell_type_processing_method,
340+
minimum_cell_diameter_setting=quantity_or_none(
341+
TQuantityValueMicrometer,
342+
measurement.minimum_cell_diameter_setting,
343+
),
344+
maximum_cell_diameter_setting=quantity_or_none(
345+
TQuantityValueMicrometer,
346+
measurement.maximum_cell_diameter_setting,
347+
),
348+
cell_density_dilution_factor=quantity_or_none(
349+
TQuantityValueUnitless,
350+
measurement.cell_density_dilution_factor,
351+
),
338352
),
353+
measurement.data_processing_custom_info or {},
339354
)
340355
processed_data_document = ProcessedDataDocumentItem(
341356
processed_data_identifier=measurement.processed_data_identifier,
@@ -389,7 +404,8 @@ def _get_processed_data_aggregate_document(
389404
return ProcessedDataAggregateDocument(
390405
processed_data_document=[
391406
add_custom_information_document(
392-
processed_data_document, custom_document
407+
processed_data_document,
408+
custom_document | (measurement.processed_data_custom_info or {}),
393409
)
394410
]
395411
)

src/allotropy/allotrope/schema_mappers/adm/multi_analyte_profiling/benchling/_2024/_09/multi_analyte_profiling.py

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
from allotropy.allotrope.models.adm.multi_analyte_profiling.benchling._2024._09.multi_analyte_profiling import (
44
AnalyteAggregateDocument,
55
AnalyteDocumentItem,
6+
CalculatedDataAggregateDocument,
7+
CalculatedDataDocumentItem,
68
CalibrationAggregateDocument,
79
CalibrationDocumentItem,
10+
DataSourceAggregateDocument,
11+
DataSourceDocumentItem,
812
DataSystemDocument,
913
DeviceControlAggregateDocument,
1014
DeviceControlDocumentItem,
@@ -17,6 +21,11 @@
1721
MultiAnalyteProfilingAggregateDocument,
1822
MultiAnalyteProfilingDocumentItem,
1923
SampleDocument,
24+
StatisticDimensionAggregateDocument,
25+
StatisticDimensionDocumentItem,
26+
StatisticsAggregateDocument,
27+
StatisticsDocumentItem,
28+
TQuantityValueModel,
2029
)
2130
from allotropy.allotrope.models.shared.components.plate_reader import SampleRoleType
2231
from allotropy.allotrope.models.shared.definitions.custom import (
@@ -28,19 +37,37 @@
2837
from allotropy.allotrope.models.shared.definitions.definitions import (
2938
TStatisticDatumRole,
3039
)
40+
from allotropy.allotrope.models.shared.definitions.units import Unitless
3141
from allotropy.allotrope.schema_mappers.schema_mapper import SchemaMapper
3242
from allotropy.constants import ASM_CONVERTER_VERSION
43+
from allotropy.parsers.utils.calculated_data_documents.definition import (
44+
CalculatedDocument,
45+
)
3346
from allotropy.parsers.utils.values import quantity_or_none
3447

3548

49+
@dataclass(frozen=True)
50+
class StatisticDimension:
51+
value: float
52+
unit: str
53+
statistic_datum_role: TStatisticDatumRole
54+
55+
56+
@dataclass(frozen=True)
57+
class StatisticsDocument:
58+
statistical_feature: str
59+
statistic_dimensions: list[StatisticDimension]
60+
61+
3662
@dataclass(frozen=True)
3763
class Analyte:
3864
identifier: str
3965
name: str
40-
value: float
4166
assay_bead_identifier: str
4267
assay_bead_count: float
43-
statistic_datum_role: TStatisticDatumRole | None
68+
statistics: list[StatisticsDocument] | None = None
69+
statistic_datum_role: TStatisticDatumRole | None = None
70+
fluorescence: float | None = None
4471

4572

4673
@dataclass(frozen=True)
@@ -115,6 +142,7 @@ class Metadata:
115142
class Data:
116143
metadata: Metadata
117144
measurement_groups: list[MeasurementGroup]
145+
calculated_data: list[CalculatedDocument] | None = None
118146

119147

120148
class Mapper(SchemaMapper[Data, Model]):
@@ -146,6 +174,9 @@ def map_model(self, data: Data) -> Model:
146174
self._get_technique_document(measurement_group, data.metadata)
147175
for measurement_group in data.measurement_groups
148176
],
177+
calculated_data_aggregate_document=self._get_calculated_data_aggregate_document(
178+
data.calculated_data
179+
),
149180
),
150181
field_asm_manifest=self.MANIFEST,
151182
)
@@ -208,9 +239,33 @@ def _get_measurement_document(
208239
),
209240
fluorescence=quantity_or_none(
210241
TQuantityValueRelativeFluorescenceUnit,
211-
analyte.value,
242+
analyte.fluorescence,
212243
has_statistic_datum_role=analyte.statistic_datum_role,
213244
),
245+
statistics_aggregate_document=(
246+
StatisticsAggregateDocument(
247+
statistics_document=[
248+
StatisticsDocumentItem(
249+
statistical_feature=statistic.statistical_feature,
250+
statistic_dimension_aggregate_document=StatisticDimensionAggregateDocument(
251+
statistic_dimension_document=[
252+
StatisticDimensionDocumentItem(
253+
statistical_value=TQuantityValueModel(
254+
value=dimension.value,
255+
unit=dimension.unit,
256+
has_statistic_datum_role=dimension.statistic_datum_role,
257+
),
258+
)
259+
for dimension in statistic.statistic_dimensions
260+
]
261+
),
262+
)
263+
for statistic in analyte.statistics
264+
]
265+
)
266+
if analyte.statistics
267+
else None
268+
),
214269
)
215270
for analyte in measurement.analytes
216271
]
@@ -261,3 +316,33 @@ def _get_error_aggregate_document(
261316
for error in errors
262317
]
263318
)
319+
320+
def _get_calculated_data_aggregate_document(
321+
self, calculated_data_items: list[CalculatedDocument] | None
322+
) -> CalculatedDataAggregateDocument | None:
323+
if not calculated_data_items:
324+
return None
325+
326+
return CalculatedDataAggregateDocument(
327+
calculated_data_document=[
328+
CalculatedDataDocumentItem(
329+
calculated_data_identifier=calculated_data_item.uuid,
330+
calculated_data_name=calculated_data_item.name,
331+
calculation_description=calculated_data_item.description,
332+
calculated_result=TQuantityValueModel(
333+
value=calculated_data_item.value,
334+
unit=calculated_data_item.unit or Unitless.unit,
335+
),
336+
data_source_aggregate_document=DataSourceAggregateDocument(
337+
data_source_document=[
338+
DataSourceDocumentItem(
339+
data_source_identifier=item.reference.uuid,
340+
data_source_feature=item.feature,
341+
)
342+
for item in calculated_data_item.data_sources
343+
]
344+
),
345+
)
346+
for calculated_data_item in calculated_data_items
347+
]
348+
)

src/allotropy/allotrope/schemas/adm/multi-analyte-profiling/BENCHLING/2024/09/CHANGE_NOTES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ Changes:
1515
* Reasoning: These should be included in REC/2024/12 core schema
1616
* Proposal: Add "method version" to measurement aggregate document
1717

18-
* Add "coefficient of variation role", "trimmed mode role", "trimmed count role", "trimmed standard deviation role", "trimmed arithmetic mean role" to tStatisiticDatumRole class
18+
* Add "coefficient of variation role", "trimmed mode value role", "trimmed count role", "trimmed standard deviation role", "trimmed arithmetic mean role" to tStatisiticDatumRole class
1919
* Reasoning: Support of additional statistical terms that are exported from multi-analyte-profilers
20-
* Proposal: Add "coefficient of variation role", "trimmed mode role", "trimmed count role", "trimmed standard deviation role", "trimmed arithmetic mean role" to tStatisiticDatumRole
20+
* Proposal: Add "coefficient of variation role", "trimmed mode value role", "trimmed count role", "trimmed standard deviation role", "trimmed arithmetic mean role" to tStatisiticDatumRole

src/allotropy/allotrope/schemas/adm/multi-analyte-profiling/BENCHLING/2024/09/multi-analyte-profiling.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2081,7 +2081,7 @@
20812081
"variance role",
20822082
"mode value role",
20832083
"maximum value role",
2084-
"trimmed mode role",
2084+
"trimmed mode value role",
20852085
"trimmed count role",
20862086
"trimmed standard deviation role",
20872087
"trimmed arithmetic mean role",

0 commit comments

Comments
 (0)