Skip to content

Commit 231ac5e

Browse files
organize properties as required
1 parent a2e8061 commit 231ac5e

7 files changed

Lines changed: 6097 additions & 751 deletions

File tree

src/allotropy/allotrope/schema_mappers/adm/plate_reader/benchling/_2023/_09/plate_reader.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class ImageFeature:
8686
feature: str
8787
result: float | InvalidJsonFloat
8888
data_sources: list[DataSource] | None = None
89+
custom_info: dict[str, Any] | None = None
8990

9091

9192
@dataclass(frozen=True)
@@ -561,15 +562,18 @@ def _get_processed_data_aggregate_document(
561562
processed_data_identifier=data.identifier,
562563
image_feature_aggregate_document=ImageFeatureAggregateDocument(
563564
image_feature_document=[
564-
ImageFeatureDocumentItem(
565-
image_feature_identifier=image_feature.identifier,
566-
image_feature_name=image_feature.feature,
567-
image_feature_result=TQuantityValueUnitless(
568-
value=image_feature.result
569-
),
570-
data_source_aggregate_document=self._get_data_source_aggregate_document(
571-
image_feature.data_sources
565+
add_custom_information_document(
566+
ImageFeatureDocumentItem(
567+
image_feature_identifier=image_feature.identifier,
568+
image_feature_name=image_feature.feature,
569+
image_feature_result=TQuantityValueUnitless(
570+
value=image_feature.result
571+
),
572+
data_source_aggregate_document=self._get_data_source_aggregate_document(
573+
image_feature.data_sources
574+
),
572575
),
576+
image_feature.custom_info,
573577
)
574578
for image_feature in data.features
575579
]

src/allotropy/parsers/ctl_immunospot/ctl_immunospot_reader.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,6 @@ def _read_plate_data_7_0_38(self, reader: CsvReader) -> list[pd.DataFrame]:
202202
return plates
203203

204204
def _read_header(self, reader: CsvReader) -> SeriesData:
205-
lines = [line.strip() for line in reader.pop_until_empty(EMPTY_STR_OR_CSV_LINE)]
206-
207205
def fix_line(line: str) -> str:
208206
# Add missing key for file path line.
209207
if line.endswith(".txt") or line.endswith(".xls"):
@@ -213,13 +211,45 @@ def fix_line(line: str) -> str:
213211
line = f"Review Date: {date_str}"
214212
return line.strip()
215213

216-
lines = [fix_line(line) for raw_line in lines for line in raw_line.split(";")]
214+
def split_multi_key_line(line: str) -> list[str]:
215+
"""Split lines that contain multiple key-value pairs into separate lines."""
216+
line = line.strip().strip('"').strip() # Clean up quotes and whitespace
217+
218+
# Case 1: Parenthetical format: (Auto Areas: Estimated, Manual Areas: Normalized)
219+
if line.startswith("(") and line.endswith(")") and "," in line:
220+
return [pair.strip() for pair in line[1:-1].split(",")]
221+
222+
# Case 2: Tab-separated format: Assay: 1\t\t\t\t\t\tEdge Compensation Level: 1.0
223+
if "\t" in line and line.count(":") > 1:
224+
return [
225+
part.strip()
226+
for part in line.split("\t")
227+
if part.strip() and ":" in part
228+
]
229+
230+
return [line]
231+
232+
def process_lines(raw_lines: list[str]) -> list[str]:
233+
"""Process a list of raw lines and return split/fixed lines."""
234+
processed = []
235+
for raw_line in raw_lines:
236+
for line in raw_line.split(";"):
237+
fixed_line = fix_line(line)
238+
processed.extend(split_multi_key_line(fixed_line))
239+
return processed
240+
241+
# Process initial header lines
242+
lines = [line.strip() for line in reader.pop_until_empty(EMPTY_STR_OR_CSV_LINE)]
243+
all_lines = process_lines(lines)
244+
245+
# Process additional header lines if they exist
217246
reader.drop_empty(EMPTY_STR_OR_CSV_LINE)
218247
if ":" in (reader.get() or ""):
219-
lines.extend(list(reader.pop_until_empty(EMPTY_STR_OR_CSV_LINE)))
248+
additional_lines = list(reader.pop_until_empty(EMPTY_STR_OR_CSV_LINE))
249+
all_lines.extend(process_lines(additional_lines))
220250

221251
df = read_csv(
222-
StringIO("\n".join(lines)),
252+
StringIO("\n".join(all_lines)),
223253
sep=r"^([^:]+):\s+",
224254
header=None,
225255
engine="python",

src/allotropy/parsers/ctl_immunospot/ctl_immunospot_structure.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,14 @@ def _create_measurement(
3333
well_plate_identifier: str,
3434
plate_data: dict[str, pd.DataFrame],
3535
histograms: dict[str, tuple[list[float], list[float]]],
36+
header_data: dict[str, float | str | None],
3637
) -> Measurement:
3738
location_identifier = f"{well_row}{well_col}"
39+
image_custom_info = {
40+
"Min. SpotSize": header_data.pop("Min. SpotSize", None),
41+
"Max. SpotSize": header_data.pop("Max. SpotSize", None),
42+
"Spot Separation": header_data.pop("Spot Separation", None),
43+
}
3844
return Measurement(
3945
type_=MeasurementType.OPTICAL_IMAGING,
4046
device_type=constants.DEVICE_TYPE,
@@ -50,6 +56,7 @@ def _create_measurement(
5056
identifier=random_uuid_str(),
5157
feature=name,
5258
result=float(data[well_col][well_row]),
59+
custom_info=image_custom_info,
5360
)
5461
for name, data in plate_data.items()
5562
],
@@ -77,6 +84,7 @@ def _create_measurement(
7784
]
7885
if histograms and location_identifier in histograms
7986
else None,
87+
device_control_custom_info=header_data,
8088
)
8189

8290

@@ -94,16 +102,23 @@ def create_measurement_groups(
94102
round_to_nearest_well_count(first_plate.size),
95103
f"Unable to determine valid plate count from dataframe of size: {first_plate.size}",
96104
)
105+
measurement_time = header[str, ("Counted", "Review Date")]
106+
analyst = header[str, "Authenticated user"]
107+
custom_info = {
108+
"Assay": header.get(str, "Assay"),
109+
}
110+
header_data = header.get_unread()
97111
return [
98112
MeasurementGroup(
99113
plate_well_count=plate_well_count,
100-
measurement_time=header[str, ("Counted", "Review Date")],
101-
analyst=header[str, "Authenticated user"],
114+
measurement_time=measurement_time,
115+
analyst=analyst,
102116
measurements=[
103117
_create_measurement(
104-
row, col, well_plate_identifier, plate_data, histograms
118+
row, col, well_plate_identifier, plate_data, histograms, header_data
105119
)
106120
],
121+
custom_info=custom_info,
107122
)
108123
for row in first_plate.index
109124
for col in first_plate.columns
@@ -128,5 +143,4 @@ def create_metadata(header: SeriesData) -> Metadata:
128143
re.match(r"^ImmunoSpot ([\d\.]+)$", header[str, "Software version"]),
129144
msg="Unable to parse software version",
130145
).group(1),
131-
custom_info=header.get_unread(),
132146
)

0 commit comments

Comments
 (0)