From e4680791db753beea2473262d1c11b9daed2c3e8 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 5 Oct 2026 20:17:44 -0700 Subject: [PATCH 01/10] feat: cell line --- src/biodata_schema/components/subjects.py | 16 +++++++++ src/biodata_schema/core/subject.py | 16 ++++++--- tests/test_subjects.py | 41 +++++++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/biodata_schema/components/subjects.py b/src/biodata_schema/components/subjects.py index 749ca46a..6deac61a 100644 --- a/src/biodata_schema/components/subjects.py +++ b/src/biodata_schema/components/subjects.py @@ -5,6 +5,7 @@ from enum import Enum from typing import Annotated, List, Optional +from biodata_models.cell_line import CellLineModel from biodata_models.organizations import Organization from biodata_models.pid_names import PIDName from biodata_models.species import Species, Strain @@ -195,3 +196,18 @@ class CalibrationObject(DataModel): objects: Optional[list[Device]] = Field( default=None, title="Objects", description="For calibration objects that are built up from one or more devices." ) + + +class CellLine(DataModel): + """Description of a cultured cell line""" + + cell_line_name: str = Field(..., title="Cell line name") + cell_line_type: CellLineModel = Field( + ..., title="Cell line type", description="Use CellLineLookup.get_by_name/search_by_name to populate" + ) + species: Species.ONE_OF = Field(..., title="Species") + protein: PIDName = Field(..., title="Protein labeled", description="Protein uses UniProt registry") + gene: PIDName = Field(..., title="Gene targeted", description="Gene uses NCBI taxonomy") + cell_structure: str = Field(..., title="Cell structure protein found in") + fluorescent_protein: PIDName = Field(..., title="Fluorescent protein", description="Uses FPbase") + clone_number: Optional[int] = Field(default=None, title="Clone number") diff --git a/src/biodata_schema/core/subject.py b/src/biodata_schema/core/subject.py index 913ab53b..bba7927b 100644 --- a/src/biodata_schema/core/subject.py +++ b/src/biodata_schema/core/subject.py @@ -1,11 +1,17 @@ -"""schema for mostly mouse metadata""" +"""schema for subject metadata""" from typing import Annotated, Literal, Optional from pydantic import Field, SkipValidation from biodata_schema.base import DataCoreModel, Discriminated, DraftRequirement -from biodata_schema.components.subjects import CalibrationObject, HumanSubject, MouseSubject, NonHumanPrimateSubject +from biodata_schema.components.subjects import ( + CalibrationObject, + CellLine, + HumanSubject, + MouseSubject, + NonHumanPrimateSubject, +) class Subject(DataCoreModel): @@ -20,8 +26,8 @@ class Subject(DataCoreModel): title="Subject ID", ) - subject_details: Discriminated[MouseSubject | HumanSubject | NonHumanPrimateSubject | CalibrationObject] = Field( - ..., title="Subject Details" - ) + subject_details: Discriminated[ + MouseSubject | HumanSubject | NonHumanPrimateSubject | CellLine | CalibrationObject + ] = Field(..., title="Subject Details") notes: Optional[str] = Field(default=None, title="Notes") diff --git a/tests/test_subjects.py b/tests/test_subjects.py index d21db829..7be3a8b9 100644 --- a/tests/test_subjects.py +++ b/tests/test_subjects.py @@ -3,6 +3,7 @@ from datetime import datetime import pytest +from biodata_models.cell_line import CellLineModel from biodata_models.organizations import Organization from biodata_models.pid_names import PIDName from biodata_models.registries import Registry @@ -11,6 +12,7 @@ from biodata_schema.components.subjects import ( BreedingInfo, CalibrationObject, + CellLine, Housing, HumanSubject, LightCycle, @@ -230,3 +232,42 @@ def test_breeding_info(self): assert breeding_info.maternal_genotype == "wt/wt" assert breeding_info.paternal_id == "P001" assert breeding_info.paternal_genotype == "wt/wt" + + +class TestCellLine: + """Test the Cell Line model""" + + def test_cellline(self): + """Test of cell line""" + + subject = CellLine( + cell_line_name="AICS-0005", + cell_line_type=CellLineModel( + name="Human induced pluripotent stem cell line cell", + registry=Registry.CLO, + registry_identifier="CLO:0037308", + ), + species=Species.HUMAN, + protein=PIDName( + name="paxilin", + registry=Registry.UNIPROT, + registry_identifier="P49023", + ), + gene=PIDName( + name="paxilin", + abbreviation="PXN", + registry=Registry.NCBI, + registry_identifier="NM_0028593", + ), + cell_structure="cytoskeleton", + fluorescent_protein=PIDName( + name="enhanced GFP", + abbreviation="EGFP", + registry=Registry.FPbase, + registry_identifier="R9NL8", + ), + clone_number=1, + ) + + assert subject.cell_structure == "cytoskeleton" + assert subject.species == Species.HUMAN From 157057a1c189776bf28a08adfb04657d97e3fb72 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 03:25:56 +0000 Subject: [PATCH 02/10] update docs --- docs/source/components/subjects.md | 16 ++++++++++++++++ docs/source/subject.md | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/source/components/subjects.md b/docs/source/components/subjects.md index 9329bd26..fc3d8bd2 100644 --- a/docs/source/components/subjects.md +++ b/docs/source/components/subjects.md @@ -25,6 +25,22 @@ Description of a calibration object | `objects` | Optional[List[[Device](devices.md#device)]] | Objects (For calibration objects that are built up from one or more devices.) | +### CellLine + +Description of a cultured cell line + +| Field | Type | Title (Description) | +|-------|------|-------------| +| `cell_line_name` | `str` | Cell line name | +| `cell_line_type` | `biodata_models.cell_line.CellLineModel` | Cell line type (Use CellLineLookup.get_by_name/search_by_name to populate) | +| `species` | [Species](../biodata_models/species.md#species) | Species | +| `protein` | [PIDName](../biodata_models/pid_names.md#pidname) | Protein labeled (Protein uses UniProt registry) | +| `gene` | [PIDName](../biodata_models/pid_names.md#pidname) | Gene targeted (Gene uses NCBI taxonomy) | +| `cell_structure` | `str` | Cell structure protein found in | +| `fluorescent_protein` | [PIDName](../biodata_models/pid_names.md#pidname) | Fluorescent protein (Uses FPbase) | +| `clone_number` | `Optional[int]` | Clone number | + + ### HomeCageEnrichment Materials provided in animal home cage diff --git a/docs/source/subject.md b/docs/source/subject.md index 916ab632..95d89b3f 100644 --- a/docs/source/subject.md +++ b/docs/source/subject.md @@ -21,5 +21,5 @@ Description of a subject of data collection | Field | Type | Title (Description) | |-------|------|-------------| | `subject_id` | `str` | Subject ID (Unique identifier for the subject of data acquisition) | -| `subject_details` | [MouseSubject](components/subjects.md#mousesubject) or [HumanSubject](components/subjects.md#humansubject) or [NonHumanPrimateSubject](components/subjects.md#nonhumanprimatesubject) or [CalibrationObject](components/subjects.md#calibrationobject) | Subject Details | +| `subject_details` | [MouseSubject](components/subjects.md#mousesubject) or [HumanSubject](components/subjects.md#humansubject) or [NonHumanPrimateSubject](components/subjects.md#nonhumanprimatesubject) or [CellLine](components/subjects.md#cellline) or [CalibrationObject](components/subjects.md#calibrationobject) | Subject Details | | `notes` | `Optional[str]` | Notes | From 212a7fd68b276b0dc9dc36fffaeb0bf10566e08b Mon Sep 17 00:00:00 2001 From: Saskia de Vries Date: Wed, 7 Oct 2026 15:11:41 -0700 Subject: [PATCH 03/10] cell line/fluorescent cell line --- src/biodata_schema/components/subjects.py | 20 ++++++++++++++------ src/biodata_schema/core/subject.py | 3 ++- tests/test_subjects.py | 11 ++++------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/biodata_schema/components/subjects.py b/src/biodata_schema/components/subjects.py index 6deac61a..3f3c9f2f 100644 --- a/src/biodata_schema/components/subjects.py +++ b/src/biodata_schema/components/subjects.py @@ -202,12 +202,20 @@ class CellLine(DataModel): """Description of a cultured cell line""" cell_line_name: str = Field(..., title="Cell line name") - cell_line_type: CellLineModel = Field( - ..., title="Cell line type", description="Use CellLineLookup.get_by_name/search_by_name to populate" - ) + cell_line_id: str = Field(..., title="Cell line ID") + source: Organization.ONE_OF = Field( + ..., + description="Where the subject was acquired from.", + title="Source", + ) + cell_line_type: CellLineModel = Field(..., title="Cell line type") species: Species.ONE_OF = Field(..., title="Species") + clone_number: Optional[int] = Field(default=None, title="Clone number") + + +class FluorescentCellLine(CellLine): + """Description of a cultured cell line with fluorescent label""" + protein: PIDName = Field(..., title="Protein labeled", description="Protein uses UniProt registry") - gene: PIDName = Field(..., title="Gene targeted", description="Gene uses NCBI taxonomy") - cell_structure: str = Field(..., title="Cell structure protein found in") + cell_structure: str = Field(..., title="Cell structure protein found in") # TODO: ontology or enum in model? fluorescent_protein: PIDName = Field(..., title="Fluorescent protein", description="Uses FPbase") - clone_number: Optional[int] = Field(default=None, title="Clone number") diff --git a/src/biodata_schema/core/subject.py b/src/biodata_schema/core/subject.py index bba7927b..c94127da 100644 --- a/src/biodata_schema/core/subject.py +++ b/src/biodata_schema/core/subject.py @@ -8,6 +8,7 @@ from biodata_schema.components.subjects import ( CalibrationObject, CellLine, + FluorescentCellLine, HumanSubject, MouseSubject, NonHumanPrimateSubject, @@ -27,7 +28,7 @@ class Subject(DataCoreModel): ) subject_details: Discriminated[ - MouseSubject | HumanSubject | NonHumanPrimateSubject | CellLine | CalibrationObject + MouseSubject | HumanSubject | NonHumanPrimateSubject | CellLine | FluorescentCellLine | CalibrationObject ] = Field(..., title="Subject Details") notes: Optional[str] = Field(default=None, title="Notes") diff --git a/tests/test_subjects.py b/tests/test_subjects.py index 7be3a8b9..d64674e8 100644 --- a/tests/test_subjects.py +++ b/tests/test_subjects.py @@ -13,6 +13,7 @@ BreedingInfo, CalibrationObject, CellLine, + FluorescentCellLine, Housing, HumanSubject, LightCycle, @@ -240,25 +241,21 @@ class TestCellLine: def test_cellline(self): """Test of cell line""" - subject = CellLine( + subject = FluorescentCellLineCellLine( cell_line_name="AICS-0005", + cell_line_id="AICS-0005", cell_line_type=CellLineModel( name="Human induced pluripotent stem cell line cell", registry=Registry.CLO, registry_identifier="CLO:0037308", ), + source=Organization.AI, species=Species.HUMAN, protein=PIDName( name="paxilin", registry=Registry.UNIPROT, registry_identifier="P49023", ), - gene=PIDName( - name="paxilin", - abbreviation="PXN", - registry=Registry.NCBI, - registry_identifier="NM_0028593", - ), cell_structure="cytoskeleton", fluorescent_protein=PIDName( name="enhanced GFP", From 525d62817212308d273eca3fb34267f002f90128 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:14:01 +0000 Subject: [PATCH 04/10] update docs --- docs/source/components/subjects.md | 19 +++++++++++++++++-- docs/source/subject.md | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/source/components/subjects.md b/docs/source/components/subjects.md index 64239c0d..55a0c896 100644 --- a/docs/source/components/subjects.md +++ b/docs/source/components/subjects.md @@ -32,12 +32,27 @@ Description of a cultured cell line | Field | Type | Title (Description) | |-------|------|-------------| | `cell_line_name` | `str` | Cell line name | -| `cell_line_type` | `biodata_models.cell_line.CellLineModel` | Cell line type (Use CellLineLookup.get_by_name/search_by_name to populate) | +| `cell_line_id` | `str` | Cell line ID | +| `source` | [Organization](../biodata_models/organizations.md#organization) | Source (Where the subject was acquired from.) | +| `cell_line_type` | `biodata_models.cell_line.CellLineModel` | Cell line type | | `species` | [Species](../biodata_models/species.md#species) | Species | +| `clone_number` | `Optional[int]` | Clone number | + + +### FluorescentCellLine + +Description of a cultured cell line with fluorescent label + +| Field | Type | Title (Description) | +|-------|------|-------------| | `protein` | [PIDName](../biodata_models/pid_names.md#pidname) | Protein labeled (Protein uses UniProt registry) | -| `gene` | [PIDName](../biodata_models/pid_names.md#pidname) | Gene targeted (Gene uses NCBI taxonomy) | | `cell_structure` | `str` | Cell structure protein found in | | `fluorescent_protein` | [PIDName](../biodata_models/pid_names.md#pidname) | Fluorescent protein (Uses FPbase) | +| `cell_line_name` | `str` | Cell line name | +| `cell_line_id` | `str` | Cell line ID | +| `source` | [Organization](../biodata_models/organizations.md#organization) | Source (Where the subject was acquired from.) | +| `cell_line_type` | `biodata_models.cell_line.CellLineModel` | Cell line type | +| `species` | [Species](../biodata_models/species.md#species) | Species | | `clone_number` | `Optional[int]` | Clone number | diff --git a/docs/source/subject.md b/docs/source/subject.md index a9226236..ed11dc08 100644 --- a/docs/source/subject.md +++ b/docs/source/subject.md @@ -21,5 +21,5 @@ Description of a subject of data collection | Field | Type | Title (Description) | |-------|------|-------------| | `subject_name` | `str` | Subject name (Unique name for the subject of data acquisition) | -| `subject_details` | [MouseSubject](components/subjects.md#mousesubject) or [HumanSubject](components/subjects.md#humansubject) or [NonHumanPrimateSubject](components/subjects.md#nonhumanprimatesubject) or [CalibrationObject](components/subjects.md#calibrationobject) | Subject Details | +| `subject_details` | [MouseSubject](components/subjects.md#mousesubject) or [HumanSubject](components/subjects.md#humansubject) or [NonHumanPrimateSubject](components/subjects.md#nonhumanprimatesubject) or [CellLine](components/subjects.md#cellline) or [FluorescentCellLine](components/subjects.md#fluorescentcellline) or [CalibrationObject](components/subjects.md#calibrationobject) | Subject Details | | `notes` | `Optional[str]` | Notes | From bd690003dfe77b1280078682d2781c37370252bf Mon Sep 17 00:00:00 2001 From: Saskia de Vries Date: Wed, 7 Oct 2026 15:14:51 -0700 Subject: [PATCH 05/10] typo --- tests/test_subjects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_subjects.py b/tests/test_subjects.py index d871ed97..57583fac 100644 --- a/tests/test_subjects.py +++ b/tests/test_subjects.py @@ -241,7 +241,7 @@ class TestCellLine: def test_cellline(self): """Test of cell line""" - subject = FluorescentCellLineCellLine( + subject = FluorescentCellLine( cell_line_name="AICS-0005", cell_line_id="AICS-0005", cell_line_type=CellLineModel( From 69b9e2d2bdc6f472949cd8ae1466b9f07ab75796 Mon Sep 17 00:00:00 2001 From: Saskia de Vries Date: Wed, 7 Oct 2026 15:18:01 -0700 Subject: [PATCH 06/10] unused import --- tests/test_subjects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_subjects.py b/tests/test_subjects.py index 57583fac..363b8957 100644 --- a/tests/test_subjects.py +++ b/tests/test_subjects.py @@ -12,7 +12,6 @@ from biodata_schema.components.subjects import ( BreedingInfo, CalibrationObject, - CellLine, FluorescentCellLine, Housing, HumanSubject, From d96fd0d9f01eba80754fa3dd2207e7c33070fc9d Mon Sep 17 00:00:00 2001 From: Saskia de Vries Date: Wed, 7 Oct 2026 15:19:54 -0700 Subject: [PATCH 07/10] format --- src/biodata_schema/components/subjects.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/biodata_schema/components/subjects.py b/src/biodata_schema/components/subjects.py index b1c30237..999ef23d 100644 --- a/src/biodata_schema/components/subjects.py +++ b/src/biodata_schema/components/subjects.py @@ -204,10 +204,10 @@ class CellLine(DataModel): cell_line_name: str = Field(..., title="Cell line name") cell_line_id: str = Field(..., title="Cell line ID") source: Organization.ONE_OF = Field( - ..., - description="Where the subject was acquired from.", - title="Source", - ) + ..., + description="Where the subject was acquired from.", + title="Source", + ) cell_line_type: CellLineModel = Field(..., title="Cell line type") species: Species.ONE_OF = Field(..., title="Species") clone_number: Optional[int] = Field(default=None, title="Clone number") From bd7c87ad8f965dee26de9b91688776702f34f746 Mon Sep 17 00:00:00 2001 From: Saskia de Vries Date: Wed, 7 Oct 2026 15:34:32 -0700 Subject: [PATCH 08/10] edits for dbirman --- src/biodata_schema/components/subjects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biodata_schema/components/subjects.py b/src/biodata_schema/components/subjects.py index 999ef23d..ae12e664 100644 --- a/src/biodata_schema/components/subjects.py +++ b/src/biodata_schema/components/subjects.py @@ -202,7 +202,7 @@ class CellLine(DataModel): """Description of a cultured cell line""" cell_line_name: str = Field(..., title="Cell line name") - cell_line_id: str = Field(..., title="Cell line ID") + cell_line_id: Optional(str) = Field(default=None, title="Cell line ID") source: Organization.ONE_OF = Field( ..., description="Where the subject was acquired from.", @@ -217,5 +217,5 @@ class FluorescentCellLine(CellLine): """Description of a cultured cell line with fluorescent label""" protein: PIDName = Field(..., title="Protein labeled", description="Protein uses UniProt registry") - cell_structure: str = Field(..., title="Cell structure protein found in") # TODO: ontology or enum in model? + cell_structure: str = Field(..., title="Cell structure the protein is found in") # TODO: ontology/enum in model fluorescent_protein: PIDName = Field(..., title="Fluorescent protein", description="Uses FPbase") From a0230c2b2cdfd17e6107fbf2389413bfd8701cf2 Mon Sep 17 00:00:00 2001 From: Saskia de Vries Date: Wed, 7 Oct 2026 15:35:56 -0700 Subject: [PATCH 09/10] typo --- src/biodata_schema/components/subjects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biodata_schema/components/subjects.py b/src/biodata_schema/components/subjects.py index ae12e664..6a205b6c 100644 --- a/src/biodata_schema/components/subjects.py +++ b/src/biodata_schema/components/subjects.py @@ -202,7 +202,7 @@ class CellLine(DataModel): """Description of a cultured cell line""" cell_line_name: str = Field(..., title="Cell line name") - cell_line_id: Optional(str) = Field(default=None, title="Cell line ID") + cell_line_id: Optional[str] = Field(default=None, title="Cell line ID") source: Organization.ONE_OF = Field( ..., description="Where the subject was acquired from.", From 4f324c60a7484c28164f91bc424eb7493c7b5484 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:36:52 +0000 Subject: [PATCH 10/10] update docs --- docs/source/components/subjects.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/components/subjects.md b/docs/source/components/subjects.md index 55a0c896..a3453589 100644 --- a/docs/source/components/subjects.md +++ b/docs/source/components/subjects.md @@ -32,7 +32,7 @@ Description of a cultured cell line | Field | Type | Title (Description) | |-------|------|-------------| | `cell_line_name` | `str` | Cell line name | -| `cell_line_id` | `str` | Cell line ID | +| `cell_line_id` | `Optional[str]` | Cell line ID | | `source` | [Organization](../biodata_models/organizations.md#organization) | Source (Where the subject was acquired from.) | | `cell_line_type` | `biodata_models.cell_line.CellLineModel` | Cell line type | | `species` | [Species](../biodata_models/species.md#species) | Species | @@ -46,10 +46,10 @@ Description of a cultured cell line with fluorescent label | Field | Type | Title (Description) | |-------|------|-------------| | `protein` | [PIDName](../biodata_models/pid_names.md#pidname) | Protein labeled (Protein uses UniProt registry) | -| `cell_structure` | `str` | Cell structure protein found in | +| `cell_structure` | `str` | Cell structure the protein is found in | | `fluorescent_protein` | [PIDName](../biodata_models/pid_names.md#pidname) | Fluorescent protein (Uses FPbase) | | `cell_line_name` | `str` | Cell line name | -| `cell_line_id` | `str` | Cell line ID | +| `cell_line_id` | `Optional[str]` | Cell line ID | | `source` | [Organization](../biodata_models/organizations.md#organization) | Source (Where the subject was acquired from.) | | `cell_line_type` | `biodata_models.cell_line.CellLineModel` | Cell line type | | `species` | [Species](../biodata_models/species.md#species) | Species |