Skip to content

Commit 643b57a

Browse files
fix: Recognize Evaluation-Kinetics sheet and Single Cycle Kinetics analyte
The parser failed to find kinetics data (ka, kd, KD, Rmax) when the sheet was named "Evaluation-Kinetics" (no spaces around dash) instead of "Evaluation - Kinetics". Also, for Single Cycle Kinetics experiments the analyte identity is in "Single cycle kinetics 1 Solution" rather than "Analyte 1 Solution", causing key mismatches in the kinetics lookup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e3a432a commit 643b57a

1 file changed

Lines changed: 24 additions & 12 deletions

File tree

src/allotropy/parsers/cytiva_biacore_insight/cytiva_biacore_insight_structure.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ def _get_regeneration_custom_info(
207207
return regen_info
208208

209209

210+
EVALUATION_KINETICS_SHEET_NAMES = ("Evaluation - Kinetics", "Evaluation-Kinetics")
211+
212+
210213
def _get_table_from_dataframe(df: pd.DataFrame, split_on: str) -> pd.DataFrame:
211214
data_table = drop_df_rows_while(df, lambda row: row[0] != split_on)
212215
return parse_header_row(
@@ -518,17 +521,20 @@ class EvaluationKinetics:
518521

519522
def __init__(self, kinetics_table: pd.DataFrame) -> None:
520523
def _get_key(row: pd.Series[Any]) -> str:
521-
# Handle both "Channel" and "Flow cell" columns
522524
channel_or_flowcell = row.get("Channel", row.get("Flow cell", ""))
523-
# For affinity data, we might have multiple capture solutions
524-
# Build key with all available capture solutions
525525
capture_solution = row.get("Capture 1 Solution", "")
526-
# Handle None, NaN, or pd.NA values for capture_solution
527526
if capture_solution is None or (
528527
isinstance(capture_solution, float) and np.isnan(capture_solution)
529528
):
530529
capture_solution = ""
531-
analyte_solution = row.get("Analyte 1 Solution", "")
530+
analyte_solution = row.get(
531+
"Single cycle kinetics 1 Solution",
532+
row.get("Analyte 1 Solution", ""),
533+
)
534+
if analyte_solution is None or (
535+
isinstance(analyte_solution, float) and np.isnan(analyte_solution)
536+
):
537+
analyte_solution = ""
532538
return f"{channel_or_flowcell} {capture_solution} {analyte_solution}"
533539

534540
self._data = {}
@@ -831,7 +837,9 @@ def create(
831837
capture_solution = _first_not_null_or_none(
832838
channel_data["Capture 1 Solution"]
833839
)
834-
analyte_solution = first_row_data.get(str, "Analyte 1 Solution")
840+
analyte_solution = first_row_data.get(
841+
str, "Single cycle kinetics 1 Solution"
842+
) or first_row_data.get(str, "Analyte 1 Solution")
835843

836844
# Get calculated concentration from Evaluation sheets if available
837845
# Use the first flow cell from channel_data to look up the concentration
@@ -896,9 +904,12 @@ def create(reader: CytivaBiacoreInsightReader) -> Data:
896904

897905
# OPTIMIZATION: Parse evaluation/affinity data once, not per cycle
898906
evaluation_kinetics = None
899-
if "Evaluation - Kinetics" in reader.data:
907+
kinetics_sheet = next(
908+
(s for s in EVALUATION_KINETICS_SHEET_NAMES if s in reader.data), None
909+
)
910+
if kinetics_sheet is not None:
900911
evaluation_kinetics_table = _get_table_from_dataframe(
901-
reader.data["Evaluation - Kinetics"], split_on="Group"
912+
reader.data[kinetics_sheet], split_on="Group"
902913
)
903914
evaluation_kinetics = EvaluationKinetics(evaluation_kinetics_table)
904915
else:
@@ -986,11 +997,13 @@ def create_measurements_for_cycle(
986997
cycle_data = report_point_table[report_point_table["Cycle"] == cycle_number]
987998

988999
# Handle optional kinetics/affinity data
989-
# Try "Evaluation - Kinetics" first, then look for "Affinity" sheets
9901000
evaluation_kinetics = None
991-
if "Evaluation - Kinetics" in reader.data:
1001+
kinetics_sheet = next(
1002+
(s for s in EVALUATION_KINETICS_SHEET_NAMES if s in reader.data), None
1003+
)
1004+
if kinetics_sheet is not None:
9921005
evaluation_kinetics_table = _get_table_from_dataframe(
993-
reader.data["Evaluation - Kinetics"], split_on="Group"
1006+
reader.data[kinetics_sheet], split_on="Group"
9941007
)
9951008
evaluation_kinetics = EvaluationKinetics(evaluation_kinetics_table)
9961009
else:
@@ -999,7 +1012,6 @@ def create_measurements_for_cycle(
9991012
sheet for sheet in reader.data.keys() if sheet.startswith("Affinity")
10001013
]
10011014
if affinity_sheets:
1002-
# Use the first Affinity sheet found
10031015
affinity_table = _get_table_from_dataframe(
10041016
reader.data[affinity_sheets[0]], split_on="Group"
10051017
)

0 commit comments

Comments
 (0)