diff --git a/nwb_linkml/src/nwb_linkml/adapters/dataset.py b/nwb_linkml/src/nwb_linkml/adapters/dataset.py index d1ee2b4..ad84c66 100644 --- a/nwb_linkml/src/nwb_linkml/adapters/dataset.py +++ b/nwb_linkml/src/nwb_linkml/adapters/dataset.py @@ -602,6 +602,7 @@ class MapClassRange(DatasetMap): # DynamicTable special cases # -------------------------------------------------- + class MapNVectors(DatasetMap): """ An unnamed container that indicates an arbitrary quantity of some other neurodata type. diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_base.py index e746c6d..263d389 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_base.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_behavior.py index a6848de..5691dab 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_device.py index 77f3252..ab24817 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ecephys.py index 095da94..136ec40 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_epoch.py index 1c7071c..4ab3c01 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_file.py index bd10fab..ae16391 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_file.py @@ -67,12 +67,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -484,7 +500,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -493,7 +509,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -502,7 +518,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -511,7 +527,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -530,9 +546,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_icephys.py index 667bc6f..439d5af 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_icephys.py @@ -69,12 +69,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -922,9 +938,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_image.py index afd8481..33784d6 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_image.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_misc.py index fbde138..e8a4896 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -464,9 +480,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) @@ -482,7 +495,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -497,7 +510,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -509,7 +522,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -525,7 +538,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -537,7 +550,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -552,16 +565,16 @@ class Units(DynamicTable): electrode_group: Optional[List[ElectrodeGroup]] = Field( None, description="""Electrode group that each spike unit came from.""" ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], @@ -578,9 +591,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ogen.py index 89f02c0..998dda0 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ophys.py index 5f89bc4..70db9d7 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_ophys.py @@ -66,12 +66,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_retinotopy.py index fc034dc..17edeec 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/core_nwb_retinotopy.py @@ -58,12 +58,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -218,7 +234,7 @@ class ImagingRetinotopy(NWBDataInterface): } }, ) - axis_1_power_map: Named[Optional[AxisMap]] = Field( + axis_1_power_map: Optional[Named[AxisMap]] = Field( None, description="""Power response on the first measured axis. Response is scaled so 0.0 is no power in the response and 1.0 is maximum relative power.""", json_schema_extra={ @@ -242,7 +258,7 @@ class ImagingRetinotopy(NWBDataInterface): } }, ) - axis_2_power_map: Named[Optional[AxisMap]] = Field( + axis_2_power_map: Optional[Named[AxisMap]] = Field( None, description="""Power response to stimulus on the second measured axis.""", json_schema_extra={ diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/namespace.py index c7c2358..d4b265d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_0/namespace.py @@ -176,12 +176,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_base.py index 06b0763..f0f43be 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_base.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_behavior.py index 1b14f72..e96918c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_device.py index 339f3e5..80de9c0 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ecephys.py index 0abefd4..169dd5e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_epoch.py index 2eae6a3..ed1353e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_file.py index 80125a3..b5a0b9b 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_file.py @@ -67,12 +67,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -484,7 +500,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -493,7 +509,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -502,7 +518,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -511,7 +527,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -530,9 +546,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_icephys.py index 9e5c172..991c1e8 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_icephys.py @@ -69,12 +69,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -922,9 +938,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_image.py index 9903c33..52c10a5 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_image.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_misc.py index 898d566..19a036f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -464,9 +480,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) @@ -482,7 +495,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -497,7 +510,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -509,7 +522,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -525,7 +538,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -537,7 +550,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -552,16 +565,16 @@ class Units(DynamicTable): electrode_group: Optional[List[ElectrodeGroup]] = Field( None, description="""Electrode group that each spike unit came from.""" ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], @@ -578,9 +591,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ogen.py index 5aa8d0f..609baf0 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ophys.py index 8c0234c..a951c51 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_ophys.py @@ -66,12 +66,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_retinotopy.py index 96dfdd0..1c6f4ad 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/core_nwb_retinotopy.py @@ -58,12 +58,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -218,7 +234,7 @@ class ImagingRetinotopy(NWBDataInterface): } }, ) - axis_1_power_map: Named[Optional[AxisMap]] = Field( + axis_1_power_map: Optional[Named[AxisMap]] = Field( None, description="""Power response on the first measured axis. Response is scaled so 0.0 is no power in the response and 1.0 is maximum relative power.""", json_schema_extra={ @@ -242,7 +258,7 @@ class ImagingRetinotopy(NWBDataInterface): } }, ) - axis_2_power_map: Named[Optional[AxisMap]] = Field( + axis_2_power_map: Optional[Named[AxisMap]] = Field( None, description="""Power response to stimulus on the second measured axis.""", json_schema_extra={ diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/namespace.py index a152e89..7f2ade1 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_1/namespace.py @@ -176,12 +176,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_base.py index 3052a7f..956e37d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_base.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_behavior.py index 9f00490..271fceb 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_device.py index 5b492f5..28aa954 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ecephys.py index 8bfcbc5..9664726 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_epoch.py index 685a3e1..c12a965 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_file.py index 93a273e..ec66471 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_file.py @@ -67,12 +67,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -484,7 +500,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -493,7 +509,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -502,7 +518,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -511,7 +527,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -530,9 +546,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_icephys.py index 89f98be..9b7729d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_icephys.py @@ -69,12 +69,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -922,9 +938,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_image.py index c050722..6e805b1 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_image.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_misc.py index ed39a48..d80af52 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -464,9 +480,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) @@ -482,7 +495,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -497,7 +510,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -509,7 +522,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -525,7 +538,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -537,7 +550,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -552,16 +565,16 @@ class Units(DynamicTable): electrode_group: Optional[List[ElectrodeGroup]] = Field( None, description="""Electrode group that each spike unit came from.""" ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], @@ -578,9 +591,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ogen.py index 630218f..debdaf9 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ophys.py index 4456b5e..e7b56da 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_ophys.py @@ -66,12 +66,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_retinotopy.py index 6669e29..bfa2ad5 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/namespace.py index a962b02..9ba793b 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_2/namespace.py @@ -179,12 +179,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_base.py index ca4642a..0e81486 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_base.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_behavior.py index 031a527..42613b4 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_device.py index b6a0790..1aeeb6c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ecephys.py index ce573c0..d4f5172 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_epoch.py index 38ab101..61f894b 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_file.py index ba6e4b9..9167a4d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_file.py @@ -68,12 +68,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -460,7 +476,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -469,7 +485,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -478,7 +494,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -487,7 +503,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -506,9 +522,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_icephys.py index 9a6ad6e..8067eb7 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_icephys.py @@ -69,12 +69,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -922,9 +938,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_image.py index a30fe3b..05c1d6e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_image.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_misc.py index dca9ee9..5ff807c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -464,9 +480,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) @@ -482,7 +495,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -497,7 +510,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -509,7 +522,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -525,7 +538,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -537,7 +550,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -552,16 +565,16 @@ class Units(DynamicTable): electrode_group: Optional[List[ElectrodeGroup]] = Field( None, description="""Electrode group that each spike unit came from.""" ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], @@ -578,9 +591,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ogen.py index 79e484e..20f6353 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ophys.py index a7f6886..b91e448 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -338,7 +354,7 @@ class PlaneSegmentation(DynamicTable): None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -354,7 +370,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -394,9 +410,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_retinotopy.py index d0b4129..362bc59 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/namespace.py index ddb8793..23ec3dd 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_4/namespace.py @@ -186,12 +186,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_base.py index 0157a3e..86fe03f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_base.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_behavior.py index 711219e..f4f5e96 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_device.py index 077b62b..5abfc5d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ecephys.py index aa674e7..48d2503 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_epoch.py index fd13aa2..6a8ba5a 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_file.py index 7603841..59aa79e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_file.py @@ -68,12 +68,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -460,7 +476,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -469,7 +485,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -478,7 +494,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -487,7 +503,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -506,9 +522,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_icephys.py index 754e05d..ee68bff 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_icephys.py @@ -69,12 +69,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -922,9 +938,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_image.py index 9210d64..f3d0d5f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_image.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_misc.py index e97da8b..5faeb05 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -464,9 +480,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) @@ -482,7 +495,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -497,7 +510,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -509,7 +522,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -525,7 +538,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -537,7 +550,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -552,16 +565,16 @@ class Units(DynamicTable): electrode_group: Optional[List[ElectrodeGroup]] = Field( None, description="""Electrode group that each spike unit came from.""" ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], @@ -578,9 +591,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ogen.py index 476bec4..6c81182 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ophys.py index c32e213..98c3a53 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -340,7 +356,7 @@ class PlaneSegmentation(DynamicTable): None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -356,7 +372,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -396,9 +412,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_retinotopy.py index 8da471b..5466646 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/namespace.py index 724c1e6..5d12f36 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_2_5/namespace.py @@ -186,12 +186,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_base.py index b18e626..ad3c5f4 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_base.py @@ -50,12 +50,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -289,7 +305,7 @@ class ProcessingModule(NWBContainer): {"from_schema": "core.nwb.base", "tree_root": True} ) - value: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + value: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} @@ -309,7 +325,7 @@ class Images(NWBDataInterface): name: str = Field("Images", json_schema_extra={"linkml_meta": {"ifabsent": "string(Images)"}}) description: str = Field(..., description="""Description of this collection of images.""") - image: List[Image] = Field(..., description="""Images stored in this collection.""") + image: List[str] = Field(..., description="""Images stored in this collection.""") # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_behavior.py index 758ac17..8358db6 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -182,7 +198,7 @@ class BehavioralEpochs(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[IntervalSeries]] = Field( + value: Optional[Dict[str, IntervalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "IntervalSeries"}]}} ) name: str = Field(...) @@ -197,7 +213,7 @@ class BehavioralEvents(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -212,7 +228,7 @@ class BehavioralTimeSeries(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -227,7 +243,7 @@ class PupilTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -242,7 +258,7 @@ class EyeTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -257,7 +273,7 @@ class CompassDirection(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -272,7 +288,7 @@ class Position(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_device.py index 2eb59fb..5c0f451 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ecephys.py index 1a708c9..2676bd5 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -369,7 +385,7 @@ class EventWaveform(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[SpikeEventSeries]] = Field( + value: Optional[Dict[str, SpikeEventSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpikeEventSeries"}]}} ) name: str = Field(...) @@ -384,7 +400,7 @@ class FilteredEphys(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) @@ -399,7 +415,7 @@ class LFP(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_epoch.py index 74265d3..93ea1ba 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class TimeIntervalsTimeseries(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_file.py index 5954c68..d692065 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_file.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -167,28 +183,28 @@ class NWBFile(NWBContainer): ..., description="""Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in \"Z\" with no timezone offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).""", ) - acquisition: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + acquisition: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, description="""Data streams recorded from the system, including ephys, ophys, tracking, etc. This group should be read-only after the experiment is completed and timestamps are corrected to a common timebase. The data stored here may be links to raw data stored in external NWB files. This will allow keeping bulky raw data out of the file while preserving the option of keeping some/all in the file. Acquired data includes tracking and experimental data streams (i.e., everything measured from the system). If bulky data is stored in the /acquisition group, the data can exist in a separate NWB file that is linked to by the file being used for processing and analysis.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} }, ) - analysis: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + analysis: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""Lab-specific and custom scientific analysis of data. There is no defined format for the content of this group - the format is up to the individual user/lab. To facilitate sharing analysis data between labs, the contents here should be stored in standard types (e.g., neurodata_types) and appropriately documented. The file can store lab-specific and custom data analysis without restriction on its form or schema, reducing data formatting restrictions on end users. Such data should be placed in the analysis group. The analysis data should be documented so that it could be shared with other labs.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - scratch: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + scratch: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""A place to store one-off analysis results. Data placed here is not intended for sharing. By placing data here, users acknowledge that there is no guarantee that their data meets any standard.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - processing: Optional[List[ProcessingModule]] = Field( + processing: Optional[Dict[str, ProcessingModule]] = Field( None, description="""The home for ProcessingModules. These modules perform intermediate analysis of data that is necessary to perform before scientific analysis. Examples include spike clustering, extracting position from tracking data, stitching together image slices. ProcessingModules can be large and express many data sets from relatively complex analysis (e.g., spike detection and clustering) or small, representing extraction of position information from tracking video, or even binary lick/no-lick decisions. Common software tools (e.g., klustakwik, MClust) are expected to read/write data here. 'Processing' refers to intermediate analysis of the acquired data to make it more amenable to scientific analysis.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ProcessingModule"}]}}, @@ -221,12 +237,12 @@ class NWBFileStimulus(ConfiguredBaseModel): "linkml_meta": {"equals_string": "stimulus", "ifabsent": "string(stimulus)"} }, ) - presentation: Optional[List[TimeSeries]] = Field( + presentation: Optional[Dict[str, TimeSeries]] = Field( None, description="""Stimuli presented during the experiment.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}}, ) - templates: Optional[List[TimeSeries]] = Field( + templates: Optional[Dict[str, TimeSeries]] = Field( None, description="""Template stimuli. Timestamps in templates are based on stimulus design and are relative to the beginning of the stimulus. When templates are used, the stimulus instances must convert presentation times to the experiment`s time reference frame.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}}, @@ -304,11 +320,11 @@ class NWBFileGeneral(ConfiguredBaseModel): None, description="""Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, etc.""", ) - lab_meta_data: Optional[List[LabMetaData]] = Field( + lab_meta_data: Optional[Dict[str, LabMetaData]] = Field( None, description="""Place-holder than can be extended so that lab-specific meta-data can be placed in /general.""", ) - devices: Optional[List[Device]] = Field( + devices: Optional[Dict[str, Device]] = Field( None, description="""Description of hardware devices used during experiment, e.g., monitors, ADC boards, microscopes, etc.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "Device"}]}}, @@ -323,12 +339,12 @@ class NWBFileGeneral(ConfiguredBaseModel): intracellular_ephys: Optional[GeneralIntracellularEphys] = Field( None, description="""Metadata related to intracellular electrophysiology.""" ) - optogenetics: Optional[List[OptogeneticStimulusSite]] = Field( + optogenetics: Optional[Dict[str, OptogeneticStimulusSite]] = Field( None, description="""Metadata describing optogenetic stimuluation.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "OptogeneticStimulusSite"}]}}, ) - optophysiology: Optional[List[ImagingPlane]] = Field( + optophysiology: Optional[Dict[str, ImagingPlane]] = Field( None, description="""Metadata related to optophysiology.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImagingPlane"}]}}, @@ -368,7 +384,7 @@ class GeneralExtracellularEphys(ConfiguredBaseModel): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( + electrode_group: Optional[Dict[str, ElectrodeGroup]] = Field( None, description="""Physical group of electrodes.""" ) electrodes: Optional[ExtracellularEphysElectrodes] = Field( @@ -443,8 +459,14 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - group: List[ElectrodeGroup] = Field( - ..., description="""Reference to the ElectrodeGroup this electrode is a part of.""" + group: VectorData[NDArray[Any, ElectrodeGroup]] = Field( + ..., + description="""Reference to the ElectrodeGroup this electrode is a part of.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) group_name: VectorData[NDArray[Any, str]] = Field( ..., @@ -455,7 +477,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -464,7 +486,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -473,7 +495,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -482,7 +504,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -501,9 +523,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class GeneralIntracellularEphys(ConfiguredBaseModel): @@ -526,7 +545,7 @@ class GeneralIntracellularEphys(ConfiguredBaseModel): None, description="""Description of filtering used. Includes filtering type and parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute for each TimeSeries.""", ) - intracellular_electrode: Optional[List[IntracellularElectrode]] = Field( + intracellular_electrode: Optional[Dict[str, IntracellularElectrode]] = Field( None, description="""An intracellular electrode.""" ) sweep_table: Optional[SweepTable] = Field( @@ -557,7 +576,7 @@ class NWBFileIntervals(ConfiguredBaseModel): invalid_times: Optional[TimeIntervals] = Field( None, description="""Time intervals that should be removed from analysis.""" ) - time_intervals: Optional[List[TimeIntervals]] = Field( + time_intervals: Optional[Dict[str, TimeIntervals]] = Field( None, description="""Optional additional table(s) for describing other experimental time intervals.""", ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_icephys.py index 54b3ae9..1fb2a04 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_icephys.py @@ -69,12 +69,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -899,8 +915,14 @@ class SweepTable(DynamicTable): } }, ) - series: List[PatchClampSeries] = Field( - ..., description="""The PatchClampSeries with the sweep number in that row.""" + series: VectorData[NDArray[Any, PatchClampSeries]] = Field( + ..., + description="""The PatchClampSeries with the sweep number in that row.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) series_index: Named[VectorIndex] = Field( ..., @@ -924,9 +946,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_image.py index 63577ef..8758ca8 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_image.py @@ -50,12 +50,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_misc.py index 0560527..ac3b366 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -328,7 +344,7 @@ class DecompositionSeries(TimeSeries): ..., description="""Data decomposed into frequency bands.""" ) metric: str = Field(..., description="""The metric used, e.g. phase, amplitude, power.""") - source_channels: Named[Optional[DynamicTableRegion]] = Field( + source_channels: Optional[Named[DynamicTableRegion]] = Field( None, description="""DynamicTableRegion pointer to the channels that this decomposition series was generated from.""", json_schema_extra={ @@ -476,9 +492,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class Units(DynamicTable): @@ -491,7 +504,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -506,7 +519,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -518,7 +531,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -534,7 +547,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -546,7 +559,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -558,26 +571,32 @@ class Units(DynamicTable): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( - None, description="""Electrode group that each spike unit came from.""" + electrode_group: Optional[VectorData[NDArray[Any, ElectrodeGroup]]] = Field( + None, + description="""Electrode group that each spike unit came from.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform standard deviation for each spike unit.""") - waveforms: VectorData[Optional[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( + waveforms: Optional[VectorData[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( Field( None, description="""Individual waveforms for each spike on each electrode. This is a doubly indexed column. The 'waveforms_index' column indexes which waveforms in this column belong to the same spike event for a given unit, where each waveform was recorded from a different electrode. The 'waveforms_index_index' column indexes the 'waveforms_index' column to indicate which spike events belong to a given unit. For example, if the 'waveforms_index_index' column has values [2, 5, 6], then the first 2 elements of the 'waveforms_index' column correspond to the 2 spike events of the first unit, the next 3 elements of the 'waveforms_index' column correspond to the 3 spike events of the second unit, and the next 1 element of the 'waveforms_index' column corresponds to the 1 spike event of the third unit. If the 'waveforms_index' column has values [3, 6, 8, 10, 12, 13], then the first 3 elements of the 'waveforms' column contain the 3 spike waveforms that were recorded from 3 different electrodes for the first spike time of the first unit. See https://nwb-schema.readthedocs.io/en/stable/format_description.html#doubly-ragged-arrays for a graphical representation of this example. When there is only one electrode for each unit (i.e., each spike time is associated with a single waveform), then the 'waveforms_index' column will have values 1, 2, ..., N, where N is the number of spike events. The number of electrodes for each spike event should be the same within a given unit. The 'electrodes' column should be used to indicate which electrodes are associated with each unit, and the order of the waveforms within a given unit x spike event should be in the same order as the electrodes referenced in the 'electrodes' column of this table. The number of samples for each waveform must be the same.""", @@ -588,7 +607,7 @@ class Units(DynamicTable): }, ) ) - waveforms_index: Named[Optional[VectorIndex]] = Field( + waveforms_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms dataset. One value for every spike event. See 'waveforms' for more detail.""", json_schema_extra={ @@ -600,7 +619,7 @@ class Units(DynamicTable): } }, ) - waveforms_index_index: Named[Optional[VectorIndex]] = Field( + waveforms_index_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms_index dataset. One value for every unit (row in the table). See 'waveforms' for more detail.""", json_schema_extra={ @@ -622,9 +641,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class UnitsSpikeTimes(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ogen.py index 22f7adc..bf95c5c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ophys.py index ab3cdbc..670269a 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -299,7 +315,7 @@ class DfOverF(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -314,7 +330,7 @@ class Fluorescence(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -329,7 +345,7 @@ class ImageSegmentation(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[PlaneSegmentation]] = Field( + value: Optional[Dict[str, PlaneSegmentation]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "PlaneSegmentation"}]}} ) name: str = Field(...) @@ -345,11 +361,18 @@ class PlaneSegmentation(DynamicTable): ) name: str = Field(...) - image_mask: Optional[PlaneSegmentationImageMask] = Field( + image_mask: Optional[ + VectorData[ + Union[ + NDArray[Shape["* num_roi, * num_x, * num_y"], Any], + NDArray[Shape["* num_roi, * num_x, * num_y, * num_z"], Any], + ] + ] + ] = Field( None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -365,7 +388,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -381,7 +404,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - reference_images: Optional[List[ImageSeries]] = Field( + reference_images: Optional[Dict[str, ImageSeries]] = Field( None, description="""Image stacks that the segmentation masks apply to.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImageSeries"}]}}, @@ -405,33 +428,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) - - -class PlaneSegmentationImageMask(VectorData): - """ - ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "core.nwb.ophys"}) - - name: Literal["image_mask"] = Field( - "image_mask", - json_schema_extra={ - "linkml_meta": {"equals_string": "image_mask", "ifabsent": "string(image_mask)"} - }, - ) - description: str = Field(..., description="""Description of what these vectors represent.""") - value: Optional[ - Union[ - NDArray[Shape["* dim0"], Any], - NDArray[Shape["* dim0, * dim1"], Any], - NDArray[Shape["* dim0, * dim1, * dim2"], Any], - NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], - ] - ] = Field(None) class PlaneSegmentationPixelMask(VectorData): @@ -554,7 +550,7 @@ class ImagingPlane(NWBContainer): None, description="""Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = \"Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral).\"""", ) - optical_channel: List[OpticalChannel] = Field( + optical_channel: Dict[str, OpticalChannel] = Field( ..., description="""An optical channel used to record from an imaging plane.""" ) device: Union[Device, str] = Field( @@ -668,7 +664,7 @@ class MotionCorrection(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[CorrectedImageStack]] = Field( + value: Optional[Dict[str, CorrectedImageStack]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "CorrectedImageStack"}]}} ) name: str = Field(...) @@ -710,7 +706,6 @@ DfOverF.model_rebuild() Fluorescence.model_rebuild() ImageSegmentation.model_rebuild() PlaneSegmentation.model_rebuild() -PlaneSegmentationImageMask.model_rebuild() PlaneSegmentationPixelMask.model_rebuild() PlaneSegmentationVoxelMask.model_rebuild() ImagingPlane.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_retinotopy.py index 07097bb..5c78658 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/namespace.py index 97dfca3..2125d57 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_3_0/namespace.py @@ -117,7 +117,6 @@ from ...core.v2_3_0.core_nwb_ophys import ( MotionCorrection, OpticalChannel, PlaneSegmentation, - PlaneSegmentationImageMask, PlaneSegmentationPixelMask, PlaneSegmentationVoxelMask, RoiResponseSeries, @@ -134,7 +133,7 @@ from ...core.v2_3_0.core_nwb_retinotopy import ( ImagingRetinotopyVasculatureImage, ) from ...hdmf_common.v1_5_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -189,12 +188,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_base.py index 2cb8f77..f8b6d99 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_base.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -103,7 +119,7 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - def __init__(self, value: Optional[NDArray] = None, **kwargs): + def __init__(self, value: Optional[T] = None, **kwargs): if value is not None and "value" not in kwargs: kwargs["value"] = value super().__init__(**kwargs) @@ -484,7 +500,7 @@ class ProcessingModule(NWBContainer): {"from_schema": "core.nwb.base", "tree_root": True} ) - value: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + value: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} @@ -504,7 +520,7 @@ class Images(NWBDataInterface): name: str = Field("Images", json_schema_extra={"linkml_meta": {"ifabsent": "string(Images)"}}) description: str = Field(..., description="""Description of this collection of images.""") - image: List[Image] = Field(..., description="""Images stored in this collection.""") + image: List[str] = Field(..., description="""Images stored in this collection.""") # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_behavior.py index 8444b65..7c0abb8 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -182,7 +198,7 @@ class BehavioralEpochs(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[IntervalSeries]] = Field( + value: Optional[Dict[str, IntervalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "IntervalSeries"}]}} ) name: str = Field(...) @@ -197,7 +213,7 @@ class BehavioralEvents(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -212,7 +228,7 @@ class BehavioralTimeSeries(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -227,7 +243,7 @@ class PupilTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -242,7 +258,7 @@ class EyeTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -257,7 +273,7 @@ class CompassDirection(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -272,7 +288,7 @@ class Position(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_device.py index b468f9a..436d2d4 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ecephys.py index 6153aad..ac26b29 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -369,7 +385,7 @@ class EventWaveform(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[SpikeEventSeries]] = Field( + value: Optional[Dict[str, SpikeEventSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpikeEventSeries"}]}} ) name: str = Field(...) @@ -384,7 +400,7 @@ class FilteredEphys(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) @@ -399,7 +415,7 @@ class LFP(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_epoch.py index 1967963..25894a3 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -171,7 +187,7 @@ class TimeIntervals(DynamicTable): timeseries: Optional[TimeIntervalsTimeseries] = Field( None, description="""An index into a TimeSeries object.""" ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -193,9 +209,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class TimeIntervalsTimeseries(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_file.py index c337990..84d5b9a 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_file.py @@ -71,12 +71,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -175,28 +191,28 @@ class NWBFile(NWBContainer): ..., description="""Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in \"Z\" with no timezone offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).""", ) - acquisition: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + acquisition: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, description="""Data streams recorded from the system, including ephys, ophys, tracking, etc. This group should be read-only after the experiment is completed and timestamps are corrected to a common timebase. The data stored here may be links to raw data stored in external NWB files. This will allow keeping bulky raw data out of the file while preserving the option of keeping some/all in the file. Acquired data includes tracking and experimental data streams (i.e., everything measured from the system). If bulky data is stored in the /acquisition group, the data can exist in a separate NWB file that is linked to by the file being used for processing and analysis.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} }, ) - analysis: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + analysis: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""Lab-specific and custom scientific analysis of data. There is no defined format for the content of this group - the format is up to the individual user/lab. To facilitate sharing analysis data between labs, the contents here should be stored in standard types (e.g., neurodata_types) and appropriately documented. The file can store lab-specific and custom data analysis without restriction on its form or schema, reducing data formatting restrictions on end users. Such data should be placed in the analysis group. The analysis data should be documented so that it could be shared with other labs.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - scratch: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + scratch: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""A place to store one-off analysis results. Data placed here is not intended for sharing. By placing data here, users acknowledge that there is no guarantee that their data meets any standard.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - processing: Optional[List[ProcessingModule]] = Field( + processing: Optional[Dict[str, ProcessingModule]] = Field( None, description="""The home for ProcessingModules. These modules perform intermediate analysis of data that is necessary to perform before scientific analysis. Examples include spike clustering, extracting position from tracking data, stitching together image slices. ProcessingModules can be large and express many data sets from relatively complex analysis (e.g., spike detection and clustering) or small, representing extraction of position information from tracking video, or even binary lick/no-lick decisions. Common software tools (e.g., klustakwik, MClust) are expected to read/write data here. 'Processing' refers to intermediate analysis of the acquired data to make it more amenable to scientific analysis.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ProcessingModule"}]}}, @@ -229,12 +245,12 @@ class NWBFileStimulus(ConfiguredBaseModel): "linkml_meta": {"equals_string": "stimulus", "ifabsent": "string(stimulus)"} }, ) - presentation: Optional[List[TimeSeries]] = Field( + presentation: Optional[Dict[str, TimeSeries]] = Field( None, description="""Stimuli presented during the experiment.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}}, ) - templates: Optional[List[TimeSeries]] = Field( + templates: Optional[Dict[str, TimeSeries]] = Field( None, description="""Template stimuli. Timestamps in templates are based on stimulus design and are relative to the beginning of the stimulus. When templates are used, the stimulus instances must convert presentation times to the experiment`s time reference frame.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}}, @@ -312,11 +328,11 @@ class NWBFileGeneral(ConfiguredBaseModel): None, description="""Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, etc.""", ) - lab_meta_data: Optional[List[LabMetaData]] = Field( + lab_meta_data: Optional[Dict[str, LabMetaData]] = Field( None, description="""Place-holder than can be extended so that lab-specific meta-data can be placed in /general.""", ) - devices: Optional[List[Device]] = Field( + devices: Optional[Dict[str, Device]] = Field( None, description="""Description of hardware devices used during experiment, e.g., monitors, ADC boards, microscopes, etc.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "Device"}]}}, @@ -331,12 +347,12 @@ class NWBFileGeneral(ConfiguredBaseModel): intracellular_ephys: Optional[GeneralIntracellularEphys] = Field( None, description="""Metadata related to intracellular electrophysiology.""" ) - optogenetics: Optional[List[OptogeneticStimulusSite]] = Field( + optogenetics: Optional[Dict[str, OptogeneticStimulusSite]] = Field( None, description="""Metadata describing optogenetic stimuluation.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "OptogeneticStimulusSite"}]}}, ) - optophysiology: Optional[List[ImagingPlane]] = Field( + optophysiology: Optional[Dict[str, ImagingPlane]] = Field( None, description="""Metadata related to optophysiology.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImagingPlane"}]}}, @@ -376,7 +392,7 @@ class GeneralExtracellularEphys(ConfiguredBaseModel): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( + electrode_group: Optional[Dict[str, ElectrodeGroup]] = Field( None, description="""Physical group of electrodes.""" ) electrodes: Optional[ExtracellularEphysElectrodes] = Field( @@ -451,8 +467,14 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - group: List[ElectrodeGroup] = Field( - ..., description="""Reference to the ElectrodeGroup this electrode is a part of.""" + group: VectorData[NDArray[Any, ElectrodeGroup]] = Field( + ..., + description="""Reference to the ElectrodeGroup this electrode is a part of.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) group_name: VectorData[NDArray[Any, str]] = Field( ..., @@ -463,7 +485,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -472,7 +494,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -481,7 +503,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -490,7 +512,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference used for this electrode.""", json_schema_extra={ @@ -509,9 +531,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class GeneralIntracellularEphys(ConfiguredBaseModel): @@ -534,7 +553,7 @@ class GeneralIntracellularEphys(ConfiguredBaseModel): None, description="""[DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute for each TimeSeries.""", ) - intracellular_electrode: Optional[List[IntracellularElectrode]] = Field( + intracellular_electrode: Optional[Dict[str, IntracellularElectrode]] = Field( None, description="""An intracellular electrode.""" ) sweep_table: Optional[SweepTable] = Field( @@ -586,7 +605,7 @@ class NWBFileIntervals(ConfiguredBaseModel): invalid_times: Optional[TimeIntervals] = Field( None, description="""Time intervals that should be removed from analysis.""" ) - time_intervals: Optional[List[TimeIntervals]] = Field( + time_intervals: Optional[Dict[str, TimeIntervals]] = Field( None, description="""Optional additional table(s) for describing other experimental time intervals.""", ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_icephys.py index 29932a6..d4ebcb3 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_icephys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -902,8 +918,14 @@ class SweepTable(DynamicTable): } }, ) - series: List[PatchClampSeries] = Field( - ..., description="""The PatchClampSeries with the sweep number in that row.""" + series: VectorData[NDArray[Any, PatchClampSeries]] = Field( + ..., + description="""The PatchClampSeries with the sweep number in that row.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) series_index: Named[VectorIndex] = Field( ..., @@ -927,9 +949,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularElectrodesTable(DynamicTable): @@ -952,8 +971,14 @@ class IntracellularElectrodesTable(DynamicTable): } }, ) - electrode: List[IntracellularElectrode] = Field( - ..., description="""Column for storing the reference to the intracellular electrode.""" + electrode: VectorData[NDArray[Any, IntracellularElectrode]] = Field( + ..., + description="""Column for storing the reference to the intracellular electrode.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) colnames: List[str] = Field( ..., @@ -964,9 +989,6 @@ class IntracellularElectrodesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularStimuliTable(DynamicTable): @@ -1010,9 +1032,6 @@ class IntracellularStimuliTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularResponsesTable(DynamicTable): @@ -1056,9 +1075,6 @@ class IntracellularResponsesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularRecordingsTable(AlignedDynamicTable): @@ -1110,7 +1126,7 @@ class IntracellularRecordingsTable(AlignedDynamicTable): responses: IntracellularResponsesTable = Field( ..., description="""Table for storing intracellular response related metadata.""" ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) colnames: List[str] = Field( @@ -1122,9 +1138,6 @@ class IntracellularRecordingsTable(AlignedDynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTable(DynamicTable): @@ -1171,9 +1184,6 @@ class SimultaneousRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTableRecordings(DynamicTableRegion): @@ -1259,9 +1269,6 @@ class SequentialRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SequentialRecordingsTableSimultaneousRecordings(DynamicTableRegion): @@ -1338,9 +1345,6 @@ class RepetitionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class RepetitionsTableSequentialRecordings(DynamicTableRegion): @@ -1419,9 +1423,6 @@ class ExperimentalConditionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class ExperimentalConditionsTableRepetitions(DynamicTableRegion): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_image.py index 4cf5fd4..8fd3288 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_image.py @@ -50,12 +50,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_misc.py index f83ca9e..3ab6b75 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -328,7 +344,7 @@ class DecompositionSeries(TimeSeries): ..., description="""Data decomposed into frequency bands.""" ) metric: str = Field(..., description="""The metric used, e.g. phase, amplitude, power.""") - source_channels: Named[Optional[DynamicTableRegion]] = Field( + source_channels: Optional[Named[DynamicTableRegion]] = Field( None, description="""DynamicTableRegion pointer to the channels that this decomposition series was generated from.""", json_schema_extra={ @@ -476,9 +492,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class Units(DynamicTable): @@ -491,7 +504,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -506,7 +519,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -518,7 +531,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -534,7 +547,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -546,7 +559,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -558,26 +571,32 @@ class Units(DynamicTable): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( - None, description="""Electrode group that each spike unit came from.""" + electrode_group: Optional[VectorData[NDArray[Any, ElectrodeGroup]]] = Field( + None, + description="""Electrode group that each spike unit came from.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform standard deviation for each spike unit.""") - waveforms: VectorData[Optional[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( + waveforms: Optional[VectorData[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( Field( None, description="""Individual waveforms for each spike on each electrode. This is a doubly indexed column. The 'waveforms_index' column indexes which waveforms in this column belong to the same spike event for a given unit, where each waveform was recorded from a different electrode. The 'waveforms_index_index' column indexes the 'waveforms_index' column to indicate which spike events belong to a given unit. For example, if the 'waveforms_index_index' column has values [2, 5, 6], then the first 2 elements of the 'waveforms_index' column correspond to the 2 spike events of the first unit, the next 3 elements of the 'waveforms_index' column correspond to the 3 spike events of the second unit, and the next 1 element of the 'waveforms_index' column corresponds to the 1 spike event of the third unit. If the 'waveforms_index' column has values [3, 6, 8, 10, 12, 13], then the first 3 elements of the 'waveforms' column contain the 3 spike waveforms that were recorded from 3 different electrodes for the first spike time of the first unit. See https://nwb-schema.readthedocs.io/en/stable/format_description.html#doubly-ragged-arrays for a graphical representation of this example. When there is only one electrode for each unit (i.e., each spike time is associated with a single waveform), then the 'waveforms_index' column will have values 1, 2, ..., N, where N is the number of spike events. The number of electrodes for each spike event should be the same within a given unit. The 'electrodes' column should be used to indicate which electrodes are associated with each unit, and the order of the waveforms within a given unit x spike event should be in the same order as the electrodes referenced in the 'electrodes' column of this table. The number of samples for each waveform must be the same.""", @@ -588,7 +607,7 @@ class Units(DynamicTable): }, ) ) - waveforms_index: Named[Optional[VectorIndex]] = Field( + waveforms_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms dataset. One value for every spike event. See 'waveforms' for more detail.""", json_schema_extra={ @@ -600,7 +619,7 @@ class Units(DynamicTable): } }, ) - waveforms_index_index: Named[Optional[VectorIndex]] = Field( + waveforms_index_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms_index dataset. One value for every unit (row in the table). See 'waveforms' for more detail.""", json_schema_extra={ @@ -622,9 +641,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class UnitsSpikeTimes(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ogen.py index 35b5c3c..350a398 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ophys.py index 48afbe4..9f6e191 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -299,7 +315,7 @@ class DfOverF(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -314,7 +330,7 @@ class Fluorescence(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -329,7 +345,7 @@ class ImageSegmentation(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[PlaneSegmentation]] = Field( + value: Optional[Dict[str, PlaneSegmentation]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "PlaneSegmentation"}]}} ) name: str = Field(...) @@ -345,11 +361,18 @@ class PlaneSegmentation(DynamicTable): ) name: str = Field(...) - image_mask: Optional[PlaneSegmentationImageMask] = Field( + image_mask: Optional[ + VectorData[ + Union[ + NDArray[Shape["* num_roi, * num_x, * num_y"], Any], + NDArray[Shape["* num_roi, * num_x, * num_y, * num_z"], Any], + ] + ] + ] = Field( None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -365,7 +388,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -381,7 +404,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - reference_images: Optional[List[ImageSeries]] = Field( + reference_images: Optional[Dict[str, ImageSeries]] = Field( None, description="""Image stacks that the segmentation masks apply to.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImageSeries"}]}}, @@ -405,33 +428,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) - - -class PlaneSegmentationImageMask(VectorData): - """ - ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "core.nwb.ophys"}) - - name: Literal["image_mask"] = Field( - "image_mask", - json_schema_extra={ - "linkml_meta": {"equals_string": "image_mask", "ifabsent": "string(image_mask)"} - }, - ) - description: str = Field(..., description="""Description of what these vectors represent.""") - value: Optional[ - Union[ - NDArray[Shape["* dim0"], Any], - NDArray[Shape["* dim0, * dim1"], Any], - NDArray[Shape["* dim0, * dim1, * dim2"], Any], - NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], - ] - ] = Field(None) class PlaneSegmentationPixelMask(VectorData): @@ -554,7 +550,7 @@ class ImagingPlane(NWBContainer): None, description="""Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = \"Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral).\"""", ) - optical_channel: List[OpticalChannel] = Field( + optical_channel: Dict[str, OpticalChannel] = Field( ..., description="""An optical channel used to record from an imaging plane.""" ) device: Union[Device, str] = Field( @@ -668,7 +664,7 @@ class MotionCorrection(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[CorrectedImageStack]] = Field( + value: Optional[Dict[str, CorrectedImageStack]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "CorrectedImageStack"}]}} ) name: str = Field(...) @@ -710,7 +706,6 @@ DfOverF.model_rebuild() Fluorescence.model_rebuild() ImageSegmentation.model_rebuild() PlaneSegmentation.model_rebuild() -PlaneSegmentationImageMask.model_rebuild() PlaneSegmentationPixelMask.model_rebuild() PlaneSegmentationVoxelMask.model_rebuild() ImagingPlane.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_retinotopy.py index 5973f2e..ffc194e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/namespace.py index dd7a197..620dcf2 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_4_0/namespace.py @@ -130,7 +130,6 @@ from ...core.v2_4_0.core_nwb_ophys import ( MotionCorrection, OpticalChannel, PlaneSegmentation, - PlaneSegmentationImageMask, PlaneSegmentationPixelMask, PlaneSegmentationVoxelMask, RoiResponseSeries, @@ -147,7 +146,7 @@ from ...core.v2_4_0.core_nwb_retinotopy import ( ImagingRetinotopyVasculatureImage, ) from ...hdmf_common.v1_5_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -202,12 +201,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_base.py index 2b9db4d..2db9763 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_base.py @@ -74,12 +74,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -114,7 +130,7 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - def __init__(self, value: Optional[NDArray] = None, **kwargs): + def __init__(self, value: Optional[T] = None, **kwargs): if value is not None and "value" not in kwargs: kwargs["value"] = value super().__init__(**kwargs) @@ -535,7 +551,7 @@ class ProcessingModule(NWBContainer): {"from_schema": "core.nwb.base", "tree_root": True} ) - value: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + value: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} @@ -555,8 +571,8 @@ class Images(NWBDataInterface): name: str = Field("Images", json_schema_extra={"linkml_meta": {"ifabsent": "string(Images)"}}) description: str = Field(..., description="""Description of this collection of images.""") - image: List[Image] = Field(..., description="""Images stored in this collection.""") - order_of_images: Named[Optional[ImageReferences]] = Field( + image: List[str] = Field(..., description="""Images stored in this collection.""") + order_of_images: Optional[Named[ImageReferences]] = Field( None, description="""Ordered dataset of references to Image objects stored in the parent group. Each Image object in the Images group should be stored once and only once, so the dataset should have the same length as the number of images.""", json_schema_extra={ diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_behavior.py index 4eb79e6..89c1038 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -184,7 +200,7 @@ class BehavioralEpochs(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[IntervalSeries]] = Field( + value: Optional[Dict[str, IntervalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "IntervalSeries"}]}} ) name: str = Field(...) @@ -199,7 +215,7 @@ class BehavioralEvents(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -214,7 +230,7 @@ class BehavioralTimeSeries(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -229,7 +245,7 @@ class PupilTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -244,7 +260,7 @@ class EyeTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -259,7 +275,7 @@ class CompassDirection(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -274,7 +290,7 @@ class Position(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_device.py index 60821f8..e4cf279 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ecephys.py index 237a995..91c2222 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -369,7 +385,7 @@ class EventWaveform(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[SpikeEventSeries]] = Field( + value: Optional[Dict[str, SpikeEventSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpikeEventSeries"}]}} ) name: str = Field(...) @@ -384,7 +400,7 @@ class FilteredEphys(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) @@ -399,7 +415,7 @@ class LFP(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_epoch.py index 0f5f803..ab92eb7 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -168,7 +184,7 @@ class TimeIntervals(DynamicTable): } }, ) - timeseries: Named[Optional[TimeSeriesReferenceVectorData]] = Field( + timeseries: Optional[Named[TimeSeriesReferenceVectorData]] = Field( None, description="""An index into a TimeSeries object.""", json_schema_extra={ @@ -180,7 +196,7 @@ class TimeIntervals(DynamicTable): } }, ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -202,9 +218,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_file.py index c47be55..6c056a6 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_file.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -176,28 +192,28 @@ class NWBFile(NWBContainer): ..., description="""Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in \"Z\" with no timezone offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).""", ) - acquisition: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + acquisition: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, description="""Data streams recorded from the system, including ephys, ophys, tracking, etc. This group should be read-only after the experiment is completed and timestamps are corrected to a common timebase. The data stored here may be links to raw data stored in external NWB files. This will allow keeping bulky raw data out of the file while preserving the option of keeping some/all in the file. Acquired data includes tracking and experimental data streams (i.e., everything measured from the system). If bulky data is stored in the /acquisition group, the data can exist in a separate NWB file that is linked to by the file being used for processing and analysis.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} }, ) - analysis: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + analysis: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""Lab-specific and custom scientific analysis of data. There is no defined format for the content of this group - the format is up to the individual user/lab. To facilitate sharing analysis data between labs, the contents here should be stored in standard types (e.g., neurodata_types) and appropriately documented. The file can store lab-specific and custom data analysis without restriction on its form or schema, reducing data formatting restrictions on end users. Such data should be placed in the analysis group. The analysis data should be documented so that it could be shared with other labs.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - scratch: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + scratch: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""A place to store one-off analysis results. Data placed here is not intended for sharing. By placing data here, users acknowledge that there is no guarantee that their data meets any standard.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - processing: Optional[List[ProcessingModule]] = Field( + processing: Optional[Dict[str, ProcessingModule]] = Field( None, description="""The home for ProcessingModules. These modules perform intermediate analysis of data that is necessary to perform before scientific analysis. Examples include spike clustering, extracting position from tracking data, stitching together image slices. ProcessingModules can be large and express many data sets from relatively complex analysis (e.g., spike detection and clustering) or small, representing extraction of position information from tracking video, or even binary lick/no-lick decisions. Common software tools (e.g., klustakwik, MClust) are expected to read/write data here. 'Processing' refers to intermediate analysis of the acquired data to make it more amenable to scientific analysis.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ProcessingModule"}]}}, @@ -230,12 +246,12 @@ class NWBFileStimulus(ConfiguredBaseModel): "linkml_meta": {"equals_string": "stimulus", "ifabsent": "string(stimulus)"} }, ) - presentation: Optional[List[TimeSeries]] = Field( + presentation: Optional[Dict[str, TimeSeries]] = Field( None, description="""Stimuli presented during the experiment.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}}, ) - templates: Optional[List[Union[Images, TimeSeries]]] = Field( + templates: Optional[Dict[str, Union[Images, TimeSeries]]] = Field( None, description="""Template stimuli. Timestamps in templates are based on stimulus design and are relative to the beginning of the stimulus. When templates are used, the stimulus instances must convert presentation times to the experiment`s time reference frame.""", json_schema_extra={ @@ -315,11 +331,11 @@ class NWBFileGeneral(ConfiguredBaseModel): None, description="""Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, etc.""", ) - lab_meta_data: Optional[List[LabMetaData]] = Field( + lab_meta_data: Optional[Dict[str, LabMetaData]] = Field( None, description="""Place-holder than can be extended so that lab-specific meta-data can be placed in /general.""", ) - devices: Optional[List[Device]] = Field( + devices: Optional[Dict[str, Device]] = Field( None, description="""Description of hardware devices used during experiment, e.g., monitors, ADC boards, microscopes, etc.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "Device"}]}}, @@ -334,12 +350,12 @@ class NWBFileGeneral(ConfiguredBaseModel): intracellular_ephys: Optional[GeneralIntracellularEphys] = Field( None, description="""Metadata related to intracellular electrophysiology.""" ) - optogenetics: Optional[List[OptogeneticStimulusSite]] = Field( + optogenetics: Optional[Dict[str, OptogeneticStimulusSite]] = Field( None, description="""Metadata describing optogenetic stimuluation.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "OptogeneticStimulusSite"}]}}, ) - optophysiology: Optional[List[ImagingPlane]] = Field( + optophysiology: Optional[Dict[str, ImagingPlane]] = Field( None, description="""Metadata related to optophysiology.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImagingPlane"}]}}, @@ -379,7 +395,7 @@ class GeneralExtracellularEphys(ConfiguredBaseModel): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( + electrode_group: Optional[Dict[str, ElectrodeGroup]] = Field( None, description="""Physical group of electrodes.""" ) electrodes: Optional[ExtracellularEphysElectrodes] = Field( @@ -400,7 +416,7 @@ class ExtracellularEphysElectrodes(DynamicTable): "linkml_meta": {"equals_string": "electrodes", "ifabsent": "string(electrodes)"} }, ) - x: VectorData[Optional[NDArray[Any, float]]] = Field( + x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate of the channel location in the brain (+x is posterior).""", json_schema_extra={ @@ -409,7 +425,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - y: VectorData[Optional[NDArray[Any, float]]] = Field( + y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate of the channel location in the brain (+y is inferior).""", json_schema_extra={ @@ -418,7 +434,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - z: VectorData[Optional[NDArray[Any, float]]] = Field( + z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate of the channel location in the brain (+z is right).""", json_schema_extra={ @@ -427,7 +443,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - imp: VectorData[Optional[NDArray[Any, float]]] = Field( + imp: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""Impedance of the channel, in ohms.""", json_schema_extra={ @@ -445,7 +461,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - filtering: VectorData[Optional[NDArray[Any, str]]] = Field( + filtering: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of hardware filtering, including the filter name and frequency cutoffs.""", json_schema_extra={ @@ -454,8 +470,14 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - group: List[ElectrodeGroup] = Field( - ..., description="""Reference to the ElectrodeGroup this electrode is a part of.""" + group: VectorData[NDArray[Any, ElectrodeGroup]] = Field( + ..., + description="""Reference to the ElectrodeGroup this electrode is a part of.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) group_name: VectorData[NDArray[Any, str]] = Field( ..., @@ -466,7 +488,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -475,7 +497,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -484,7 +506,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -493,7 +515,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference electrode and/or reference scheme used for this electrode, e.g., \"stainless steel skull screw\" or \"online common average referencing\".""", json_schema_extra={ @@ -512,9 +534,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class GeneralIntracellularEphys(ConfiguredBaseModel): @@ -537,7 +556,7 @@ class GeneralIntracellularEphys(ConfiguredBaseModel): None, description="""[DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute for each TimeSeries.""", ) - intracellular_electrode: Optional[List[IntracellularElectrode]] = Field( + intracellular_electrode: Optional[Dict[str, IntracellularElectrode]] = Field( None, description="""An intracellular electrode.""" ) sweep_table: Optional[SweepTable] = Field( @@ -589,7 +608,7 @@ class NWBFileIntervals(ConfiguredBaseModel): invalid_times: Optional[TimeIntervals] = Field( None, description="""Time intervals that should be removed from analysis.""" ) - time_intervals: Optional[List[TimeIntervals]] = Field( + time_intervals: Optional[Dict[str, TimeIntervals]] = Field( None, description="""Optional additional table(s) for describing other experimental time intervals.""", ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_icephys.py index c4ae637..b500a82 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_icephys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -903,8 +919,14 @@ class SweepTable(DynamicTable): } }, ) - series: List[PatchClampSeries] = Field( - ..., description="""The PatchClampSeries with the sweep number in that row.""" + series: VectorData[NDArray[Any, PatchClampSeries]] = Field( + ..., + description="""The PatchClampSeries with the sweep number in that row.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) series_index: Named[VectorIndex] = Field( ..., @@ -928,9 +950,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularElectrodesTable(DynamicTable): @@ -953,8 +972,14 @@ class IntracellularElectrodesTable(DynamicTable): } }, ) - electrode: List[IntracellularElectrode] = Field( - ..., description="""Column for storing the reference to the intracellular electrode.""" + electrode: VectorData[NDArray[Any, IntracellularElectrode]] = Field( + ..., + description="""Column for storing the reference to the intracellular electrode.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) colnames: List[str] = Field( ..., @@ -965,9 +990,6 @@ class IntracellularElectrodesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularStimuliTable(DynamicTable): @@ -1011,9 +1033,6 @@ class IntracellularStimuliTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularResponsesTable(DynamicTable): @@ -1057,9 +1076,6 @@ class IntracellularResponsesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularRecordingsTable(AlignedDynamicTable): @@ -1111,7 +1127,7 @@ class IntracellularRecordingsTable(AlignedDynamicTable): responses: IntracellularResponsesTable = Field( ..., description="""Table for storing intracellular response related metadata.""" ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) colnames: List[str] = Field( @@ -1123,9 +1139,6 @@ class IntracellularRecordingsTable(AlignedDynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTable(DynamicTable): @@ -1172,9 +1185,6 @@ class SimultaneousRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTableRecordings(DynamicTableRegion): @@ -1260,9 +1270,6 @@ class SequentialRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SequentialRecordingsTableSimultaneousRecordings(DynamicTableRegion): @@ -1339,9 +1346,6 @@ class RepetitionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class RepetitionsTableSequentialRecordings(DynamicTableRegion): @@ -1420,9 +1424,6 @@ class ExperimentalConditionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class ExperimentalConditionsTableRepetitions(DynamicTableRegion): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_image.py index c0b1daf..520d249 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_image.py @@ -56,12 +56,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_misc.py index 293403f..7901288 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -328,7 +344,7 @@ class DecompositionSeries(TimeSeries): ..., description="""Data decomposed into frequency bands.""" ) metric: str = Field(..., description="""The metric used, e.g. phase, amplitude, power.""") - source_channels: Named[Optional[DynamicTableRegion]] = Field( + source_channels: Optional[Named[DynamicTableRegion]] = Field( None, description="""DynamicTableRegion pointer to the channels that this decomposition series was generated from.""", json_schema_extra={ @@ -476,9 +492,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class Units(DynamicTable): @@ -491,7 +504,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -506,7 +519,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -518,7 +531,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -534,7 +547,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -546,7 +559,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -558,26 +571,32 @@ class Units(DynamicTable): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( - None, description="""Electrode group that each spike unit came from.""" + electrode_group: Optional[VectorData[NDArray[Any, ElectrodeGroup]]] = Field( + None, + description="""Electrode group that each spike unit came from.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform standard deviation for each spike unit.""") - waveforms: VectorData[Optional[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( + waveforms: Optional[VectorData[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( Field( None, description="""Individual waveforms for each spike on each electrode. This is a doubly indexed column. The 'waveforms_index' column indexes which waveforms in this column belong to the same spike event for a given unit, where each waveform was recorded from a different electrode. The 'waveforms_index_index' column indexes the 'waveforms_index' column to indicate which spike events belong to a given unit. For example, if the 'waveforms_index_index' column has values [2, 5, 6], then the first 2 elements of the 'waveforms_index' column correspond to the 2 spike events of the first unit, the next 3 elements of the 'waveforms_index' column correspond to the 3 spike events of the second unit, and the next 1 element of the 'waveforms_index' column corresponds to the 1 spike event of the third unit. If the 'waveforms_index' column has values [3, 6, 8, 10, 12, 13], then the first 3 elements of the 'waveforms' column contain the 3 spike waveforms that were recorded from 3 different electrodes for the first spike time of the first unit. See https://nwb-schema.readthedocs.io/en/stable/format_description.html#doubly-ragged-arrays for a graphical representation of this example. When there is only one electrode for each unit (i.e., each spike time is associated with a single waveform), then the 'waveforms_index' column will have values 1, 2, ..., N, where N is the number of spike events. The number of electrodes for each spike event should be the same within a given unit. The 'electrodes' column should be used to indicate which electrodes are associated with each unit, and the order of the waveforms within a given unit x spike event should be in the same order as the electrodes referenced in the 'electrodes' column of this table. The number of samples for each waveform must be the same.""", @@ -588,7 +607,7 @@ class Units(DynamicTable): }, ) ) - waveforms_index: Named[Optional[VectorIndex]] = Field( + waveforms_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms dataset. One value for every spike event. See 'waveforms' for more detail.""", json_schema_extra={ @@ -600,7 +619,7 @@ class Units(DynamicTable): } }, ) - waveforms_index_index: Named[Optional[VectorIndex]] = Field( + waveforms_index_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms_index dataset. One value for every unit (row in the table). See 'waveforms' for more detail.""", json_schema_extra={ @@ -622,9 +641,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class UnitsSpikeTimes(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ogen.py index b26a59d..e39e6b2 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ophys.py index c72f71d..6107f4c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -299,7 +315,7 @@ class DfOverF(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -314,7 +330,7 @@ class Fluorescence(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -329,7 +345,7 @@ class ImageSegmentation(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[PlaneSegmentation]] = Field( + value: Optional[Dict[str, PlaneSegmentation]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "PlaneSegmentation"}]}} ) name: str = Field(...) @@ -345,11 +361,18 @@ class PlaneSegmentation(DynamicTable): ) name: str = Field(...) - image_mask: Optional[PlaneSegmentationImageMask] = Field( + image_mask: Optional[ + VectorData[ + Union[ + NDArray[Shape["* num_roi, * num_x, * num_y"], Any], + NDArray[Shape["* num_roi, * num_x, * num_y, * num_z"], Any], + ] + ] + ] = Field( None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -365,7 +388,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -381,7 +404,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - reference_images: Optional[List[ImageSeries]] = Field( + reference_images: Optional[Dict[str, ImageSeries]] = Field( None, description="""Image stacks that the segmentation masks apply to.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImageSeries"}]}}, @@ -405,33 +428,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) - - -class PlaneSegmentationImageMask(VectorData): - """ - ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "core.nwb.ophys"}) - - name: Literal["image_mask"] = Field( - "image_mask", - json_schema_extra={ - "linkml_meta": {"equals_string": "image_mask", "ifabsent": "string(image_mask)"} - }, - ) - description: str = Field(..., description="""Description of what these vectors represent.""") - value: Optional[ - Union[ - NDArray[Shape["* dim0"], Any], - NDArray[Shape["* dim0, * dim1"], Any], - NDArray[Shape["* dim0, * dim1, * dim2"], Any], - NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], - ] - ] = Field(None) class PlaneSegmentationPixelMask(VectorData): @@ -554,7 +550,7 @@ class ImagingPlane(NWBContainer): None, description="""Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = \"Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral).\"""", ) - optical_channel: List[OpticalChannel] = Field( + optical_channel: Dict[str, OpticalChannel] = Field( ..., description="""An optical channel used to record from an imaging plane.""" ) device: Union[Device, str] = Field( @@ -668,7 +664,7 @@ class MotionCorrection(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[CorrectedImageStack]] = Field( + value: Optional[Dict[str, CorrectedImageStack]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "CorrectedImageStack"}]}} ) name: str = Field(...) @@ -710,7 +706,6 @@ DfOverF.model_rebuild() Fluorescence.model_rebuild() ImageSegmentation.model_rebuild() PlaneSegmentation.model_rebuild() -PlaneSegmentationImageMask.model_rebuild() PlaneSegmentationPixelMask.model_rebuild() PlaneSegmentationVoxelMask.model_rebuild() ImagingPlane.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_retinotopy.py index c9c3d85..b72f7b4 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/namespace.py index fd08f03..7aaa8a2 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_5_0/namespace.py @@ -131,7 +131,6 @@ from ...core.v2_5_0.core_nwb_ophys import ( MotionCorrection, OpticalChannel, PlaneSegmentation, - PlaneSegmentationImageMask, PlaneSegmentationPixelMask, PlaneSegmentationVoxelMask, RoiResponseSeries, @@ -148,7 +147,7 @@ from ...core.v2_5_0.core_nwb_retinotopy import ( ImagingRetinotopyVasculatureImage, ) from ...hdmf_common.v1_5_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -203,12 +202,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_base.py index f0f66dd..4837ae7 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_base.py @@ -74,12 +74,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -114,7 +130,7 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - def __init__(self, value: Optional[NDArray] = None, **kwargs): + def __init__(self, value: Optional[T] = None, **kwargs): if value is not None and "value" not in kwargs: kwargs["value"] = value super().__init__(**kwargs) @@ -535,7 +551,7 @@ class ProcessingModule(NWBContainer): {"from_schema": "core.nwb.base", "tree_root": True} ) - value: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + value: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} @@ -555,8 +571,8 @@ class Images(NWBDataInterface): name: str = Field("Images", json_schema_extra={"linkml_meta": {"ifabsent": "string(Images)"}}) description: str = Field(..., description="""Description of this collection of images.""") - image: List[Image] = Field(..., description="""Images stored in this collection.""") - order_of_images: Named[Optional[ImageReferences]] = Field( + image: List[str] = Field(..., description="""Images stored in this collection.""") + order_of_images: Optional[Named[ImageReferences]] = Field( None, description="""Ordered dataset of references to Image objects stored in the parent group. Each Image object in the Images group should be stored once and only once, so the dataset should have the same length as the number of images.""", json_schema_extra={ diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_behavior.py index 05cece8..7e4ad59 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -184,7 +200,7 @@ class BehavioralEpochs(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[IntervalSeries]] = Field( + value: Optional[Dict[str, IntervalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "IntervalSeries"}]}} ) name: str = Field(...) @@ -199,7 +215,7 @@ class BehavioralEvents(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -214,7 +230,7 @@ class BehavioralTimeSeries(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -229,7 +245,7 @@ class PupilTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -244,7 +260,7 @@ class EyeTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -259,7 +275,7 @@ class CompassDirection(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -274,7 +290,7 @@ class Position(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_device.py index d05fe5d..c57186b 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ecephys.py index e8d05f3..0529035 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -369,7 +385,7 @@ class EventWaveform(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[SpikeEventSeries]] = Field( + value: Optional[Dict[str, SpikeEventSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpikeEventSeries"}]}} ) name: str = Field(...) @@ -384,7 +400,7 @@ class FilteredEphys(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) @@ -399,7 +415,7 @@ class LFP(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_epoch.py index 82e2b83..d3fa53b 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -147,7 +163,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags: VectorData[Optional[NDArray[Any, str]]] = Field( + tags: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""User-defined tags that identify or categorize events.""", json_schema_extra={ @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -168,7 +184,7 @@ class TimeIntervals(DynamicTable): } }, ) - timeseries: Named[Optional[TimeSeriesReferenceVectorData]] = Field( + timeseries: Optional[Named[TimeSeriesReferenceVectorData]] = Field( None, description="""An index into a TimeSeries object.""", json_schema_extra={ @@ -180,7 +196,7 @@ class TimeIntervals(DynamicTable): } }, ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -202,9 +218,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_file.py index 5e58bec..975e51c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_file.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -176,28 +192,28 @@ class NWBFile(NWBContainer): ..., description="""Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in \"Z\" with no timezone offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).""", ) - acquisition: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + acquisition: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, description="""Data streams recorded from the system, including ephys, ophys, tracking, etc. This group should be read-only after the experiment is completed and timestamps are corrected to a common timebase. The data stored here may be links to raw data stored in external NWB files. This will allow keeping bulky raw data out of the file while preserving the option of keeping some/all in the file. Acquired data includes tracking and experimental data streams (i.e., everything measured from the system). If bulky data is stored in the /acquisition group, the data can exist in a separate NWB file that is linked to by the file being used for processing and analysis.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} }, ) - analysis: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + analysis: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""Lab-specific and custom scientific analysis of data. There is no defined format for the content of this group - the format is up to the individual user/lab. To facilitate sharing analysis data between labs, the contents here should be stored in standard types (e.g., neurodata_types) and appropriately documented. The file can store lab-specific and custom data analysis without restriction on its form or schema, reducing data formatting restrictions on end users. Such data should be placed in the analysis group. The analysis data should be documented so that it could be shared with other labs.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - scratch: Optional[List[Union[DynamicTable, NWBContainer]]] = Field( + scratch: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""A place to store one-off analysis results. Data placed here is not intended for sharing. By placing data here, users acknowledge that there is no guarantee that their data meets any standard.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - processing: Optional[List[ProcessingModule]] = Field( + processing: Optional[Dict[str, ProcessingModule]] = Field( None, description="""The home for ProcessingModules. These modules perform intermediate analysis of data that is necessary to perform before scientific analysis. Examples include spike clustering, extracting position from tracking data, stitching together image slices. ProcessingModules can be large and express many data sets from relatively complex analysis (e.g., spike detection and clustering) or small, representing extraction of position information from tracking video, or even binary lick/no-lick decisions. Common software tools (e.g., klustakwik, MClust) are expected to read/write data here. 'Processing' refers to intermediate analysis of the acquired data to make it more amenable to scientific analysis.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ProcessingModule"}]}}, @@ -230,12 +246,12 @@ class NWBFileStimulus(ConfiguredBaseModel): "linkml_meta": {"equals_string": "stimulus", "ifabsent": "string(stimulus)"} }, ) - presentation: Optional[List[TimeSeries]] = Field( + presentation: Optional[Dict[str, TimeSeries]] = Field( None, description="""Stimuli presented during the experiment.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}}, ) - templates: Optional[List[Union[Images, TimeSeries]]] = Field( + templates: Optional[Dict[str, Union[Images, TimeSeries]]] = Field( None, description="""Template stimuli. Timestamps in templates are based on stimulus design and are relative to the beginning of the stimulus. When templates are used, the stimulus instances must convert presentation times to the experiment`s time reference frame.""", json_schema_extra={ @@ -315,11 +331,11 @@ class NWBFileGeneral(ConfiguredBaseModel): None, description="""Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, etc.""", ) - lab_meta_data: Optional[List[LabMetaData]] = Field( + lab_meta_data: Optional[Dict[str, LabMetaData]] = Field( None, description="""Place-holder than can be extended so that lab-specific meta-data can be placed in /general.""", ) - devices: Optional[List[Device]] = Field( + devices: Optional[Dict[str, Device]] = Field( None, description="""Description of hardware devices used during experiment, e.g., monitors, ADC boards, microscopes, etc.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "Device"}]}}, @@ -334,12 +350,12 @@ class NWBFileGeneral(ConfiguredBaseModel): intracellular_ephys: Optional[GeneralIntracellularEphys] = Field( None, description="""Metadata related to intracellular electrophysiology.""" ) - optogenetics: Optional[List[OptogeneticStimulusSite]] = Field( + optogenetics: Optional[Dict[str, OptogeneticStimulusSite]] = Field( None, description="""Metadata describing optogenetic stimuluation.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "OptogeneticStimulusSite"}]}}, ) - optophysiology: Optional[List[ImagingPlane]] = Field( + optophysiology: Optional[Dict[str, ImagingPlane]] = Field( None, description="""Metadata related to optophysiology.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImagingPlane"}]}}, @@ -379,7 +395,7 @@ class GeneralExtracellularEphys(ConfiguredBaseModel): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( + electrode_group: Optional[Dict[str, ElectrodeGroup]] = Field( None, description="""Physical group of electrodes.""" ) electrodes: Optional[ExtracellularEphysElectrodes] = Field( @@ -400,7 +416,7 @@ class ExtracellularEphysElectrodes(DynamicTable): "linkml_meta": {"equals_string": "electrodes", "ifabsent": "string(electrodes)"} }, ) - x: VectorData[Optional[NDArray[Any, float]]] = Field( + x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate of the channel location in the brain (+x is posterior).""", json_schema_extra={ @@ -409,7 +425,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - y: VectorData[Optional[NDArray[Any, float]]] = Field( + y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate of the channel location in the brain (+y is inferior).""", json_schema_extra={ @@ -418,7 +434,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - z: VectorData[Optional[NDArray[Any, float]]] = Field( + z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate of the channel location in the brain (+z is right).""", json_schema_extra={ @@ -427,7 +443,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - imp: VectorData[Optional[NDArray[Any, float]]] = Field( + imp: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""Impedance of the channel, in ohms.""", json_schema_extra={ @@ -445,7 +461,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - filtering: VectorData[Optional[NDArray[Any, str]]] = Field( + filtering: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of hardware filtering, including the filter name and frequency cutoffs.""", json_schema_extra={ @@ -454,8 +470,14 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - group: List[ElectrodeGroup] = Field( - ..., description="""Reference to the ElectrodeGroup this electrode is a part of.""" + group: VectorData[NDArray[Any, ElectrodeGroup]] = Field( + ..., + description="""Reference to the ElectrodeGroup this electrode is a part of.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) group_name: VectorData[NDArray[Any, str]] = Field( ..., @@ -466,7 +488,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_x: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_x: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""x coordinate in electrode group""", json_schema_extra={ @@ -475,7 +497,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_y: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_y: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""y coordinate in electrode group""", json_schema_extra={ @@ -484,7 +506,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - rel_z: VectorData[Optional[NDArray[Any, float]]] = Field( + rel_z: Optional[VectorData[NDArray[Any, float]]] = Field( None, description="""z coordinate in electrode group""", json_schema_extra={ @@ -493,7 +515,7 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - reference: VectorData[Optional[NDArray[Any, str]]] = Field( + reference: Optional[VectorData[NDArray[Any, str]]] = Field( None, description="""Description of the reference electrode and/or reference scheme used for this electrode, e.g., \"stainless steel skull screw\" or \"online common average referencing\".""", json_schema_extra={ @@ -512,9 +534,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class GeneralIntracellularEphys(ConfiguredBaseModel): @@ -537,7 +556,7 @@ class GeneralIntracellularEphys(ConfiguredBaseModel): None, description="""[DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute for each TimeSeries.""", ) - intracellular_electrode: Optional[List[IntracellularElectrode]] = Field( + intracellular_electrode: Optional[Dict[str, IntracellularElectrode]] = Field( None, description="""An intracellular electrode.""" ) sweep_table: Optional[SweepTable] = Field( @@ -589,7 +608,7 @@ class NWBFileIntervals(ConfiguredBaseModel): invalid_times: Optional[TimeIntervals] = Field( None, description="""Time intervals that should be removed from analysis.""" ) - time_intervals: Optional[List[TimeIntervals]] = Field( + time_intervals: Optional[Dict[str, TimeIntervals]] = Field( None, description="""Optional additional table(s) for describing other experimental time intervals.""", ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_icephys.py index 55d4c8f..1f9c04b 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_icephys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -903,8 +919,14 @@ class SweepTable(DynamicTable): } }, ) - series: List[PatchClampSeries] = Field( - ..., description="""The PatchClampSeries with the sweep number in that row.""" + series: VectorData[NDArray[Any, PatchClampSeries]] = Field( + ..., + description="""The PatchClampSeries with the sweep number in that row.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) series_index: Named[VectorIndex] = Field( ..., @@ -928,9 +950,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularElectrodesTable(DynamicTable): @@ -953,8 +972,14 @@ class IntracellularElectrodesTable(DynamicTable): } }, ) - electrode: List[IntracellularElectrode] = Field( - ..., description="""Column for storing the reference to the intracellular electrode.""" + electrode: VectorData[NDArray[Any, IntracellularElectrode]] = Field( + ..., + description="""Column for storing the reference to the intracellular electrode.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) colnames: List[str] = Field( ..., @@ -965,9 +990,6 @@ class IntracellularElectrodesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularStimuliTable(DynamicTable): @@ -1011,9 +1033,6 @@ class IntracellularStimuliTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularResponsesTable(DynamicTable): @@ -1057,9 +1076,6 @@ class IntracellularResponsesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularRecordingsTable(AlignedDynamicTable): @@ -1111,7 +1127,7 @@ class IntracellularRecordingsTable(AlignedDynamicTable): responses: IntracellularResponsesTable = Field( ..., description="""Table for storing intracellular response related metadata.""" ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) colnames: List[str] = Field( @@ -1123,9 +1139,6 @@ class IntracellularRecordingsTable(AlignedDynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTable(DynamicTable): @@ -1172,9 +1185,6 @@ class SimultaneousRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTableRecordings(DynamicTableRegion): @@ -1260,9 +1270,6 @@ class SequentialRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SequentialRecordingsTableSimultaneousRecordings(DynamicTableRegion): @@ -1339,9 +1346,6 @@ class RepetitionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class RepetitionsTableSequentialRecordings(DynamicTableRegion): @@ -1420,9 +1424,6 @@ class ExperimentalConditionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class ExperimentalConditionsTableRepetitions(DynamicTableRegion): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_image.py index cfe8a10..af69abe 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_image.py @@ -56,12 +56,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_misc.py index 94272fc..5c28736 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -328,7 +344,7 @@ class DecompositionSeries(TimeSeries): ..., description="""Data decomposed into frequency bands.""" ) metric: str = Field(..., description="""The metric used, e.g. phase, amplitude, power.""") - source_channels: Named[Optional[DynamicTableRegion]] = Field( + source_channels: Optional[Named[DynamicTableRegion]] = Field( None, description="""DynamicTableRegion pointer to the channels that this decomposition series was generated from.""", json_schema_extra={ @@ -476,9 +492,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class Units(DynamicTable): @@ -491,7 +504,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -506,7 +519,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit in seconds.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -518,7 +531,7 @@ class Units(DynamicTable): } }, ) - obs_intervals: VectorData[Optional[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( + obs_intervals: Optional[VectorData[NDArray[Shape["* num_intervals, 2 start_end"], float]]] = ( Field( None, description="""Observation intervals for each unit.""", @@ -534,7 +547,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -546,7 +559,7 @@ class Units(DynamicTable): } }, ) - electrodes: Named[Optional[DynamicTableRegion]] = Field( + electrodes: Optional[Named[DynamicTableRegion]] = Field( None, description="""Electrode that each spike unit came from, specified using a DynamicTableRegion.""", json_schema_extra={ @@ -558,26 +571,32 @@ class Units(DynamicTable): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( - None, description="""Electrode group that each spike unit came from.""" + electrode_group: Optional[VectorData[NDArray[Any, ElectrodeGroup]]] = Field( + None, + description="""Electrode group that each spike unit came from.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) - waveform_mean: VectorData[ - Optional[ + waveform_mean: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform mean for each spike unit.""") - waveform_sd: VectorData[ - Optional[ + waveform_sd: Optional[ + VectorData[ Union[ NDArray[Shape["* num_units, * num_samples"], float], NDArray[Shape["* num_units, * num_samples, * num_electrodes"], float], ] ] ] = Field(None, description="""Spike waveform standard deviation for each spike unit.""") - waveforms: VectorData[Optional[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( + waveforms: Optional[VectorData[NDArray[Shape["* num_waveforms, * num_samples"], float]]] = ( Field( None, description="""Individual waveforms for each spike on each electrode. This is a doubly indexed column. The 'waveforms_index' column indexes which waveforms in this column belong to the same spike event for a given unit, where each waveform was recorded from a different electrode. The 'waveforms_index_index' column indexes the 'waveforms_index' column to indicate which spike events belong to a given unit. For example, if the 'waveforms_index_index' column has values [2, 5, 6], then the first 2 elements of the 'waveforms_index' column correspond to the 2 spike events of the first unit, the next 3 elements of the 'waveforms_index' column correspond to the 3 spike events of the second unit, and the next 1 element of the 'waveforms_index' column corresponds to the 1 spike event of the third unit. If the 'waveforms_index' column has values [3, 6, 8, 10, 12, 13], then the first 3 elements of the 'waveforms' column contain the 3 spike waveforms that were recorded from 3 different electrodes for the first spike time of the first unit. See https://nwb-schema.readthedocs.io/en/stable/format_description.html#doubly-ragged-arrays for a graphical representation of this example. When there is only one electrode for each unit (i.e., each spike time is associated with a single waveform), then the 'waveforms_index' column will have values 1, 2, ..., N, where N is the number of spike events. The number of electrodes for each spike event should be the same within a given unit. The 'electrodes' column should be used to indicate which electrodes are associated with each unit, and the order of the waveforms within a given unit x spike event should be in the same order as the electrodes referenced in the 'electrodes' column of this table. The number of samples for each waveform must be the same.""", @@ -588,7 +607,7 @@ class Units(DynamicTable): }, ) ) - waveforms_index: Named[Optional[VectorIndex]] = Field( + waveforms_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms dataset. One value for every spike event. See 'waveforms' for more detail.""", json_schema_extra={ @@ -600,7 +619,7 @@ class Units(DynamicTable): } }, ) - waveforms_index_index: Named[Optional[VectorIndex]] = Field( + waveforms_index_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms_index dataset. One value for every unit (row in the table). See 'waveforms' for more detail.""", json_schema_extra={ @@ -622,9 +641,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class UnitsSpikeTimes(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ogen.py index 2a6b4e7..42fe82f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ophys.py index f2cfdd4..f6acd6c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -398,7 +414,7 @@ class DfOverF(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -413,7 +429,7 @@ class Fluorescence(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -428,7 +444,7 @@ class ImageSegmentation(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[PlaneSegmentation]] = Field( + value: Optional[Dict[str, PlaneSegmentation]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "PlaneSegmentation"}]}} ) name: str = Field(...) @@ -444,11 +460,18 @@ class PlaneSegmentation(DynamicTable): ) name: str = Field(...) - image_mask: Optional[PlaneSegmentationImageMask] = Field( + image_mask: Optional[ + VectorData[ + Union[ + NDArray[Shape["* num_roi, * num_x, * num_y"], Any], + NDArray[Shape["* num_roi, * num_x, * num_y, * num_z"], Any], + ] + ] + ] = Field( None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -464,7 +487,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -480,7 +503,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - reference_images: Optional[List[ImageSeries]] = Field( + reference_images: Optional[Dict[str, ImageSeries]] = Field( None, description="""Image stacks that the segmentation masks apply to.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImageSeries"}]}}, @@ -504,33 +527,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) - - -class PlaneSegmentationImageMask(VectorData): - """ - ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "core.nwb.ophys"}) - - name: Literal["image_mask"] = Field( - "image_mask", - json_schema_extra={ - "linkml_meta": {"equals_string": "image_mask", "ifabsent": "string(image_mask)"} - }, - ) - description: str = Field(..., description="""Description of what these vectors represent.""") - value: Optional[ - Union[ - NDArray[Shape["* dim0"], Any], - NDArray[Shape["* dim0, * dim1"], Any], - NDArray[Shape["* dim0, * dim1, * dim2"], Any], - NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], - ] - ] = Field(None) class PlaneSegmentationPixelMask(VectorData): @@ -653,7 +649,7 @@ class ImagingPlane(NWBContainer): None, description="""Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = \"Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral).\"""", ) - optical_channel: List[OpticalChannel] = Field( + optical_channel: Dict[str, OpticalChannel] = Field( ..., description="""An optical channel used to record from an imaging plane.""" ) device: Union[Device, str] = Field( @@ -767,7 +763,7 @@ class MotionCorrection(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[CorrectedImageStack]] = Field( + value: Optional[Dict[str, CorrectedImageStack]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "CorrectedImageStack"}]}} ) name: str = Field(...) @@ -810,7 +806,6 @@ DfOverF.model_rebuild() Fluorescence.model_rebuild() ImageSegmentation.model_rebuild() PlaneSegmentation.model_rebuild() -PlaneSegmentationImageMask.model_rebuild() PlaneSegmentationPixelMask.model_rebuild() PlaneSegmentationVoxelMask.model_rebuild() ImagingPlane.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_retinotopy.py index 404fbda..3a085f7 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/namespace.py index 5d523c9..21b7046 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_6_0_alpha/namespace.py @@ -133,7 +133,6 @@ from ...core.v2_6_0_alpha.core_nwb_ophys import ( OnePhotonSeries, OpticalChannel, PlaneSegmentation, - PlaneSegmentationImageMask, PlaneSegmentationPixelMask, PlaneSegmentationVoxelMask, RoiResponseSeries, @@ -150,7 +149,7 @@ from ...core.v2_6_0_alpha.core_nwb_retinotopy import ( ImagingRetinotopyVasculatureImage, ) from ...hdmf_common.v1_5_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -205,12 +204,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_base.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_base.py index d58b849..a645a2f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_base.py @@ -74,12 +74,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -114,7 +130,7 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - def __init__(self, value: Optional[NDArray] = None, **kwargs): + def __init__(self, value: Optional[T] = None, **kwargs): if value is not None and "value" not in kwargs: kwargs["value"] = value super().__init__(**kwargs) @@ -535,7 +551,7 @@ class ProcessingModule(NWBContainer): {"from_schema": "core.nwb.base", "tree_root": True} ) - value: Optional[List[Union[DynamicTable, NWBDataInterface]]] = Field( + value: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} @@ -555,8 +571,8 @@ class Images(NWBDataInterface): name: str = Field("Images", json_schema_extra={"linkml_meta": {"ifabsent": "string(Images)"}}) description: str = Field(..., description="""Description of this collection of images.""") - image: List[Image] = Field(..., description="""Images stored in this collection.""") - order_of_images: Named[Optional[ImageReferences]] = Field( + image: List[str] = Field(..., description="""Images stored in this collection.""") + order_of_images: Optional[Named[ImageReferences]] = Field( None, description="""Ordered dataset of references to Image objects stored in the parent group. Each Image object in the Images group should be stored once and only once, so the dataset should have the same length as the number of images.""", json_schema_extra={ diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_behavior.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_behavior.py index 1e1712a..836c2e2 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_behavior.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_behavior.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -184,7 +200,7 @@ class BehavioralEpochs(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[IntervalSeries]] = Field( + value: Optional[Dict[str, IntervalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "IntervalSeries"}]}} ) name: str = Field(...) @@ -199,7 +215,7 @@ class BehavioralEvents(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -214,7 +230,7 @@ class BehavioralTimeSeries(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -229,7 +245,7 @@ class PupilTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[TimeSeries]] = Field( + value: Optional[Dict[str, TimeSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "TimeSeries"}]}} ) name: str = Field(...) @@ -244,7 +260,7 @@ class EyeTracking(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -259,7 +275,7 @@ class CompassDirection(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) @@ -274,7 +290,7 @@ class Position(NWBDataInterface): {"from_schema": "core.nwb.behavior", "tree_root": True} ) - value: Optional[List[SpatialSeries]] = Field( + value: Optional[Dict[str, SpatialSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpatialSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_device.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_device.py index 0209d51..59b53c8 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_device.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_device.py @@ -48,12 +48,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ecephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ecephys.py index 9d1ddde..dc96a98 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ecephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ecephys.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -369,7 +385,7 @@ class EventWaveform(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[SpikeEventSeries]] = Field( + value: Optional[Dict[str, SpikeEventSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "SpikeEventSeries"}]}} ) name: str = Field(...) @@ -384,7 +400,7 @@ class FilteredEphys(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) @@ -399,7 +415,7 @@ class LFP(NWBDataInterface): {"from_schema": "core.nwb.ecephys", "tree_root": True} ) - value: Optional[List[ElectricalSeries]] = Field( + value: Optional[Dict[str, ElectricalSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "ElectricalSeries"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_epoch.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_epoch.py index cd1875d..e8b5539 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_epoch.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_epoch.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -156,7 +172,7 @@ class TimeIntervals(DynamicTable): } }, ) - tags_index: Named[Optional[VectorIndex]] = Field( + tags_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for tags.""", json_schema_extra={ @@ -180,7 +196,7 @@ class TimeIntervals(DynamicTable): } }, ) - timeseries_index: Named[Optional[VectorIndex]] = Field( + timeseries_index: Optional[Named[VectorIndex]] = Field( None, description="""Index for timeseries.""", json_schema_extra={ @@ -202,9 +218,6 @@ class TimeIntervals(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_file.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_file.py index 1a9b46e..038a4ae 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_file.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_file.py @@ -1,6 +1,5 @@ from __future__ import annotations -import pdb import re import sys from datetime import date, datetime, time @@ -77,7 +76,7 @@ class ConfiguredBaseModel(BaseModel): except AttributeError: try: return handler(v["value"]) - except (KeyError, TypeError): + except (IndexError, KeyError, TypeError): raise e1 @field_validator("*", mode="before") @@ -86,10 +85,14 @@ class ConfiguredBaseModel(BaseModel): """Recast parent classes into child classes""" if isinstance(v, BaseModel): annotation = cls.model_fields[info.field_name].annotation - annotation = annotation.__args__[0] if hasattr(annotation, "__args__") else annotation - # pdb.set_trace() - if issubclass(annotation, type(v)) and annotation is not type(v): - v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass return v @@ -189,28 +192,28 @@ class NWBFile(NWBContainer): ..., description="""Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in \"Z\" with no timezone offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).""", ) - acquisition: Optional[dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( + acquisition: Optional[Dict[str, Union[DynamicTable, NWBDataInterface]]] = Field( None, description="""Data streams recorded from the system, including ephys, ophys, tracking, etc. This group should be read-only after the experiment is completed and timestamps are corrected to a common timebase. The data stored here may be links to raw data stored in external NWB files. This will allow keeping bulky raw data out of the file while preserving the option of keeping some/all in the file. Acquired data includes tracking and experimental data streams (i.e., everything measured from the system). If bulky data is stored in the /acquisition group, the data can exist in a separate NWB file that is linked to by the file being used for processing and analysis.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBDataInterface"}, {"range": "DynamicTable"}]} }, ) - analysis: Optional[dict[str, Union[DynamicTable, NWBContainer]]] = Field( + analysis: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""Lab-specific and custom scientific analysis of data. There is no defined format for the content of this group - the format is up to the individual user/lab. To facilitate sharing analysis data between labs, the contents here should be stored in standard types (e.g., neurodata_types) and appropriately documented. The file can store lab-specific and custom data analysis without restriction on its form or schema, reducing data formatting restrictions on end users. Such data should be placed in the analysis group. The analysis data should be documented so that it could be shared with other labs.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - scratch: Optional[dict[str, Union[DynamicTable, NWBContainer]]] = Field( + scratch: Optional[Dict[str, Union[DynamicTable, NWBContainer]]] = Field( None, description="""A place to store one-off analysis results. Data placed here is not intended for sharing. By placing data here, users acknowledge that there is no guarantee that their data meets any standard.""", json_schema_extra={ "linkml_meta": {"any_of": [{"range": "NWBContainer"}, {"range": "DynamicTable"}]} }, ) - processing: Optional[dict[str, ProcessingModule]] = Field( + processing: Optional[Dict[str, ProcessingModule]] = Field( None, description="""The home for ProcessingModules. These modules perform intermediate analysis of data that is necessary to perform before scientific analysis. Examples include spike clustering, extracting position from tracking data, stitching together image slices. ProcessingModules can be large and express many data sets from relatively complex analysis (e.g., spike detection and clustering) or small, representing extraction of position information from tracking video, or even binary lick/no-lick decisions. Common software tools (e.g., klustakwik, MClust) are expected to read/write data here. 'Processing' refers to intermediate analysis of the acquired data to make it more amenable to scientific analysis.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ProcessingModule"}]}}, @@ -243,7 +246,7 @@ class NWBFileStimulus(ConfiguredBaseModel): "linkml_meta": {"equals_string": "stimulus", "ifabsent": "string(stimulus)"} }, ) - presentation: Optional[dict[str, Union[DynamicTable, NWBDataInterface, TimeSeries]]] = Field( + presentation: Optional[Dict[str, Union[DynamicTable, NWBDataInterface, TimeSeries]]] = Field( None, description="""Stimuli presented during the experiment.""", json_schema_extra={ @@ -256,7 +259,7 @@ class NWBFileStimulus(ConfiguredBaseModel): } }, ) - templates: Optional[dict[str, Union[Images, TimeSeries]]] = Field( + templates: Optional[Dict[str, Union[Images, TimeSeries]]] = Field( None, description="""Template stimuli. Timestamps in templates are based on stimulus design and are relative to the beginning of the stimulus. When templates are used, the stimulus instances must convert presentation times to the experiment`s time reference frame.""", json_schema_extra={ @@ -336,11 +339,11 @@ class NWBFileGeneral(ConfiguredBaseModel): None, description="""Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, etc.""", ) - lab_meta_data: Optional[List[LabMetaData]] = Field( + lab_meta_data: Optional[Dict[str, LabMetaData]] = Field( None, description="""Place-holder than can be extended so that lab-specific meta-data can be placed in /general.""", ) - devices: Optional[dict[str, Device]] = Field( + devices: Optional[Dict[str, Device]] = Field( None, description="""Description of hardware devices used during experiment, e.g., monitors, ADC boards, microscopes, etc.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "Device"}]}}, @@ -355,12 +358,12 @@ class NWBFileGeneral(ConfiguredBaseModel): intracellular_ephys: Optional[GeneralIntracellularEphys] = Field( None, description="""Metadata related to intracellular electrophysiology.""" ) - optogenetics: Optional[List[OptogeneticStimulusSite]] = Field( + optogenetics: Optional[Dict[str, OptogeneticStimulusSite]] = Field( None, description="""Metadata describing optogenetic stimuluation.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "OptogeneticStimulusSite"}]}}, ) - optophysiology: Optional[List[ImagingPlane]] = Field( + optophysiology: Optional[Dict[str, ImagingPlane]] = Field( None, description="""Metadata related to optophysiology.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImagingPlane"}]}}, @@ -400,7 +403,7 @@ class GeneralExtracellularEphys(ConfiguredBaseModel): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( + electrode_group: Optional[Dict[str, ElectrodeGroup]] = Field( None, description="""Physical group of electrodes.""" ) electrodes: Optional[ExtracellularEphysElectrodes] = Field( @@ -475,8 +478,14 @@ class ExtracellularEphysElectrodes(DynamicTable): } }, ) - group: List[ElectrodeGroup] = Field( - ..., description="""Reference to the ElectrodeGroup this electrode is a part of.""" + group: VectorData[NDArray[Any, ElectrodeGroup]] = Field( + ..., + description="""Reference to the ElectrodeGroup this electrode is a part of.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) group_name: VectorData[NDArray[Any, str]] = Field( ..., @@ -533,9 +542,6 @@ class ExtracellularEphysElectrodes(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class GeneralIntracellularEphys(ConfiguredBaseModel): @@ -558,7 +564,7 @@ class GeneralIntracellularEphys(ConfiguredBaseModel): None, description="""[DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute for each TimeSeries.""", ) - intracellular_electrode: Optional[List[IntracellularElectrode]] = Field( + intracellular_electrode: Optional[Dict[str, IntracellularElectrode]] = Field( None, description="""An intracellular electrode.""" ) sweep_table: Optional[SweepTable] = Field( @@ -610,7 +616,7 @@ class NWBFileIntervals(ConfiguredBaseModel): invalid_times: Optional[TimeIntervals] = Field( None, description="""Time intervals that should be removed from analysis.""" ) - time_intervals: Optional[List[TimeIntervals]] = Field( + time_intervals: Optional[Dict[str, TimeIntervals]] = Field( None, description="""Optional additional table(s) for describing other experimental time intervals.""", ) diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_icephys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_icephys.py index f1a0a50..c1818b4 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_icephys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_icephys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -903,8 +919,14 @@ class SweepTable(DynamicTable): } }, ) - series: List[PatchClampSeries] = Field( - ..., description="""The PatchClampSeries with the sweep number in that row.""" + series: VectorData[NDArray[Any, PatchClampSeries]] = Field( + ..., + description="""The PatchClampSeries with the sweep number in that row.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) series_index: Named[VectorIndex] = Field( ..., @@ -928,9 +950,6 @@ class SweepTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularElectrodesTable(DynamicTable): @@ -953,8 +972,14 @@ class IntracellularElectrodesTable(DynamicTable): } }, ) - electrode: List[IntracellularElectrode] = Field( - ..., description="""Column for storing the reference to the intracellular electrode.""" + electrode: VectorData[NDArray[Any, IntracellularElectrode]] = Field( + ..., + description="""Column for storing the reference to the intracellular electrode.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) colnames: List[str] = Field( ..., @@ -965,9 +990,6 @@ class IntracellularElectrodesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularStimuliTable(DynamicTable): @@ -1002,7 +1024,7 @@ class IntracellularStimuliTable(DynamicTable): } }, ) - stimulus_template: Named[Optional[TimeSeriesReferenceVectorData]] = Field( + stimulus_template: Optional[Named[TimeSeriesReferenceVectorData]] = Field( None, description="""Column storing the reference to the stimulus template for the recording (rows).""", json_schema_extra={ @@ -1023,9 +1045,6 @@ class IntracellularStimuliTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularResponsesTable(DynamicTable): @@ -1069,9 +1088,6 @@ class IntracellularResponsesTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class IntracellularRecordingsTable(AlignedDynamicTable): @@ -1123,7 +1139,7 @@ class IntracellularRecordingsTable(AlignedDynamicTable): responses: IntracellularResponsesTable = Field( ..., description="""Table for storing intracellular response related metadata.""" ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) colnames: List[str] = Field( @@ -1135,9 +1151,6 @@ class IntracellularRecordingsTable(AlignedDynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTable(DynamicTable): @@ -1184,9 +1197,6 @@ class SimultaneousRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SimultaneousRecordingsTableRecordings(DynamicTableRegion): @@ -1272,9 +1282,6 @@ class SequentialRecordingsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class SequentialRecordingsTableSimultaneousRecordings(DynamicTableRegion): @@ -1351,9 +1358,6 @@ class RepetitionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class RepetitionsTableSequentialRecordings(DynamicTableRegion): @@ -1432,9 +1436,6 @@ class ExperimentalConditionsTable(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class ExperimentalConditionsTableRepetitions(DynamicTableRegion): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_image.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_image.py index 51da971..6e97172 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_image.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_image.py @@ -56,12 +56,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_misc.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_misc.py index dde35ee..1eb2c3a 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_misc.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_misc.py @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -328,7 +344,7 @@ class DecompositionSeries(TimeSeries): ..., description="""Data decomposed into frequency bands.""" ) metric: str = Field(..., description="""The metric used, e.g. phase, amplitude, power.""") - source_channels: Named[Optional[DynamicTableRegion]] = Field( + source_channels: Optional[Named[DynamicTableRegion]] = Field( None, description="""DynamicTableRegion pointer to the channels that this decomposition series was generated from.""", json_schema_extra={ @@ -476,9 +492,6 @@ class DecompositionSeriesBands(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class Units(DynamicTable): @@ -491,7 +504,7 @@ class Units(DynamicTable): ) name: str = Field("Units", json_schema_extra={"linkml_meta": {"ifabsent": "string(Units)"}}) - spike_times_index: Named[Optional[VectorIndex]] = Field( + spike_times_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the spike_times dataset.""", json_schema_extra={ @@ -506,7 +519,7 @@ class Units(DynamicTable): spike_times: Optional[UnitsSpikeTimes] = Field( None, description="""Spike times for each unit in seconds.""" ) - obs_intervals_index: Named[Optional[VectorIndex]] = Field( + obs_intervals_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the obs_intervals dataset.""", json_schema_extra={ @@ -534,7 +547,7 @@ class Units(DynamicTable): }, ) ) - electrodes_index: Named[Optional[VectorIndex]] = Field( + electrodes_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into electrodes.""", json_schema_extra={ @@ -558,8 +571,14 @@ class Units(DynamicTable): } }, ) - electrode_group: Optional[List[ElectrodeGroup]] = Field( - None, description="""Electrode group that each spike unit came from.""" + electrode_group: Optional[VectorData[NDArray[Any, ElectrodeGroup]]] = Field( + None, + description="""Electrode group that each spike unit came from.""", + json_schema_extra={ + "linkml_meta": { + "array": {"maximum_number_dimensions": False, "minimum_number_dimensions": 1} + } + }, ) waveform_mean: Optional[ VectorData[ @@ -588,7 +607,7 @@ class Units(DynamicTable): }, ) ) - waveforms_index: Named[Optional[VectorIndex]] = Field( + waveforms_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms dataset. One value for every spike event. See 'waveforms' for more detail.""", json_schema_extra={ @@ -600,7 +619,7 @@ class Units(DynamicTable): } }, ) - waveforms_index_index: Named[Optional[VectorIndex]] = Field( + waveforms_index_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into the waveforms_index dataset. One value for every unit (row in the table). See 'waveforms' for more detail.""", json_schema_extra={ @@ -622,9 +641,6 @@ class Units(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class UnitsSpikeTimes(VectorData): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ogen.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ogen.py index a321489..626a28c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ogen.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ogen.py @@ -55,12 +55,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ophys.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ophys.py index a3bbf68..d462064 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ophys.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_ophys.py @@ -72,12 +72,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -398,7 +414,7 @@ class DfOverF(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -413,7 +429,7 @@ class Fluorescence(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[RoiResponseSeries]] = Field( + value: Optional[Dict[str, RoiResponseSeries]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "RoiResponseSeries"}]}} ) name: str = Field(...) @@ -428,7 +444,7 @@ class ImageSegmentation(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[PlaneSegmentation]] = Field( + value: Optional[Dict[str, PlaneSegmentation]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "PlaneSegmentation"}]}} ) name: str = Field(...) @@ -444,11 +460,18 @@ class PlaneSegmentation(DynamicTable): ) name: str = Field(...) - image_mask: Optional[PlaneSegmentationImageMask] = Field( + image_mask: Optional[ + VectorData[ + Union[ + NDArray[Shape["* num_roi, * num_x, * num_y"], Any], + NDArray[Shape["* num_roi, * num_x, * num_y, * num_z"], Any], + ] + ] + ] = Field( None, description="""ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero.""", ) - pixel_mask_index: Named[Optional[VectorIndex]] = Field( + pixel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into pixel_mask.""", json_schema_extra={ @@ -464,7 +487,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - voxel_mask_index: Named[Optional[VectorIndex]] = Field( + voxel_mask_index: Optional[Named[VectorIndex]] = Field( None, description="""Index into voxel_mask.""", json_schema_extra={ @@ -480,7 +503,7 @@ class PlaneSegmentation(DynamicTable): None, description="""Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation""", ) - reference_images: Optional[List[ImageSeries]] = Field( + reference_images: Optional[Dict[str, ImageSeries]] = Field( None, description="""Image stacks that the segmentation masks apply to.""", json_schema_extra={"linkml_meta": {"any_of": [{"range": "ImageSeries"}]}}, @@ -504,33 +527,6 @@ class PlaneSegmentation(DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) - - -class PlaneSegmentationImageMask(VectorData): - """ - ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "core.nwb.ophys"}) - - name: Literal["image_mask"] = Field( - "image_mask", - json_schema_extra={ - "linkml_meta": {"equals_string": "image_mask", "ifabsent": "string(image_mask)"} - }, - ) - description: str = Field(..., description="""Description of what these vectors represent.""") - value: Optional[ - Union[ - NDArray[Shape["* dim0"], Any], - NDArray[Shape["* dim0, * dim1"], Any], - NDArray[Shape["* dim0, * dim1, * dim2"], Any], - NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], - ] - ] = Field(None) class PlaneSegmentationPixelMask(VectorData): @@ -653,7 +649,7 @@ class ImagingPlane(NWBContainer): None, description="""Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = \"Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral).\"""", ) - optical_channel: List[OpticalChannel] = Field( + optical_channel: Dict[str, OpticalChannel] = Field( ..., description="""An optical channel used to record from an imaging plane.""" ) device: Union[Device, str] = Field( @@ -767,7 +763,7 @@ class MotionCorrection(NWBDataInterface): {"from_schema": "core.nwb.ophys", "tree_root": True} ) - value: Optional[List[CorrectedImageStack]] = Field( + value: Optional[Dict[str, CorrectedImageStack]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "CorrectedImageStack"}]}} ) name: str = Field(...) @@ -810,7 +806,6 @@ DfOverF.model_rebuild() Fluorescence.model_rebuild() ImageSegmentation.model_rebuild() PlaneSegmentation.model_rebuild() -PlaneSegmentationImageMask.model_rebuild() PlaneSegmentationPixelMask.model_rebuild() PlaneSegmentationVoxelMask.model_rebuild() ImagingPlane.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_retinotopy.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_retinotopy.py index 6d1a2d2..26f2f92 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_retinotopy.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/core_nwb_retinotopy.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/namespace.py index e2c6018..5747cde 100644 --- a/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/core/v2_7_0/namespace.py @@ -133,7 +133,6 @@ from ...core.v2_7_0.core_nwb_ophys import ( OnePhotonSeries, OpticalChannel, PlaneSegmentation, - PlaneSegmentationImageMask, PlaneSegmentationPixelMask, PlaneSegmentationVoxelMask, RoiResponseSeries, @@ -150,7 +149,7 @@ from ...core.v2_7_0.core_nwb_retinotopy import ( ImagingRetinotopyVasculatureImage, ) from ...hdmf_common.v1_8_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_8_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_8_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_8_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -206,12 +205,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_sparse.py index 59cf857..56af1b8 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_sparse.py @@ -47,12 +47,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py index f6fb96e..c1dd417 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py @@ -71,12 +71,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -111,10 +127,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -313,6 +329,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -508,6 +525,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -523,6 +541,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -545,14 +564,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -595,9 +611,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -647,6 +663,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -682,7 +699,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -694,9 +711,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -816,7 +833,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -966,9 +983,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/namespace.py index e9c0f5a..dcc5707 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_0/namespace.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_sparse.py index 59dc4e8..8ce7f43 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_sparse.py @@ -47,12 +47,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py index 05581fb..7c9cede 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py @@ -71,12 +71,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -111,10 +127,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -313,6 +329,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -508,6 +525,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -523,6 +541,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -545,14 +564,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -595,9 +611,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -647,6 +663,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -682,7 +699,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -694,9 +711,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -816,7 +833,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -966,9 +983,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/namespace.py index 1dbfe1b..0f66985 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_2/namespace.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_sparse.py index 6e16e46..c0f7fcc 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_sparse.py @@ -47,12 +47,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py index e0ce3e1..3ce5c1c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py @@ -71,12 +71,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -111,10 +127,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -313,6 +329,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -508,6 +525,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -523,6 +541,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -545,14 +564,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -595,9 +611,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -647,6 +663,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -682,7 +699,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -694,9 +711,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -816,7 +833,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -978,9 +995,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns of this dynamic table.""" - ) vector_index: Optional[List[VectorIndex]] = Field( None, description="""Indices for the vector columns of this dynamic table.""" ) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/namespace.py index bed51d1..c505d77 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_1_3/namespace.py @@ -63,12 +63,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py index 02db015..656629d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py index 31d5909..13824fe 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py @@ -47,12 +47,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py index 3232dbf..08fa840 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -973,9 +990,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/namespace.py index 538cb3a..25e5651 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_0/namespace.py @@ -62,12 +62,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py index 8bd7526..affaa59 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py index f430892..01484f3 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py index e6e72ea..fc08eaf 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -973,9 +990,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/namespace.py index b6d2dde..1338679 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_2_1/namespace.py @@ -62,12 +62,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py index b0a5b1f..a7ed66d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py index 83e7be1..4d4850c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py index 28e5990..2620eb6 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py index de1ea05..7bbf3fd 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -973,9 +990,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/namespace.py index 6e4f626..040adf7 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_3_0/namespace.py @@ -64,12 +64,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py index b74a6dc..02d67bf 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -118,7 +134,7 @@ class SimpleMultiContainer(Container): {"from_schema": "hdmf-common.base", "tree_root": True} ) - value: Optional[List[Container]] = Field( + value: Optional[Dict[str, Container]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py index e32e04f..ad70998 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -116,23 +132,15 @@ class CSRMatrix(Container): "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} }, ) - data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") - - -class CSRMatrixData(ConfiguredBaseModel): - """ - The non-zero values in the matrix. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) - - name: Literal["data"] = Field( - "data", - json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + data: NDArray[Shape["* number_of_non_zero_values"], Any] = Field( + ..., + description="""The non-zero values in the matrix.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, ) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model CSRMatrix.model_rebuild() -CSRMatrixData.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py index f6f0ea5..a2a0a05 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -947,9 +964,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/namespace.py index 2cad381..0a85a76 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_4_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_4_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_4_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_4_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_4_0.hdmf_common_table import ( DynamicTable, DynamicTableRegion, @@ -56,12 +56,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_base.py index 1d3106d..7c62f93 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -118,7 +134,7 @@ class SimpleMultiContainer(Container): {"from_schema": "hdmf-common.base", "tree_root": True} ) - value: Optional[List[Container]] = Field( + value: Optional[Dict[str, Container]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_sparse.py index a60b286..d434cd9 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -116,23 +132,15 @@ class CSRMatrix(Container): "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} }, ) - data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") - - -class CSRMatrixData(ConfiguredBaseModel): - """ - The non-zero values in the matrix. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) - - name: Literal["data"] = Field( - "data", - json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + data: NDArray[Shape["* number_of_non_zero_values"], Any] = Field( + ..., + description="""The non-zero values in the matrix.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, ) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model CSRMatrix.model_rebuild() -CSRMatrixData.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py index 6570cc0..4e94a01 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -947,9 +964,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): @@ -961,7 +975,7 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): {"from_schema": "hdmf-common.table", "tree_root": True} ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) name: str = Field(...) @@ -975,9 +989,6 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/namespace.py index c8a084c..6e04fd0 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_5_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -57,12 +57,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py index af3eb2b..df22948 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -118,7 +134,7 @@ class SimpleMultiContainer(Container): {"from_schema": "hdmf-common.base", "tree_root": True} ) - value: Optional[List[Container]] = Field( + value: Optional[Dict[str, Container]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py index 1c5dcff..4e921cc 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -116,23 +132,15 @@ class CSRMatrix(Container): "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} }, ) - data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") - - -class CSRMatrixData(ConfiguredBaseModel): - """ - The non-zero values in the matrix. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) - - name: Literal["data"] = Field( - "data", - json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + data: NDArray[Shape["* number_of_non_zero_values"], Any] = Field( + ..., + description="""The non-zero values in the matrix.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, ) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model CSRMatrix.model_rebuild() -CSRMatrixData.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py index dfede77..75eadd1 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -947,9 +964,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): @@ -961,7 +975,7 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): {"from_schema": "hdmf-common.table", "tree_root": True} ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) name: str = Field(...) @@ -975,9 +989,6 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/namespace.py index 91fdf29..fa4ea72 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_5_1/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_5_1.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_1.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_1.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_1.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -57,12 +57,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py index 408b90c..57c9079 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -118,7 +134,7 @@ class SimpleMultiContainer(Container): {"from_schema": "hdmf-common.base", "tree_root": True} ) - value: Optional[List[Container]] = Field( + value: Optional[Dict[str, Container]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py index aee2271..73a6043 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -116,23 +132,15 @@ class CSRMatrix(Container): "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} }, ) - data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") - - -class CSRMatrixData(ConfiguredBaseModel): - """ - The non-zero values in the matrix. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) - - name: Literal["data"] = Field( - "data", - json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + data: NDArray[Shape["* number_of_non_zero_values"], Any] = Field( + ..., + description="""The non-zero values in the matrix.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, ) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model CSRMatrix.model_rebuild() -CSRMatrixData.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py index bccbdeb..7536e5c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -947,9 +964,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): @@ -961,7 +975,7 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): {"from_schema": "hdmf-common.table", "tree_root": True} ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) name: str = Field(...) @@ -975,9 +989,6 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/namespace.py index a30f739..981e600 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_6_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_6_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_6_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_6_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_6_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -57,12 +57,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py index 4df3ea5..e785e04 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py @@ -46,12 +46,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -118,7 +134,7 @@ class SimpleMultiContainer(Container): {"from_schema": "hdmf-common.base", "tree_root": True} ) - value: Optional[List[Container]] = Field( + value: Optional[Dict[str, Container]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py index 6dea1d7..b2fe190 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -116,23 +132,15 @@ class CSRMatrix(Container): "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} }, ) - data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") - - -class CSRMatrixData(ConfiguredBaseModel): - """ - The non-zero values in the matrix. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) - - name: Literal["data"] = Field( - "data", - json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + data: NDArray[Shape["* number_of_non_zero_values"], Any] = Field( + ..., + description="""The non-zero values in the matrix.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, ) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model CSRMatrix.model_rebuild() -CSRMatrixData.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py index e2c046c..92f64a1 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py @@ -73,12 +73,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -113,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -315,6 +331,7 @@ class DynamicTableMixin(BaseModel): NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", "name", + "categories", "colnames", "description", "hdf5_path", @@ -510,6 +527,7 @@ class DynamicTableMixin(BaseModel): if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] model["colnames"] = colnames else: @@ -525,6 +543,7 @@ class DynamicTableMixin(BaseModel): and not k.endswith("_index") and k not in model["colnames"] and not isinstance(model[k], VectorIndexMixin) + and model[k] is not None ] ) model["colnames"] = colnames @@ -547,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -597,9 +613,9 @@ class DynamicTableMixin(BaseModel): """ Ensure that all columns are equal length """ - lengths = [len(v) for v in self._columns.values()] + [len(self.id)] + lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -649,6 +665,7 @@ class AlignedDynamicTableMixin(BaseModel): __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", @@ -684,7 +701,7 @@ class AlignedDynamicTableMixin(BaseModel): elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[1], str): # get a slice of a single table return self._categories[item[1]][item[0]] - elif isinstance(item, (int, slice, Iterable)): + elif isinstance(item, (int, slice, Iterable, np.int_)): # get a slice of all the tables ids = self.id[item] if not isinstance(ids, Iterable): @@ -696,9 +713,9 @@ class AlignedDynamicTableMixin(BaseModel): if isinstance(table, pd.DataFrame): table = table.reset_index() elif isinstance(table, np.ndarray): - table = pd.DataFrame({category_name: [table]}) + table = pd.DataFrame({category_name: [table]}, index=ids.index) elif isinstance(table, Iterable): - table = pd.DataFrame({category_name: table}) + table = pd.DataFrame({category_name: table}, index=ids.index) else: raise ValueError( f"Don't know how to construct category table for {category_name}" @@ -818,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -947,9 +964,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): @@ -961,7 +975,7 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): {"from_schema": "hdmf-common.table", "tree_root": True} ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) name: str = Field(...) @@ -975,9 +989,6 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/namespace.py index 7e281c4..4aaa46d 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_7_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_7_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_7_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_7_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_7_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -57,12 +57,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_base.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_base.py index 6c72af5..7731368 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_base.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_base.py @@ -50,7 +50,7 @@ class ConfiguredBaseModel(BaseModel): except AttributeError: try: return handler(v["value"]) - except (KeyError, TypeError): + except (IndexError, KeyError, TypeError): raise e1 @field_validator("*", mode="before") @@ -59,9 +59,14 @@ class ConfiguredBaseModel(BaseModel): """Recast parent classes into child classes""" if isinstance(v, BaseModel): annotation = cls.model_fields[info.field_name].annotation - annotation = annotation.__args__[0] if hasattr(annotation, "__args__") else annotation - if issubclass(annotation, type(v)) and annotation is not type(v): - v = annotation(**v.__dict__) + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass return v @@ -129,7 +134,7 @@ class SimpleMultiContainer(Container): {"from_schema": "hdmf-common.base", "tree_root": True} ) - value: Optional[List[Container]] = Field( + value: Optional[Dict[str, Container]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} ) name: str = Field(...) diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_sparse.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_sparse.py index 9e95ec9..7a3e72c 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_sparse.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_sparse.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): @@ -116,23 +132,15 @@ class CSRMatrix(Container): "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} }, ) - data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") - - -class CSRMatrixData(ConfiguredBaseModel): - """ - The non-zero values in the matrix. - """ - - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) - - name: Literal["data"] = Field( - "data", - json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + data: NDArray[Shape["* number_of_non_zero_values"], Any] = Field( + ..., + description="""The non-zero values in the matrix.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, ) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model CSRMatrix.model_rebuild() -CSRMatrixData.model_rebuild() diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py index abdff19..06a391f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py @@ -1,6 +1,5 @@ from __future__ import annotations -import pdb import re import sys from datetime import date, datetime, time @@ -51,7 +50,6 @@ class ConfiguredBaseModel(BaseModel): arbitrary_types_allowed=True, use_enum_values=True, strict=False, - validation_error_cause=True, ) hdf5_path: Optional[str] = Field( None, description="The absolute path that this object is stored in an NWB file" @@ -69,7 +67,7 @@ class ConfiguredBaseModel(BaseModel): @field_validator("*", mode="wrap") @classmethod - def coerce_value(cls, v: Any, handler, info) -> Any: + def coerce_value(cls, v: Any, handler) -> Any: """Try to rescue instantiation by using the value field""" try: return handler(v) @@ -79,14 +77,8 @@ class ConfiguredBaseModel(BaseModel): except AttributeError: try: return handler(v["value"]) - except (KeyError, TypeError): + except (IndexError, KeyError, TypeError): raise e1 - # try: - # if hasattr(v, "value"): - # else: - # return handler(v["value"]) - # except Exception as e2: - # raise e2 from e1 @field_validator("*", mode="before") @classmethod @@ -94,9 +86,14 @@ class ConfiguredBaseModel(BaseModel): """Recast parent classes into child classes""" if isinstance(v, BaseModel): annotation = cls.model_fields[info.field_name].annotation - annotation = annotation.__args__[0] if hasattr(annotation, "__args__") else annotation - if issubclass(annotation, type(v)) and annotation is not type(v): - v = annotation(**v.__dict__) + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass return v @@ -132,10 +129,10 @@ class VectorDataMixin(BaseModel, Generic[T]): # redefined in `VectorData`, but included here for testing and type checking value: Optional[T] = None - # def __init__(self, value: Optional[NDArray] = None, **kwargs): - # if value is not None and "value" not in kwargs: - # kwargs["value"] = value - # super().__init__(**kwargs) + def __init__(self, value: Optional[T] = None, **kwargs): + if value is not None and "value" not in kwargs: + kwargs["value"] = value + super().__init__(**kwargs) def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: if self._index: @@ -329,7 +326,7 @@ class DynamicTableMixin(BaseModel): but simplifying along the way :) """ - model_config = ConfigDict(extra="allow", validate_assignment=True, validation_error_cause=True) + model_config = ConfigDict(extra="allow", validate_assignment=True) __pydantic_extra__: Dict[str, Union["VectorDataMixin", "VectorIndexMixin", "NDArray", list]] NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( "id", @@ -569,14 +566,11 @@ class DynamicTableMixin(BaseModel): continue if not isinstance(val, (VectorData, VectorIndex)): try: - if key.endswith("_index"): - to_cast = VectorIndex - else: - to_cast = VectorData + to_cast = VectorIndex if key.endswith("_index") else VectorData if isinstance(val, dict): model[key] = to_cast(**val) else: - model[key] = VectorIndex(name=key, description="", value=val) + model[key] = to_cast(name=key, description="", value=val) except ValidationError as e: # pragma: no cover raise ValidationError.from_exception_data( title=f"field {key} cannot be cast to VectorData from {val}", @@ -621,7 +615,7 @@ class DynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._columns.values() if v is not None] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "DynamicTable Columns are not of equal length! " + "DynamicTable columns are not of equal length! " f"Got colnames:\n{self.colnames}\nand lengths: {lengths}" ) return self @@ -667,16 +661,16 @@ class AlignedDynamicTableMixin(BaseModel): and also it's not so easy to copy a pydantic validator method. """ - model_config = ConfigDict(extra="allow", validate_assignment=True, validation_error_cause=True) + model_config = ConfigDict(extra="allow", validate_assignment=True) __pydantic_extra__: Dict[str, Union["DynamicTableMixin", "VectorDataMixin", "VectorIndexMixin"]] NON_CATEGORY_FIELDS: ClassVar[tuple[str]] = ( + "id", "name", "categories", "colnames", "description", "hdf5_path", - "id", "object_id", ) @@ -731,7 +725,6 @@ class AlignedDynamicTableMixin(BaseModel): names = [self.name] + self.categories # construct below in case we need to support array indexing in the future else: - pdb.set_trace() raise ValueError( f"Dont know how to index with {item}, " "need an int, string, slice, ndarray, or tuple[int | slice, str]" @@ -842,7 +835,7 @@ class AlignedDynamicTableMixin(BaseModel): """ lengths = [len(v) for v in self._categories.values()] + [len(self.id)] assert all([length == lengths[0] for length in lengths]), ( - "AlignedDynamicTable Columns are not of equal length! " + "AlignedDynamicTableColumns are not of equal length! " f"Got colnames:\n{self.categories}\nand lengths: {lengths}" ) return self @@ -971,9 +964,6 @@ class DynamicTable(DynamicTableMixin, ConfiguredBaseModel): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): @@ -985,7 +975,7 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): {"from_schema": "hdmf-common.table", "tree_root": True} ) - value: Optional[List[DynamicTable]] = Field( + value: Optional[Dict[str, DynamicTable]] = Field( None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} ) name: str = Field(...) @@ -999,9 +989,6 @@ class AlignedDynamicTable(AlignedDynamicTableMixin, DynamicTable): description="""Array of unique identifiers for the rows of this dynamic table.""", json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, ) - vector_data: Optional[List[VectorData]] = Field( - None, description="""Vector columns, including index columns, of this dynamic table.""" - ) # Model rebuild diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/namespace.py index b5fab8b..dd09b7f 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_common/v1_8_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_8_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_8_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_8_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_8_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -57,12 +57,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py index b168c99..ad617da 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py index f44da2b..cda720e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/namespace.py index 03d52ee..3429a1e 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_1_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_4_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_4_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_4_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_4_0.hdmf_common_table import ( DynamicTable, DynamicTableRegion, @@ -65,12 +65,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py index 12d022e..1a88edc 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py index 691cfa5..8d5af36 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/namespace.py index a15c9b3..c697f83 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_2_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_5_1.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_5_1.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_1.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_5_1.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -66,12 +66,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py index aa98c17..cbd0ad9 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py index 5aec913..9f337fa 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/namespace.py index 747571d..e1a12ca 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_3_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_6_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_6_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_6_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_6_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -66,12 +66,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py index 430676e..0551cfd 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py index 0e38714..09e6f05 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/namespace.py index 50252af..c904202 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_4_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_7_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_7_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_7_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_7_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -67,12 +67,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_experimental.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_experimental.py index 6f62e2f..714ae52 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_experimental.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_experimental.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_resources.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_resources.py index 8e802e2..d3132cd 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_resources.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/hdmf_experimental_resources.py @@ -49,12 +49,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/namespace.py b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/namespace.py index 84b1442..281e5b2 100644 --- a/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/namespace.py +++ b/nwb_models/src/nwb_models/models/pydantic/hdmf_experimental/v0_5_0/namespace.py @@ -11,7 +11,7 @@ import numpy as np from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator from ...hdmf_common.v1_8_0.hdmf_common_base import Container, Data, SimpleMultiContainer -from ...hdmf_common.v1_8_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_8_0.hdmf_common_sparse import CSRMatrix from ...hdmf_common.v1_8_0.hdmf_common_table import ( AlignedDynamicTable, DynamicTable, @@ -67,12 +67,28 @@ class ConfiguredBaseModel(BaseModel): return handler(v) except Exception as e1: try: - if hasattr(v, "value"): - return handler(v.value) - else: + return handler(v.value) + except AttributeError: + try: return handler(v["value"]) - except Exception as e2: - raise e2 from e1 + except (IndexError, KeyError, TypeError): + raise e1 + + @field_validator("*", mode="before") + @classmethod + def coerce_subclass(cls, v: Any, info) -> Any: + """Recast parent classes into child classes""" + if isinstance(v, BaseModel): + annotation = cls.model_fields[info.field_name].annotation + while hasattr(annotation, "__args__"): + annotation = annotation.__args__[0] + try: + if issubclass(annotation, type(v)) and annotation is not type(v): + v = annotation(**{**v.__dict__, **v.__pydantic_extra__}) + except TypeError: + # fine, annotation is a non-class type like a TypeVar + pass + return v class LinkMLMeta(RootModel): diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.base.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.base.yaml index 24a4bbf..b11b02c 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.base.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -33,6 +34,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true resolution: @@ -74,6 +76,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -85,6 +88,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -95,6 +99,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -120,6 +125,7 @@ classes: range: TimeSeries__data required: true multivalued: false + inlined: true starting_time: name: starting_time description: Timestamp of the first sample in seconds. When timestamps are @@ -128,6 +134,7 @@ classes: range: TimeSeries__starting_time required: false multivalued: false + inlined: true timestamps: name: timestamps description: Timestamps for samples stored in data, in seconds, relative to @@ -171,6 +178,8 @@ classes: range: TimeSeries__sync required: false multivalued: false + inlined: true + inlined_as_list: true tree_root: true TimeSeries__data: name: TimeSeries__data @@ -181,6 +190,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -258,6 +268,7 @@ classes: name: name: name ifabsent: string(starting_time) + identifier: true range: string required: true equals_string: starting_time @@ -289,6 +300,7 @@ classes: name: name: name ifabsent: string(sync) + identifier: true range: string required: true equals_string: sync @@ -313,6 +325,7 @@ classes: name: name: name ifabsent: string(Images) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.behavior.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.behavior.yaml index 1b74203..f63c218 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.behavior.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.behavior.yaml @@ -29,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: SpatialSeries__data required: true multivalued: false + inlined: true reference_frame: name: reference_frame description: Description defining what exactly 'straight-ahead' means. @@ -53,6 +55,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.device.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.device.yaml index 7881fcf..8d60d56 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.device.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.device.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ecephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ecephys.yaml index 2f3dd97..7a93461 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ecephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ecephys.yaml @@ -25,6 +25,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true filtering: @@ -71,6 +72,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true channel_conversion: name: channel_conversion description: Channel-specific conversion factor. Multiply the data in the @@ -103,6 +105,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -143,6 +146,7 @@ classes: name: name: name ifabsent: string(FeatureExtraction) + identifier: true range: string required: true description: @@ -189,6 +193,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true EventDetection: name: EventDetection @@ -198,6 +203,7 @@ classes: name: name: name ifabsent: string(EventDetection) + identifier: true range: string required: true detection_method: @@ -236,6 +242,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ElectricalSeries - range: string @@ -297,6 +304,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -317,6 +325,7 @@ classes: range: ElectrodeGroup__position required: false multivalued: false + inlined: true device: name: device annotations: @@ -325,6 +334,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -336,6 +346,7 @@ classes: name: name: name ifabsent: string(position) + identifier: true range: string required: true equals_string: position @@ -376,6 +387,7 @@ classes: name: name: name ifabsent: string(ClusterWaveforms) + identifier: true range: string required: true waveform_filtering: @@ -416,6 +428,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Clustering - range: string @@ -429,6 +442,7 @@ classes: name: name: name ifabsent: string(Clustering) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.epoch.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.epoch.yaml index ce14120..4eb778d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.epoch.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.epoch.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true start_time: @@ -64,12 +65,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true timeseries: name: timeseries description: An index into a TimeSeries object. range: TimeIntervals__timeseries required: false multivalued: false + inlined: true timeseries_index: name: timeseries_index annotations: @@ -83,6 +86,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true TimeIntervals__timeseries: name: TimeIntervals__timeseries @@ -92,6 +96,7 @@ classes: name: name: name ifabsent: string(timeseries) + identifier: true range: string required: true equals_string: timeseries @@ -122,3 +127,4 @@ classes: range: TimeSeries required: false multivalued: false + inlined: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.file.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.file.yaml index 0b76f4f..a3eb463 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.file.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.file.yaml @@ -28,6 +28,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true notes: @@ -45,6 +46,7 @@ classes: name: name: name ifabsent: string(root) + identifier: true range: string required: true equals_string: root @@ -184,6 +186,8 @@ classes: range: NWBFile__stimulus required: true multivalued: false + inlined: true + inlined_as_list: true general: name: general description: Experimental metadata, including protocol, notes and description @@ -204,6 +208,8 @@ classes: range: NWBFile__general required: true multivalued: false + inlined: true + inlined_as_list: true intervals: name: intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -213,12 +219,16 @@ classes: range: NWBFile__intervals required: false multivalued: false + inlined: true + inlined_as_list: true units: name: units description: Data about sorted spike units. range: Units required: false multivalued: false + inlined: true + inlined_as_list: false tree_root: true NWBFile__stimulus: name: NWBFile__stimulus @@ -238,6 +248,7 @@ classes: name: name: name ifabsent: string(stimulus) + identifier: true range: string required: true equals_string: stimulus @@ -280,6 +291,7 @@ classes: name: name: name ifabsent: string(general) + identifier: true range: string required: true equals_string: general @@ -375,6 +387,7 @@ classes: range: general__source_script required: false multivalued: false + inlined: true stimulus: name: stimulus description: Notes about stimuli, such as how and where they were presented. @@ -402,6 +415,8 @@ classes: range: LabMetaData required: false multivalued: true + inlined: true + inlined_as_list: false devices: name: devices description: Description of hardware devices used during experiment, e.g., @@ -418,18 +433,24 @@ classes: range: Subject required: false multivalued: false + inlined: true + inlined_as_list: false extracellular_ephys: name: extracellular_ephys description: Metadata related to extracellular electrophysiology. range: general__extracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true intracellular_ephys: name: intracellular_ephys description: Metadata related to intracellular electrophysiology. range: general__intracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true optogenetics: name: optogenetics description: Metadata describing optogenetic stimuluation. @@ -454,6 +475,7 @@ classes: name: name: name ifabsent: string(source_script) + identifier: true range: string required: true equals_string: source_script @@ -473,6 +495,7 @@ classes: name: name: name ifabsent: string(extracellular_ephys) + identifier: true range: string required: true equals_string: extracellular_ephys @@ -482,12 +505,16 @@ classes: range: ElectrodeGroup required: false multivalued: true + inlined: true + inlined_as_list: false electrodes: name: electrodes description: A table of all electrodes (i.e. channels) used for recording. range: extracellular_ephys__electrodes required: false multivalued: false + inlined: true + inlined_as_list: true extracellular_ephys__electrodes: name: extracellular_ephys__electrodes description: A table of all electrodes (i.e. channels) used for recording. @@ -496,6 +523,7 @@ classes: name: name: name ifabsent: string(electrodes) + identifier: true range: string required: true equals_string: electrodes @@ -559,9 +587,13 @@ classes: group: name: group description: Reference to the ElectrodeGroup this electrode is a part of. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: true - multivalued: true + multivalued: false + inlined: true group_name: name: group_name description: Name of the ElectrodeGroup this electrode is a part of. @@ -614,6 +646,7 @@ classes: name: name: name ifabsent: string(intracellular_ephys) + identifier: true range: string required: true equals_string: intracellular_ephys @@ -631,12 +664,16 @@ classes: range: IntracellularElectrode required: false multivalued: true + inlined: true + inlined_as_list: false sweep_table: name: sweep_table description: The table which groups different PatchClampSeries together. range: SweepTable required: false multivalued: false + inlined: true + inlined_as_list: false NWBFile__intervals: name: NWBFile__intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -646,6 +683,7 @@ classes: name: name: name ifabsent: string(intervals) + identifier: true range: string required: true equals_string: intervals @@ -656,18 +694,24 @@ classes: range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false trials: name: trials description: Repeated experimental events that have a logical grouping. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false invalid_times: name: invalid_times description: Time intervals that should be removed from analysis. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false time_intervals: name: time_intervals description: Optional additional table(s) for describing other experimental @@ -675,6 +719,8 @@ classes: range: TimeIntervals required: false multivalued: true + inlined: true + inlined_as_list: false LabMetaData: name: LabMetaData description: Lab-specific meta-data. @@ -682,6 +728,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -692,6 +739,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true age: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.icephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.icephys.yaml index d93bb52..26823be 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.icephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.icephys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -41,6 +42,7 @@ classes: range: PatchClampSeries__data required: true multivalued: false + inlined: true gain: name: gain description: Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt @@ -56,6 +58,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: IntracellularElectrode - range: string @@ -67,6 +70,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -92,6 +96,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -100,6 +105,7 @@ classes: range: CurrentClampSeries__data required: true multivalued: false + inlined: true bias_current: name: bias_current description: Bias current, in amps. @@ -126,6 +132,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -152,6 +159,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -188,6 +196,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -196,6 +205,7 @@ classes: range: CurrentClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true CurrentClampStimulusSeries__data: name: CurrentClampStimulusSeries__data @@ -204,6 +214,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -229,6 +240,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -237,48 +249,56 @@ classes: range: VoltageClampSeries__data required: true multivalued: false + inlined: true capacitance_fast: name: capacitance_fast description: Fast capacitance, in farads. range: VoltageClampSeries__capacitance_fast required: false multivalued: false + inlined: true capacitance_slow: name: capacitance_slow description: Slow capacitance, in farads. range: VoltageClampSeries__capacitance_slow required: false multivalued: false + inlined: true resistance_comp_bandwidth: name: resistance_comp_bandwidth description: Resistance compensation bandwidth, in hertz. range: VoltageClampSeries__resistance_comp_bandwidth required: false multivalued: false + inlined: true resistance_comp_correction: name: resistance_comp_correction description: Resistance compensation correction, in percent. range: VoltageClampSeries__resistance_comp_correction required: false multivalued: false + inlined: true resistance_comp_prediction: name: resistance_comp_prediction description: Resistance compensation prediction, in percent. range: VoltageClampSeries__resistance_comp_prediction required: false multivalued: false + inlined: true whole_cell_capacitance_comp: name: whole_cell_capacitance_comp description: Whole cell capacitance compensation, in farads. range: VoltageClampSeries__whole_cell_capacitance_comp required: false multivalued: false + inlined: true whole_cell_series_resistance_comp: name: whole_cell_series_resistance_comp description: Whole cell series resistance compensation, in ohms. range: VoltageClampSeries__whole_cell_series_resistance_comp required: false multivalued: false + inlined: true tree_root: true VoltageClampSeries__data: name: VoltageClampSeries__data @@ -287,6 +307,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -310,6 +331,7 @@ classes: name: name: name ifabsent: string(capacitance_fast) + identifier: true range: string required: true equals_string: capacitance_fast @@ -331,6 +353,7 @@ classes: name: name: name ifabsent: string(capacitance_slow) + identifier: true range: string required: true equals_string: capacitance_slow @@ -352,6 +375,7 @@ classes: name: name: name ifabsent: string(resistance_comp_bandwidth) + identifier: true range: string required: true equals_string: resistance_comp_bandwidth @@ -374,6 +398,7 @@ classes: name: name: name ifabsent: string(resistance_comp_correction) + identifier: true range: string required: true equals_string: resistance_comp_correction @@ -396,6 +421,7 @@ classes: name: name: name ifabsent: string(resistance_comp_prediction) + identifier: true range: string required: true equals_string: resistance_comp_prediction @@ -418,6 +444,7 @@ classes: name: name: name ifabsent: string(whole_cell_capacitance_comp) + identifier: true range: string required: true equals_string: whole_cell_capacitance_comp @@ -440,6 +467,7 @@ classes: name: name: name ifabsent: string(whole_cell_series_resistance_comp) + identifier: true range: string required: true equals_string: whole_cell_series_resistance_comp @@ -462,6 +490,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -470,6 +499,7 @@ classes: range: VoltageClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true VoltageClampStimulusSeries__data: name: VoltageClampStimulusSeries__data @@ -478,6 +508,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -501,6 +532,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -555,6 +587,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -566,6 +599,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true sweep_number: @@ -580,9 +614,13 @@ classes: series: name: series description: The PatchClampSeries with the sweep number in that row. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: PatchClampSeries required: true - multivalued: true + multivalued: false + inlined: true series_index: name: series_index annotations: @@ -596,4 +634,5 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.image.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.image.yaml index 7659152..adfab1b 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.image.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.image.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -38,6 +39,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -57,6 +59,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -81,6 +84,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -120,6 +124,7 @@ classes: range: ImageSeries__external_file required: false multivalued: false + inlined: true format: name: format description: Format of image. If this is 'external', then the attribute 'external_file' @@ -137,6 +142,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Device - range: string @@ -151,6 +157,7 @@ classes: name: name: name ifabsent: string(external_file) + identifier: true range: string required: true equals_string: external_file @@ -188,6 +195,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true masked_imageseries: @@ -198,6 +206,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -213,6 +222,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true distance: @@ -275,6 +285,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -294,6 +305,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.misc.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.misc.yaml index 89d5ee0..c2323b8 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.misc.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.misc.yaml @@ -30,6 +30,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: AbstractFeatureSeries__data required: true multivalued: false + inlined: true feature_units: name: feature_units description: Units of each feature. @@ -64,6 +66,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -96,6 +99,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -140,6 +145,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -148,6 +154,7 @@ classes: range: DecompositionSeries__data required: true multivalued: false + inlined: true metric: name: metric description: The metric used, e.g. phase, amplitude, power. @@ -168,6 +175,7 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true bands: name: bands description: Table for describing the bands that this series was generated @@ -175,6 +183,8 @@ classes: range: DecompositionSeries__bands required: true multivalued: false + inlined: true + inlined_as_list: true source_timeseries: name: source_timeseries annotations: @@ -183,6 +193,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: TimeSeries - range: string @@ -194,6 +205,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -222,6 +234,7 @@ classes: name: name: name ifabsent: string(bands) + identifier: true range: string required: true equals_string: bands @@ -273,6 +286,7 @@ classes: name: name: name ifabsent: string(Units) + identifier: true range: string required: true spike_times_index: @@ -288,12 +302,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true spike_times: name: spike_times description: Spike times for each unit. range: Units__spike_times required: false multivalued: false + inlined: true obs_intervals_index: name: obs_intervals_index annotations: @@ -307,6 +323,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true obs_intervals: name: obs_intervals description: Observation intervals for each unit. @@ -331,6 +348,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true electrodes: name: electrodes annotations: @@ -344,12 +362,17 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true electrode_group: name: electrode_group description: Electrode group that each spike unit came from. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: false - multivalued: true + multivalued: false + inlined: true waveform_mean: name: waveform_mean description: Spike waveform mean for each spike unit. @@ -428,6 +451,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true waveforms_index_index: name: waveforms_index_index annotations: @@ -442,6 +466,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true Units__spike_times: name: Units__spike_times @@ -451,6 +476,7 @@ classes: name: name: name ifabsent: string(spike_times) + identifier: true range: string required: true equals_string: spike_times diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ogen.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ogen.yaml index 3148b98..0dc7be0 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ogen.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ogen.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -40,6 +41,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: OptogeneticStimulusSite - range: string @@ -51,6 +53,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -81,6 +84,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ophys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ophys.yaml index e9c680d..40860fc 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ophys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.ophys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -60,6 +61,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -72,6 +74,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -102,6 +105,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true DfOverF: name: DfOverF @@ -156,15 +160,28 @@ classes: attributes: name: name: name + identifier: true range: string required: true image_mask: name: image_mask description: ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - range: PlaneSegmentation__image_mask + range: AnyType required: false multivalued: false + any_of: + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - alias: num_z pixel_mask_index: name: pixel_mask_index annotations: @@ -178,6 +195,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true pixel_mask: name: pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for @@ -186,6 +204,7 @@ classes: range: PlaneSegmentation__pixel_mask required: false multivalued: false + inlined: true voxel_mask_index: name: voxel_mask_index annotations: @@ -199,6 +218,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true voxel_mask: name: voxel_mask description: 'Voxel masks for each ROI: a list of indices and weights for @@ -207,6 +227,7 @@ classes: range: PlaneSegmentation__voxel_mask required: false multivalued: false + inlined: true reference_images: name: reference_images description: Image stacks that the segmentation masks apply to. @@ -223,22 +244,11 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string tree_root: true - PlaneSegmentation__image_mask: - name: PlaneSegmentation__image_mask - description: ROI masks for each ROI. Each image mask is the size of the original - imaging plane (or volume) and members of the ROI are finite non-zero. - is_a: VectorData - attributes: - name: - name: name - ifabsent: string(image_mask) - range: string - required: true - equals_string: image_mask PlaneSegmentation__pixel_mask: name: PlaneSegmentation__pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for the @@ -249,6 +259,7 @@ classes: name: name: name ifabsent: string(pixel_mask) + identifier: true range: string required: true equals_string: pixel_mask @@ -286,6 +297,7 @@ classes: name: name: name ifabsent: string(voxel_mask) + identifier: true range: string required: true equals_string: voxel_mask @@ -328,6 +340,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -371,6 +384,7 @@ classes: range: ImagingPlane__manifold required: false multivalued: false + inlined: true origin_coords: name: origin_coords description: Physical location of the first element of the imaging plane (0, @@ -379,6 +393,7 @@ classes: range: ImagingPlane__origin_coords required: false multivalued: false + inlined: true grid_spacing: name: grid_spacing description: Space between pixels in (x, y) or voxels in (x, y, z) directions, @@ -387,6 +402,7 @@ classes: range: ImagingPlane__grid_spacing required: false multivalued: false + inlined: true reference_frame: name: reference_frame description: Describes reference frame of origin_coords and grid_spacing. @@ -415,6 +431,8 @@ classes: range: OpticalChannel required: true multivalued: true + inlined: true + inlined_as_list: false device: name: device annotations: @@ -423,6 +441,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -436,6 +455,7 @@ classes: name: name: name ifabsent: string(manifold) + identifier: true range: string required: true equals_string: manifold @@ -485,6 +505,7 @@ classes: name: name: name ifabsent: string(origin_coords) + identifier: true range: string required: true equals_string: origin_coords @@ -515,6 +536,7 @@ classes: name: name: name ifabsent: string(grid_spacing) + identifier: true range: string required: true equals_string: grid_spacing @@ -543,6 +565,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -579,6 +602,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true corrected: @@ -587,6 +611,8 @@ classes: range: ImageSeries required: true multivalued: false + inlined: true + inlined_as_list: false xy_translation: name: xy_translation description: Stores the x,y delta necessary to align each frame to the common @@ -594,6 +620,8 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true + inlined_as_list: false original: name: original annotations: @@ -602,6 +630,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.retinotopy.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.retinotopy.yaml index cc06e90..97007ea 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.retinotopy.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_3_0/core.nwb.retinotopy.yaml @@ -29,6 +29,7 @@ classes: name: name: name ifabsent: string(ImagingRetinotopy) + identifier: true range: string required: true axis_1_phase_map: @@ -37,6 +38,7 @@ classes: range: ImagingRetinotopy__axis_1_phase_map required: true multivalued: false + inlined: true axis_1_power_map: name: axis_1_power_map description: Power response on the first measured axis. Response is scaled @@ -44,12 +46,14 @@ classes: range: ImagingRetinotopy__axis_1_power_map required: false multivalued: false + inlined: true axis_2_phase_map: name: axis_2_phase_map description: Phase response to stimulus on the second measured axis. range: ImagingRetinotopy__axis_2_phase_map required: true multivalued: false + inlined: true axis_2_power_map: name: axis_2_power_map description: Power response on the second measured axis. Response is scaled @@ -57,6 +61,7 @@ classes: range: ImagingRetinotopy__axis_2_power_map required: false multivalued: false + inlined: true axis_descriptions: name: axis_descriptions description: Two-element array describing the contents of the two response @@ -76,6 +81,7 @@ classes: range: ImagingRetinotopy__focal_depth_image required: false multivalued: false + inlined: true sign_map: name: sign_map description: Sine of the angle between the direction of the gradient in axis_1 @@ -83,6 +89,7 @@ classes: range: ImagingRetinotopy__sign_map required: false multivalued: false + inlined: true vasculature_image: name: vasculature_image description: 'Gray-scale anatomical image of cortical surface. Array structure: @@ -90,6 +97,7 @@ classes: range: ImagingRetinotopy__vasculature_image required: true multivalued: false + inlined: true tree_root: true ImagingRetinotopy__axis_1_phase_map: name: ImagingRetinotopy__axis_1_phase_map @@ -98,6 +106,7 @@ classes: name: name: name ifabsent: string(axis_1_phase_map) + identifier: true range: string required: true equals_string: axis_1_phase_map @@ -134,6 +143,7 @@ classes: name: name: name ifabsent: string(axis_1_power_map) + identifier: true range: string required: true equals_string: axis_1_power_map @@ -169,6 +179,7 @@ classes: name: name: name ifabsent: string(axis_2_phase_map) + identifier: true range: string required: true equals_string: axis_2_phase_map @@ -205,6 +216,7 @@ classes: name: name: name ifabsent: string(axis_2_power_map) + identifier: true range: string required: true equals_string: axis_2_power_map @@ -241,6 +253,7 @@ classes: name: name: name ifabsent: string(focal_depth_image) + identifier: true range: string required: true equals_string: focal_depth_image @@ -288,6 +301,7 @@ classes: name: name: name ifabsent: string(sign_map) + identifier: true range: string required: true equals_string: sign_map @@ -319,6 +333,7 @@ classes: name: name: name ifabsent: string(vasculature_image) + identifier: true range: string required: true equals_string: vasculature_image diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.base.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.base.yaml index 8369707..1bfb911 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.base.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -34,6 +35,7 @@ classes: name: name: name ifabsent: string(timeseries) + identifier: true range: string required: true idx_start: @@ -63,6 +65,7 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true tree_root: true Image: name: Image @@ -73,6 +76,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true resolution: @@ -114,6 +118,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -125,6 +130,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -135,6 +141,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -160,6 +167,7 @@ classes: range: TimeSeries__data required: true multivalued: false + inlined: true starting_time: name: starting_time description: Timestamp of the first sample in seconds. When timestamps are @@ -168,6 +176,7 @@ classes: range: TimeSeries__starting_time required: false multivalued: false + inlined: true timestamps: name: timestamps description: Timestamps for samples stored in data, in seconds, relative to @@ -211,6 +220,8 @@ classes: range: TimeSeries__sync required: false multivalued: false + inlined: true + inlined_as_list: true tree_root: true TimeSeries__data: name: TimeSeries__data @@ -221,6 +232,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -298,6 +310,7 @@ classes: name: name: name ifabsent: string(starting_time) + identifier: true range: string required: true equals_string: starting_time @@ -329,6 +342,7 @@ classes: name: name: name ifabsent: string(sync) + identifier: true range: string required: true equals_string: sync @@ -353,6 +367,7 @@ classes: name: name: name ifabsent: string(Images) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.behavior.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.behavior.yaml index ba29236..47aa752 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.behavior.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.behavior.yaml @@ -29,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: SpatialSeries__data required: true multivalued: false + inlined: true reference_frame: name: reference_frame description: Description defining what exactly 'straight-ahead' means. @@ -53,6 +55,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.device.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.device.yaml index fc320af..307f846 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.device.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.device.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ecephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ecephys.yaml index f0eccd6..4d8e539 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ecephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ecephys.yaml @@ -25,6 +25,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true filtering: @@ -71,6 +72,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true channel_conversion: name: channel_conversion description: Channel-specific conversion factor. Multiply the data in the @@ -103,6 +105,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -143,6 +146,7 @@ classes: name: name: name ifabsent: string(FeatureExtraction) + identifier: true range: string required: true description: @@ -189,6 +193,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true EventDetection: name: EventDetection @@ -198,6 +203,7 @@ classes: name: name: name ifabsent: string(EventDetection) + identifier: true range: string required: true detection_method: @@ -236,6 +242,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ElectricalSeries - range: string @@ -297,6 +304,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -317,6 +325,7 @@ classes: range: ElectrodeGroup__position required: false multivalued: false + inlined: true device: name: device annotations: @@ -325,6 +334,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -336,6 +346,7 @@ classes: name: name: name ifabsent: string(position) + identifier: true range: string required: true equals_string: position @@ -376,6 +387,7 @@ classes: name: name: name ifabsent: string(ClusterWaveforms) + identifier: true range: string required: true waveform_filtering: @@ -416,6 +428,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Clustering - range: string @@ -429,6 +442,7 @@ classes: name: name: name ifabsent: string(Clustering) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.epoch.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.epoch.yaml index c3fb2cb..e264a54 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.epoch.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.epoch.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true start_time: @@ -64,12 +65,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true timeseries: name: timeseries description: An index into a TimeSeries object. range: TimeIntervals__timeseries required: false multivalued: false + inlined: true timeseries_index: name: timeseries_index annotations: @@ -83,6 +86,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true TimeIntervals__timeseries: name: TimeIntervals__timeseries @@ -92,6 +96,7 @@ classes: name: name: name ifabsent: string(timeseries) + identifier: true range: string required: true equals_string: timeseries @@ -122,3 +127,4 @@ classes: range: TimeSeries required: false multivalued: false + inlined: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.file.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.file.yaml index 13bf8a1..f81b157 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.file.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.file.yaml @@ -28,6 +28,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true notes: @@ -45,6 +46,7 @@ classes: name: name: name ifabsent: string(root) + identifier: true range: string required: true equals_string: root @@ -184,6 +186,8 @@ classes: range: NWBFile__stimulus required: true multivalued: false + inlined: true + inlined_as_list: true general: name: general description: Experimental metadata, including protocol, notes and description @@ -204,6 +208,8 @@ classes: range: NWBFile__general required: true multivalued: false + inlined: true + inlined_as_list: true intervals: name: intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -213,12 +219,16 @@ classes: range: NWBFile__intervals required: false multivalued: false + inlined: true + inlined_as_list: true units: name: units description: Data about sorted spike units. range: Units required: false multivalued: false + inlined: true + inlined_as_list: false tree_root: true NWBFile__stimulus: name: NWBFile__stimulus @@ -238,6 +248,7 @@ classes: name: name: name ifabsent: string(stimulus) + identifier: true range: string required: true equals_string: stimulus @@ -280,6 +291,7 @@ classes: name: name: name ifabsent: string(general) + identifier: true range: string required: true equals_string: general @@ -375,6 +387,7 @@ classes: range: general__source_script required: false multivalued: false + inlined: true stimulus: name: stimulus description: Notes about stimuli, such as how and where they were presented. @@ -402,6 +415,8 @@ classes: range: LabMetaData required: false multivalued: true + inlined: true + inlined_as_list: false devices: name: devices description: Description of hardware devices used during experiment, e.g., @@ -418,18 +433,24 @@ classes: range: Subject required: false multivalued: false + inlined: true + inlined_as_list: false extracellular_ephys: name: extracellular_ephys description: Metadata related to extracellular electrophysiology. range: general__extracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true intracellular_ephys: name: intracellular_ephys description: Metadata related to intracellular electrophysiology. range: general__intracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true optogenetics: name: optogenetics description: Metadata describing optogenetic stimuluation. @@ -454,6 +475,7 @@ classes: name: name: name ifabsent: string(source_script) + identifier: true range: string required: true equals_string: source_script @@ -473,6 +495,7 @@ classes: name: name: name ifabsent: string(extracellular_ephys) + identifier: true range: string required: true equals_string: extracellular_ephys @@ -482,12 +505,16 @@ classes: range: ElectrodeGroup required: false multivalued: true + inlined: true + inlined_as_list: false electrodes: name: electrodes description: A table of all electrodes (i.e. channels) used for recording. range: extracellular_ephys__electrodes required: false multivalued: false + inlined: true + inlined_as_list: true extracellular_ephys__electrodes: name: extracellular_ephys__electrodes description: A table of all electrodes (i.e. channels) used for recording. @@ -496,6 +523,7 @@ classes: name: name: name ifabsent: string(electrodes) + identifier: true range: string required: true equals_string: electrodes @@ -559,9 +587,13 @@ classes: group: name: group description: Reference to the ElectrodeGroup this electrode is a part of. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: true - multivalued: true + multivalued: false + inlined: true group_name: name: group_name description: Name of the ElectrodeGroup this electrode is a part of. @@ -614,6 +646,7 @@ classes: name: name: name ifabsent: string(intracellular_ephys) + identifier: true range: string required: true equals_string: intracellular_ephys @@ -632,6 +665,8 @@ classes: range: IntracellularElectrode required: false multivalued: true + inlined: true + inlined_as_list: false sweep_table: name: sweep_table description: '[DEPRECATED] Table used to group different PatchClampSeries. @@ -641,6 +676,8 @@ classes: range: SweepTable required: false multivalued: false + inlined: true + inlined_as_list: false intracellular_recordings: name: intracellular_recordings description: A table to group together a stimulus and response from a single @@ -658,6 +695,8 @@ classes: range: IntracellularRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false simultaneous_recordings: name: simultaneous_recordings description: A table for grouping different intracellular recordings from @@ -666,6 +705,8 @@ classes: range: SimultaneousRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false sequential_recordings: name: sequential_recordings description: A table for grouping different sequential recordings from the @@ -675,6 +716,8 @@ classes: range: SequentialRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false repetitions: name: repetitions description: A table for grouping different sequential intracellular recordings @@ -684,6 +727,8 @@ classes: range: RepetitionsTable required: false multivalued: false + inlined: true + inlined_as_list: false experimental_conditions: name: experimental_conditions description: A table for grouping different intracellular recording repetitions @@ -691,6 +736,8 @@ classes: range: ExperimentalConditionsTable required: false multivalued: false + inlined: true + inlined_as_list: false NWBFile__intervals: name: NWBFile__intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -700,6 +747,7 @@ classes: name: name: name ifabsent: string(intervals) + identifier: true range: string required: true equals_string: intervals @@ -710,18 +758,24 @@ classes: range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false trials: name: trials description: Repeated experimental events that have a logical grouping. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false invalid_times: name: invalid_times description: Time intervals that should be removed from analysis. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false time_intervals: name: time_intervals description: Optional additional table(s) for describing other experimental @@ -729,6 +783,8 @@ classes: range: TimeIntervals required: false multivalued: true + inlined: true + inlined_as_list: false LabMetaData: name: LabMetaData description: Lab-specific meta-data. @@ -736,6 +792,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -746,6 +803,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true age: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.icephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.icephys.yaml index 346751e..d3a808f 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.icephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.icephys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -41,6 +42,7 @@ classes: range: PatchClampSeries__data required: true multivalued: false + inlined: true gain: name: gain description: Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt @@ -56,6 +58,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: IntracellularElectrode - range: string @@ -67,6 +70,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -92,6 +96,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -100,6 +105,7 @@ classes: range: CurrentClampSeries__data required: true multivalued: false + inlined: true bias_current: name: bias_current description: Bias current, in amps. @@ -126,6 +132,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -152,6 +159,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -188,6 +196,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -196,6 +205,7 @@ classes: range: CurrentClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true CurrentClampStimulusSeries__data: name: CurrentClampStimulusSeries__data @@ -204,6 +214,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -229,6 +240,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -237,48 +249,56 @@ classes: range: VoltageClampSeries__data required: true multivalued: false + inlined: true capacitance_fast: name: capacitance_fast description: Fast capacitance, in farads. range: VoltageClampSeries__capacitance_fast required: false multivalued: false + inlined: true capacitance_slow: name: capacitance_slow description: Slow capacitance, in farads. range: VoltageClampSeries__capacitance_slow required: false multivalued: false + inlined: true resistance_comp_bandwidth: name: resistance_comp_bandwidth description: Resistance compensation bandwidth, in hertz. range: VoltageClampSeries__resistance_comp_bandwidth required: false multivalued: false + inlined: true resistance_comp_correction: name: resistance_comp_correction description: Resistance compensation correction, in percent. range: VoltageClampSeries__resistance_comp_correction required: false multivalued: false + inlined: true resistance_comp_prediction: name: resistance_comp_prediction description: Resistance compensation prediction, in percent. range: VoltageClampSeries__resistance_comp_prediction required: false multivalued: false + inlined: true whole_cell_capacitance_comp: name: whole_cell_capacitance_comp description: Whole cell capacitance compensation, in farads. range: VoltageClampSeries__whole_cell_capacitance_comp required: false multivalued: false + inlined: true whole_cell_series_resistance_comp: name: whole_cell_series_resistance_comp description: Whole cell series resistance compensation, in ohms. range: VoltageClampSeries__whole_cell_series_resistance_comp required: false multivalued: false + inlined: true tree_root: true VoltageClampSeries__data: name: VoltageClampSeries__data @@ -287,6 +307,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -310,6 +331,7 @@ classes: name: name: name ifabsent: string(capacitance_fast) + identifier: true range: string required: true equals_string: capacitance_fast @@ -331,6 +353,7 @@ classes: name: name: name ifabsent: string(capacitance_slow) + identifier: true range: string required: true equals_string: capacitance_slow @@ -352,6 +375,7 @@ classes: name: name: name ifabsent: string(resistance_comp_bandwidth) + identifier: true range: string required: true equals_string: resistance_comp_bandwidth @@ -374,6 +398,7 @@ classes: name: name: name ifabsent: string(resistance_comp_correction) + identifier: true range: string required: true equals_string: resistance_comp_correction @@ -396,6 +421,7 @@ classes: name: name: name ifabsent: string(resistance_comp_prediction) + identifier: true range: string required: true equals_string: resistance_comp_prediction @@ -418,6 +444,7 @@ classes: name: name: name ifabsent: string(whole_cell_capacitance_comp) + identifier: true range: string required: true equals_string: whole_cell_capacitance_comp @@ -440,6 +467,7 @@ classes: name: name: name ifabsent: string(whole_cell_series_resistance_comp) + identifier: true range: string required: true equals_string: whole_cell_series_resistance_comp @@ -462,6 +490,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -470,6 +499,7 @@ classes: range: VoltageClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true VoltageClampStimulusSeries__data: name: VoltageClampStimulusSeries__data @@ -478,6 +508,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -501,6 +532,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -555,6 +587,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -569,6 +602,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true sweep_number: @@ -583,9 +617,13 @@ classes: series: name: series description: The PatchClampSeries with the sweep number in that row. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: PatchClampSeries required: true - multivalued: true + multivalued: false + inlined: true series_index: name: series_index annotations: @@ -599,6 +637,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true IntracellularElectrodesTable: name: IntracellularElectrodesTable @@ -607,6 +646,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -619,9 +659,13 @@ classes: electrode: name: electrode description: Column for storing the reference to the intracellular electrode. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: IntracellularElectrode required: true - multivalued: true + multivalued: false + inlined: true tree_root: true IntracellularStimuliTable: name: IntracellularStimuliTable @@ -630,6 +674,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -653,6 +698,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularResponsesTable: name: IntracellularResponsesTable @@ -661,6 +707,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -684,6 +731,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularRecordingsTable: name: IntracellularRecordingsTable @@ -703,6 +751,7 @@ classes: name: name: name ifabsent: string(intracellular_recordings) + identifier: true range: string required: true equals_string: intracellular_recordings @@ -724,18 +773,24 @@ classes: range: IntracellularElectrodesTable required: true multivalued: false + inlined: true + inlined_as_list: false stimuli: name: stimuli description: Table for storing intracellular stimulus related metadata. range: IntracellularStimuliTable required: true multivalued: false + inlined: true + inlined_as_list: false responses: name: responses description: Table for storing intracellular response related metadata. range: IntracellularResponsesTable required: true multivalued: false + inlined: true + inlined_as_list: false tree_root: true SimultaneousRecordingsTable: name: SimultaneousRecordingsTable @@ -747,6 +802,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -757,6 +813,7 @@ classes: range: SimultaneousRecordingsTable__recordings required: true multivalued: false + inlined: true recordings_index: name: recordings_index annotations: @@ -770,6 +827,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true SimultaneousRecordingsTable__recordings: name: SimultaneousRecordingsTable__recordings @@ -780,6 +838,7 @@ classes: name: name: name ifabsent: string(recordings) + identifier: true range: string required: true equals_string: recordings @@ -790,6 +849,7 @@ classes: to fix the type of table that can be referenced here. range: IntracellularRecordingsTable required: true + inlined: true SequentialRecordingsTable: name: SequentialRecordingsTable description: A table for grouping different sequential recordings from the SimultaneousRecordingsTable @@ -801,6 +861,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -811,6 +872,7 @@ classes: range: SequentialRecordingsTable__simultaneous_recordings required: true multivalued: false + inlined: true simultaneous_recordings_index: name: simultaneous_recordings_index annotations: @@ -824,6 +886,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true stimulus_type: name: stimulus_type description: The type of stimulus used for the sequential recording. @@ -843,6 +906,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -853,6 +917,7 @@ classes: to fix the type of table that can be referenced here. range: SimultaneousRecordingsTable required: true + inlined: true RepetitionsTable: name: RepetitionsTable description: A table for grouping different sequential intracellular recordings @@ -864,6 +929,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -874,6 +940,7 @@ classes: range: RepetitionsTable__sequential_recordings required: true multivalued: false + inlined: true sequential_recordings_index: name: sequential_recordings_index annotations: @@ -887,6 +954,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true RepetitionsTable__sequential_recordings: name: RepetitionsTable__sequential_recordings @@ -897,6 +965,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -907,6 +976,7 @@ classes: to fix the type of table that can be referenced here. range: SequentialRecordingsTable required: true + inlined: true ExperimentalConditionsTable: name: ExperimentalConditionsTable description: A table for grouping different intracellular recording repetitions @@ -916,6 +986,7 @@ classes: name: name: name ifabsent: string(experimental_conditions) + identifier: true range: string required: true equals_string: experimental_conditions @@ -925,6 +996,7 @@ classes: range: ExperimentalConditionsTable__repetitions required: true multivalued: false + inlined: true repetitions_index: name: repetitions_index annotations: @@ -938,6 +1010,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true ExperimentalConditionsTable__repetitions: name: ExperimentalConditionsTable__repetitions @@ -947,6 +1020,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -957,3 +1031,4 @@ classes: to fix the type of table that can be referenced here. range: RepetitionsTable required: true + inlined: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.image.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.image.yaml index a72ef59..fec75ec 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.image.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.image.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -38,6 +39,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -57,6 +59,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -81,6 +84,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: range: ImageSeries__external_file required: false multivalued: false + inlined: true format: name: format description: Format of image. If this is 'external', then the attribute 'external_file' @@ -138,6 +143,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Device - range: string @@ -152,6 +158,7 @@ classes: name: name: name ifabsent: string(external_file) + identifier: true range: string required: true equals_string: external_file @@ -189,6 +196,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true masked_imageseries: @@ -199,6 +207,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -214,6 +223,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true distance: @@ -276,6 +286,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -295,6 +306,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.misc.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.misc.yaml index 97927d6..ec02fc4 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.misc.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.misc.yaml @@ -30,6 +30,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: AbstractFeatureSeries__data required: true multivalued: false + inlined: true feature_units: name: feature_units description: Units of each feature. @@ -64,6 +66,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -96,6 +99,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -140,6 +145,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -148,6 +154,7 @@ classes: range: DecompositionSeries__data required: true multivalued: false + inlined: true metric: name: metric description: The metric used, e.g. phase, amplitude, power. @@ -168,6 +175,7 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true bands: name: bands description: Table for describing the bands that this series was generated @@ -175,6 +183,8 @@ classes: range: DecompositionSeries__bands required: true multivalued: false + inlined: true + inlined_as_list: true source_timeseries: name: source_timeseries annotations: @@ -183,6 +193,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: TimeSeries - range: string @@ -194,6 +205,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -222,6 +234,7 @@ classes: name: name: name ifabsent: string(bands) + identifier: true range: string required: true equals_string: bands @@ -273,6 +286,7 @@ classes: name: name: name ifabsent: string(Units) + identifier: true range: string required: true spike_times_index: @@ -288,12 +302,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true spike_times: name: spike_times description: Spike times for each unit. range: Units__spike_times required: false multivalued: false + inlined: true obs_intervals_index: name: obs_intervals_index annotations: @@ -307,6 +323,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true obs_intervals: name: obs_intervals description: Observation intervals for each unit. @@ -331,6 +348,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true electrodes: name: electrodes annotations: @@ -344,12 +362,17 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true electrode_group: name: electrode_group description: Electrode group that each spike unit came from. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: false - multivalued: true + multivalued: false + inlined: true waveform_mean: name: waveform_mean description: Spike waveform mean for each spike unit. @@ -428,6 +451,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true waveforms_index_index: name: waveforms_index_index annotations: @@ -442,6 +466,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true Units__spike_times: name: Units__spike_times @@ -451,6 +476,7 @@ classes: name: name: name ifabsent: string(spike_times) + identifier: true range: string required: true equals_string: spike_times diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ogen.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ogen.yaml index 1add778..cbe1a6d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ogen.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ogen.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -40,6 +41,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: OptogeneticStimulusSite - range: string @@ -51,6 +53,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -81,6 +84,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ophys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ophys.yaml index c6215f1..aec8547 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ophys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.ophys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -60,6 +61,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -72,6 +74,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -102,6 +105,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true DfOverF: name: DfOverF @@ -156,15 +160,28 @@ classes: attributes: name: name: name + identifier: true range: string required: true image_mask: name: image_mask description: ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - range: PlaneSegmentation__image_mask + range: AnyType required: false multivalued: false + any_of: + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - alias: num_z pixel_mask_index: name: pixel_mask_index annotations: @@ -178,6 +195,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true pixel_mask: name: pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for @@ -186,6 +204,7 @@ classes: range: PlaneSegmentation__pixel_mask required: false multivalued: false + inlined: true voxel_mask_index: name: voxel_mask_index annotations: @@ -199,6 +218,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true voxel_mask: name: voxel_mask description: 'Voxel masks for each ROI: a list of indices and weights for @@ -207,6 +227,7 @@ classes: range: PlaneSegmentation__voxel_mask required: false multivalued: false + inlined: true reference_images: name: reference_images description: Image stacks that the segmentation masks apply to. @@ -223,22 +244,11 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string tree_root: true - PlaneSegmentation__image_mask: - name: PlaneSegmentation__image_mask - description: ROI masks for each ROI. Each image mask is the size of the original - imaging plane (or volume) and members of the ROI are finite non-zero. - is_a: VectorData - attributes: - name: - name: name - ifabsent: string(image_mask) - range: string - required: true - equals_string: image_mask PlaneSegmentation__pixel_mask: name: PlaneSegmentation__pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for the @@ -249,6 +259,7 @@ classes: name: name: name ifabsent: string(pixel_mask) + identifier: true range: string required: true equals_string: pixel_mask @@ -286,6 +297,7 @@ classes: name: name: name ifabsent: string(voxel_mask) + identifier: true range: string required: true equals_string: voxel_mask @@ -328,6 +340,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -371,6 +384,7 @@ classes: range: ImagingPlane__manifold required: false multivalued: false + inlined: true origin_coords: name: origin_coords description: Physical location of the first element of the imaging plane (0, @@ -379,6 +393,7 @@ classes: range: ImagingPlane__origin_coords required: false multivalued: false + inlined: true grid_spacing: name: grid_spacing description: Space between pixels in (x, y) or voxels in (x, y, z) directions, @@ -387,6 +402,7 @@ classes: range: ImagingPlane__grid_spacing required: false multivalued: false + inlined: true reference_frame: name: reference_frame description: Describes reference frame of origin_coords and grid_spacing. @@ -415,6 +431,8 @@ classes: range: OpticalChannel required: true multivalued: true + inlined: true + inlined_as_list: false device: name: device annotations: @@ -423,6 +441,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -436,6 +455,7 @@ classes: name: name: name ifabsent: string(manifold) + identifier: true range: string required: true equals_string: manifold @@ -485,6 +505,7 @@ classes: name: name: name ifabsent: string(origin_coords) + identifier: true range: string required: true equals_string: origin_coords @@ -515,6 +536,7 @@ classes: name: name: name ifabsent: string(grid_spacing) + identifier: true range: string required: true equals_string: grid_spacing @@ -543,6 +565,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -579,6 +602,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true corrected: @@ -587,6 +611,8 @@ classes: range: ImageSeries required: true multivalued: false + inlined: true + inlined_as_list: false xy_translation: name: xy_translation description: Stores the x,y delta necessary to align each frame to the common @@ -594,6 +620,8 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true + inlined_as_list: false original: name: original annotations: @@ -602,6 +630,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.retinotopy.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.retinotopy.yaml index f433f10..f30f06f 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.retinotopy.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_4_0/core.nwb.retinotopy.yaml @@ -29,6 +29,7 @@ classes: name: name: name ifabsent: string(ImagingRetinotopy) + identifier: true range: string required: true axis_1_phase_map: @@ -37,6 +38,7 @@ classes: range: ImagingRetinotopy__axis_1_phase_map required: true multivalued: false + inlined: true axis_1_power_map: name: axis_1_power_map description: Power response on the first measured axis. Response is scaled @@ -44,12 +46,14 @@ classes: range: ImagingRetinotopy__axis_1_power_map required: false multivalued: false + inlined: true axis_2_phase_map: name: axis_2_phase_map description: Phase response to stimulus on the second measured axis. range: ImagingRetinotopy__axis_2_phase_map required: true multivalued: false + inlined: true axis_2_power_map: name: axis_2_power_map description: Power response on the second measured axis. Response is scaled @@ -57,6 +61,7 @@ classes: range: ImagingRetinotopy__axis_2_power_map required: false multivalued: false + inlined: true axis_descriptions: name: axis_descriptions description: Two-element array describing the contents of the two response @@ -76,6 +81,7 @@ classes: range: ImagingRetinotopy__focal_depth_image required: false multivalued: false + inlined: true sign_map: name: sign_map description: Sine of the angle between the direction of the gradient in axis_1 @@ -83,6 +89,7 @@ classes: range: ImagingRetinotopy__sign_map required: false multivalued: false + inlined: true vasculature_image: name: vasculature_image description: 'Gray-scale anatomical image of cortical surface. Array structure: @@ -90,6 +97,7 @@ classes: range: ImagingRetinotopy__vasculature_image required: true multivalued: false + inlined: true tree_root: true ImagingRetinotopy__axis_1_phase_map: name: ImagingRetinotopy__axis_1_phase_map @@ -98,6 +106,7 @@ classes: name: name: name ifabsent: string(axis_1_phase_map) + identifier: true range: string required: true equals_string: axis_1_phase_map @@ -134,6 +143,7 @@ classes: name: name: name ifabsent: string(axis_1_power_map) + identifier: true range: string required: true equals_string: axis_1_power_map @@ -169,6 +179,7 @@ classes: name: name: name ifabsent: string(axis_2_phase_map) + identifier: true range: string required: true equals_string: axis_2_phase_map @@ -205,6 +216,7 @@ classes: name: name: name ifabsent: string(axis_2_power_map) + identifier: true range: string required: true equals_string: axis_2_power_map @@ -241,6 +253,7 @@ classes: name: name: name ifabsent: string(focal_depth_image) + identifier: true range: string required: true equals_string: focal_depth_image @@ -288,6 +301,7 @@ classes: name: name: name ifabsent: string(sign_map) + identifier: true range: string required: true equals_string: sign_map @@ -319,6 +333,7 @@ classes: name: name: name ifabsent: string(vasculature_image) + identifier: true range: string required: true equals_string: vasculature_image diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.base.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.base.yaml index 373ff4d..547dd4c 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.base.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -34,6 +35,7 @@ classes: name: name: name ifabsent: string(timeseries) + identifier: true range: string required: true idx_start: @@ -63,6 +65,7 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true tree_root: true Image: name: Image @@ -73,6 +76,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true resolution: @@ -113,6 +117,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -125,6 +130,8 @@ classes: range: Image required: true multivalued: true + inlined: true + inlined_as_list: true tree_root: true NWBContainer: name: NWBContainer @@ -134,6 +141,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -145,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -155,6 +164,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -180,6 +190,7 @@ classes: range: TimeSeries__data required: true multivalued: false + inlined: true starting_time: name: starting_time description: Timestamp of the first sample in seconds. When timestamps are @@ -188,6 +199,7 @@ classes: range: TimeSeries__starting_time required: false multivalued: false + inlined: true timestamps: name: timestamps description: Timestamps for samples stored in data, in seconds, relative to @@ -231,6 +243,8 @@ classes: range: TimeSeries__sync required: false multivalued: false + inlined: true + inlined_as_list: true tree_root: true TimeSeries__data: name: TimeSeries__data @@ -241,6 +255,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -327,6 +342,7 @@ classes: name: name: name ifabsent: string(starting_time) + identifier: true range: string required: true equals_string: starting_time @@ -358,6 +374,7 @@ classes: name: name: name ifabsent: string(sync) + identifier: true range: string required: true equals_string: sync @@ -384,6 +401,7 @@ classes: name: name: name ifabsent: string(Images) + identifier: true range: string required: true description: @@ -413,4 +431,5 @@ classes: range: ImageReferences required: false multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.behavior.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.behavior.yaml index fd2c46f..94ff5f8 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.behavior.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.behavior.yaml @@ -29,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: SpatialSeries__data required: true multivalued: false + inlined: true reference_frame: name: reference_frame description: Description defining what exactly 'straight-ahead' means. @@ -53,6 +55,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.device.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.device.yaml index 3f1acc9..d2ec1a5 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.device.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.device.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ecephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ecephys.yaml index ce256eb..b611d74 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ecephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ecephys.yaml @@ -25,6 +25,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true filtering: @@ -71,6 +72,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true channel_conversion: name: channel_conversion description: Channel-specific conversion factor. Multiply the data in the @@ -103,6 +105,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -143,6 +146,7 @@ classes: name: name: name ifabsent: string(FeatureExtraction) + identifier: true range: string required: true description: @@ -189,6 +193,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true EventDetection: name: EventDetection @@ -198,6 +203,7 @@ classes: name: name: name ifabsent: string(EventDetection) + identifier: true range: string required: true detection_method: @@ -236,6 +242,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ElectricalSeries - range: string @@ -297,6 +304,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -317,6 +325,7 @@ classes: range: ElectrodeGroup__position required: false multivalued: false + inlined: true device: name: device annotations: @@ -325,6 +334,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -336,6 +346,7 @@ classes: name: name: name ifabsent: string(position) + identifier: true range: string required: true equals_string: position @@ -376,6 +387,7 @@ classes: name: name: name ifabsent: string(ClusterWaveforms) + identifier: true range: string required: true waveform_filtering: @@ -416,6 +428,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Clustering - range: string @@ -429,6 +442,7 @@ classes: name: name: name ifabsent: string(Clustering) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.epoch.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.epoch.yaml index 3764b00..9857394 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.epoch.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.epoch.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true start_time: @@ -64,6 +65,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true timeseries: name: timeseries annotations: @@ -77,6 +79,7 @@ classes: range: TimeSeriesReferenceVectorData required: false multivalued: false + inlined: true timeseries_index: name: timeseries_index annotations: @@ -90,4 +93,5 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.file.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.file.yaml index f468049..01ef5b5 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.file.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.file.yaml @@ -28,6 +28,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true notes: @@ -45,6 +46,7 @@ classes: name: name: name ifabsent: string(root) + identifier: true range: string required: true equals_string: root @@ -184,6 +186,8 @@ classes: range: NWBFile__stimulus required: true multivalued: false + inlined: true + inlined_as_list: true general: name: general description: Experimental metadata, including protocol, notes and description @@ -204,6 +208,8 @@ classes: range: NWBFile__general required: true multivalued: false + inlined: true + inlined_as_list: true intervals: name: intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -213,12 +219,16 @@ classes: range: NWBFile__intervals required: false multivalued: false + inlined: true + inlined_as_list: true units: name: units description: Data about sorted spike units. range: Units required: false multivalued: false + inlined: true + inlined_as_list: false tree_root: true NWBFile__stimulus: name: NWBFile__stimulus @@ -238,6 +248,7 @@ classes: name: name: name ifabsent: string(stimulus) + identifier: true range: string required: true equals_string: stimulus @@ -281,6 +292,7 @@ classes: name: name: name ifabsent: string(general) + identifier: true range: string required: true equals_string: general @@ -376,6 +388,7 @@ classes: range: general__source_script required: false multivalued: false + inlined: true stimulus: name: stimulus description: Notes about stimuli, such as how and where they were presented. @@ -403,6 +416,8 @@ classes: range: LabMetaData required: false multivalued: true + inlined: true + inlined_as_list: false devices: name: devices description: Description of hardware devices used during experiment, e.g., @@ -419,18 +434,24 @@ classes: range: Subject required: false multivalued: false + inlined: true + inlined_as_list: false extracellular_ephys: name: extracellular_ephys description: Metadata related to extracellular electrophysiology. range: general__extracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true intracellular_ephys: name: intracellular_ephys description: Metadata related to intracellular electrophysiology. range: general__intracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true optogenetics: name: optogenetics description: Metadata describing optogenetic stimuluation. @@ -455,6 +476,7 @@ classes: name: name: name ifabsent: string(source_script) + identifier: true range: string required: true equals_string: source_script @@ -474,6 +496,7 @@ classes: name: name: name ifabsent: string(extracellular_ephys) + identifier: true range: string required: true equals_string: extracellular_ephys @@ -483,12 +506,16 @@ classes: range: ElectrodeGroup required: false multivalued: true + inlined: true + inlined_as_list: false electrodes: name: electrodes description: A table of all electrodes (i.e. channels) used for recording. range: extracellular_ephys__electrodes required: false multivalued: false + inlined: true + inlined_as_list: true extracellular_ephys__electrodes: name: extracellular_ephys__electrodes description: A table of all electrodes (i.e. channels) used for recording. @@ -497,6 +524,7 @@ classes: name: name: name ifabsent: string(electrodes) + identifier: true range: string required: true equals_string: electrodes @@ -560,9 +588,13 @@ classes: group: name: group description: Reference to the ElectrodeGroup this electrode is a part of. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: true - multivalued: true + multivalued: false + inlined: true group_name: name: group_name description: Name of the ElectrodeGroup this electrode is a part of. @@ -617,6 +649,7 @@ classes: name: name: name ifabsent: string(intracellular_ephys) + identifier: true range: string required: true equals_string: intracellular_ephys @@ -635,6 +668,8 @@ classes: range: IntracellularElectrode required: false multivalued: true + inlined: true + inlined_as_list: false sweep_table: name: sweep_table description: '[DEPRECATED] Table used to group different PatchClampSeries. @@ -644,6 +679,8 @@ classes: range: SweepTable required: false multivalued: false + inlined: true + inlined_as_list: false intracellular_recordings: name: intracellular_recordings description: A table to group together a stimulus and response from a single @@ -661,6 +698,8 @@ classes: range: IntracellularRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false simultaneous_recordings: name: simultaneous_recordings description: A table for grouping different intracellular recordings from @@ -669,6 +708,8 @@ classes: range: SimultaneousRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false sequential_recordings: name: sequential_recordings description: A table for grouping different sequential recordings from the @@ -678,6 +719,8 @@ classes: range: SequentialRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false repetitions: name: repetitions description: A table for grouping different sequential intracellular recordings @@ -687,6 +730,8 @@ classes: range: RepetitionsTable required: false multivalued: false + inlined: true + inlined_as_list: false experimental_conditions: name: experimental_conditions description: A table for grouping different intracellular recording repetitions @@ -694,6 +739,8 @@ classes: range: ExperimentalConditionsTable required: false multivalued: false + inlined: true + inlined_as_list: false NWBFile__intervals: name: NWBFile__intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -703,6 +750,7 @@ classes: name: name: name ifabsent: string(intervals) + identifier: true range: string required: true equals_string: intervals @@ -713,18 +761,24 @@ classes: range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false trials: name: trials description: Repeated experimental events that have a logical grouping. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false invalid_times: name: invalid_times description: Time intervals that should be removed from analysis. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false time_intervals: name: time_intervals description: Optional additional table(s) for describing other experimental @@ -732,6 +786,8 @@ classes: range: TimeIntervals required: false multivalued: true + inlined: true + inlined_as_list: false LabMetaData: name: LabMetaData description: Lab-specific meta-data. @@ -739,6 +795,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -749,6 +806,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true age: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.icephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.icephys.yaml index bdd9dd5..257b07b 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.icephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.icephys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -41,6 +42,7 @@ classes: range: PatchClampSeries__data required: true multivalued: false + inlined: true gain: name: gain description: Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt @@ -56,6 +58,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: IntracellularElectrode - range: string @@ -67,6 +70,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -92,6 +96,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -100,6 +105,7 @@ classes: range: CurrentClampSeries__data required: true multivalued: false + inlined: true bias_current: name: bias_current description: Bias current, in amps. @@ -126,6 +132,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -153,6 +160,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -189,6 +197,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -197,6 +206,7 @@ classes: range: CurrentClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true CurrentClampStimulusSeries__data: name: CurrentClampStimulusSeries__data @@ -205,6 +215,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -231,6 +242,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -239,48 +251,56 @@ classes: range: VoltageClampSeries__data required: true multivalued: false + inlined: true capacitance_fast: name: capacitance_fast description: Fast capacitance, in farads. range: VoltageClampSeries__capacitance_fast required: false multivalued: false + inlined: true capacitance_slow: name: capacitance_slow description: Slow capacitance, in farads. range: VoltageClampSeries__capacitance_slow required: false multivalued: false + inlined: true resistance_comp_bandwidth: name: resistance_comp_bandwidth description: Resistance compensation bandwidth, in hertz. range: VoltageClampSeries__resistance_comp_bandwidth required: false multivalued: false + inlined: true resistance_comp_correction: name: resistance_comp_correction description: Resistance compensation correction, in percent. range: VoltageClampSeries__resistance_comp_correction required: false multivalued: false + inlined: true resistance_comp_prediction: name: resistance_comp_prediction description: Resistance compensation prediction, in percent. range: VoltageClampSeries__resistance_comp_prediction required: false multivalued: false + inlined: true whole_cell_capacitance_comp: name: whole_cell_capacitance_comp description: Whole cell capacitance compensation, in farads. range: VoltageClampSeries__whole_cell_capacitance_comp required: false multivalued: false + inlined: true whole_cell_series_resistance_comp: name: whole_cell_series_resistance_comp description: Whole cell series resistance compensation, in ohms. range: VoltageClampSeries__whole_cell_series_resistance_comp required: false multivalued: false + inlined: true tree_root: true VoltageClampSeries__data: name: VoltageClampSeries__data @@ -289,6 +309,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -313,6 +334,7 @@ classes: name: name: name ifabsent: string(capacitance_fast) + identifier: true range: string required: true equals_string: capacitance_fast @@ -334,6 +356,7 @@ classes: name: name: name ifabsent: string(capacitance_slow) + identifier: true range: string required: true equals_string: capacitance_slow @@ -355,6 +378,7 @@ classes: name: name: name ifabsent: string(resistance_comp_bandwidth) + identifier: true range: string required: true equals_string: resistance_comp_bandwidth @@ -377,6 +401,7 @@ classes: name: name: name ifabsent: string(resistance_comp_correction) + identifier: true range: string required: true equals_string: resistance_comp_correction @@ -399,6 +424,7 @@ classes: name: name: name ifabsent: string(resistance_comp_prediction) + identifier: true range: string required: true equals_string: resistance_comp_prediction @@ -421,6 +447,7 @@ classes: name: name: name ifabsent: string(whole_cell_capacitance_comp) + identifier: true range: string required: true equals_string: whole_cell_capacitance_comp @@ -443,6 +470,7 @@ classes: name: name: name ifabsent: string(whole_cell_series_resistance_comp) + identifier: true range: string required: true equals_string: whole_cell_series_resistance_comp @@ -465,6 +493,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -473,6 +502,7 @@ classes: range: VoltageClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true VoltageClampStimulusSeries__data: name: VoltageClampStimulusSeries__data @@ -481,6 +511,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -505,6 +536,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true cell_id: @@ -565,6 +597,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -579,6 +612,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true sweep_number: @@ -593,9 +627,13 @@ classes: series: name: series description: The PatchClampSeries with the sweep number in that row. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: PatchClampSeries required: true - multivalued: true + multivalued: false + inlined: true series_index: name: series_index annotations: @@ -609,6 +647,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true IntracellularElectrodesTable: name: IntracellularElectrodesTable @@ -617,6 +656,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -629,9 +669,13 @@ classes: electrode: name: electrode description: Column for storing the reference to the intracellular electrode. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: IntracellularElectrode required: true - multivalued: true + multivalued: false + inlined: true tree_root: true IntracellularStimuliTable: name: IntracellularStimuliTable @@ -640,6 +684,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -663,6 +708,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularResponsesTable: name: IntracellularResponsesTable @@ -671,6 +717,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -694,6 +741,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularRecordingsTable: name: IntracellularRecordingsTable @@ -713,6 +761,7 @@ classes: name: name: name ifabsent: string(intracellular_recordings) + identifier: true range: string required: true equals_string: intracellular_recordings @@ -734,18 +783,24 @@ classes: range: IntracellularElectrodesTable required: true multivalued: false + inlined: true + inlined_as_list: false stimuli: name: stimuli description: Table for storing intracellular stimulus related metadata. range: IntracellularStimuliTable required: true multivalued: false + inlined: true + inlined_as_list: false responses: name: responses description: Table for storing intracellular response related metadata. range: IntracellularResponsesTable required: true multivalued: false + inlined: true + inlined_as_list: false tree_root: true SimultaneousRecordingsTable: name: SimultaneousRecordingsTable @@ -757,6 +812,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -767,6 +823,7 @@ classes: range: SimultaneousRecordingsTable__recordings required: true multivalued: false + inlined: true recordings_index: name: recordings_index annotations: @@ -780,6 +837,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true SimultaneousRecordingsTable__recordings: name: SimultaneousRecordingsTable__recordings @@ -790,6 +848,7 @@ classes: name: name: name ifabsent: string(recordings) + identifier: true range: string required: true equals_string: recordings @@ -800,6 +859,7 @@ classes: to fix the type of table that can be referenced here. range: IntracellularRecordingsTable required: true + inlined: true SequentialRecordingsTable: name: SequentialRecordingsTable description: A table for grouping different sequential recordings from the SimultaneousRecordingsTable @@ -811,6 +871,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -821,6 +882,7 @@ classes: range: SequentialRecordingsTable__simultaneous_recordings required: true multivalued: false + inlined: true simultaneous_recordings_index: name: simultaneous_recordings_index annotations: @@ -834,6 +896,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true stimulus_type: name: stimulus_type description: The type of stimulus used for the sequential recording. @@ -853,6 +916,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -863,6 +927,7 @@ classes: to fix the type of table that can be referenced here. range: SimultaneousRecordingsTable required: true + inlined: true RepetitionsTable: name: RepetitionsTable description: A table for grouping different sequential intracellular recordings @@ -874,6 +939,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -884,6 +950,7 @@ classes: range: RepetitionsTable__sequential_recordings required: true multivalued: false + inlined: true sequential_recordings_index: name: sequential_recordings_index annotations: @@ -897,6 +964,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true RepetitionsTable__sequential_recordings: name: RepetitionsTable__sequential_recordings @@ -907,6 +975,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -917,6 +986,7 @@ classes: to fix the type of table that can be referenced here. range: SequentialRecordingsTable required: true + inlined: true ExperimentalConditionsTable: name: ExperimentalConditionsTable description: A table for grouping different intracellular recording repetitions @@ -926,6 +996,7 @@ classes: name: name: name ifabsent: string(experimental_conditions) + identifier: true range: string required: true equals_string: experimental_conditions @@ -935,6 +1006,7 @@ classes: range: ExperimentalConditionsTable__repetitions required: true multivalued: false + inlined: true repetitions_index: name: repetitions_index annotations: @@ -948,6 +1020,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true ExperimentalConditionsTable__repetitions: name: ExperimentalConditionsTable__repetitions @@ -957,6 +1030,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -967,3 +1041,4 @@ classes: to fix the type of table that can be referenced here. range: RepetitionsTable required: true + inlined: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.image.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.image.yaml index b6ca2c0..dd4d2f4 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.image.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.image.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -38,6 +39,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -57,6 +59,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -81,6 +84,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: range: ImageSeries__external_file required: false multivalued: false + inlined: true format: name: format description: Format of image. If this is 'external', then the attribute 'external_file' @@ -138,6 +143,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Device - range: string @@ -152,6 +158,7 @@ classes: name: name: name ifabsent: string(external_file) + identifier: true range: string required: true equals_string: external_file @@ -189,6 +196,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true masked_imageseries: @@ -199,6 +207,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -214,6 +223,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true distance: @@ -277,6 +287,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -297,6 +308,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -308,6 +320,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Images - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.misc.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.misc.yaml index f663994..5bfeb44 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.misc.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.misc.yaml @@ -30,6 +30,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: AbstractFeatureSeries__data required: true multivalued: false + inlined: true feature_units: name: feature_units description: Units of each feature. @@ -64,6 +66,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -96,6 +99,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -140,6 +145,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -148,6 +154,7 @@ classes: range: DecompositionSeries__data required: true multivalued: false + inlined: true metric: name: metric description: The metric used, e.g. phase, amplitude, power. @@ -168,6 +175,7 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true bands: name: bands description: Table for describing the bands that this series was generated @@ -175,6 +183,8 @@ classes: range: DecompositionSeries__bands required: true multivalued: false + inlined: true + inlined_as_list: true source_timeseries: name: source_timeseries annotations: @@ -183,6 +193,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: TimeSeries - range: string @@ -194,6 +205,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -222,6 +234,7 @@ classes: name: name: name ifabsent: string(bands) + identifier: true range: string required: true equals_string: bands @@ -273,6 +286,7 @@ classes: name: name: name ifabsent: string(Units) + identifier: true range: string required: true spike_times_index: @@ -288,12 +302,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true spike_times: name: spike_times description: Spike times for each unit. range: Units__spike_times required: false multivalued: false + inlined: true obs_intervals_index: name: obs_intervals_index annotations: @@ -307,6 +323,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true obs_intervals: name: obs_intervals description: Observation intervals for each unit. @@ -331,6 +348,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true electrodes: name: electrodes annotations: @@ -344,12 +362,17 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true electrode_group: name: electrode_group description: Electrode group that each spike unit came from. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: false - multivalued: true + multivalued: false + inlined: true waveform_mean: name: waveform_mean description: Spike waveform mean for each spike unit. @@ -428,6 +451,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true waveforms_index_index: name: waveforms_index_index annotations: @@ -442,6 +466,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true Units__spike_times: name: Units__spike_times @@ -451,6 +476,7 @@ classes: name: name: name ifabsent: string(spike_times) + identifier: true range: string required: true equals_string: spike_times diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ogen.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ogen.yaml index adadc3e..8c6b076 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ogen.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ogen.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -40,6 +41,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: OptogeneticStimulusSite - range: string @@ -51,6 +53,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -81,6 +84,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ophys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ophys.yaml index 9cd8b1e..17bb442 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ophys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.ophys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -60,6 +61,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -72,6 +74,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -102,6 +105,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true DfOverF: name: DfOverF @@ -156,15 +160,28 @@ classes: attributes: name: name: name + identifier: true range: string required: true image_mask: name: image_mask description: ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - range: PlaneSegmentation__image_mask + range: AnyType required: false multivalued: false + any_of: + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - alias: num_z pixel_mask_index: name: pixel_mask_index annotations: @@ -178,6 +195,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true pixel_mask: name: pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for @@ -186,6 +204,7 @@ classes: range: PlaneSegmentation__pixel_mask required: false multivalued: false + inlined: true voxel_mask_index: name: voxel_mask_index annotations: @@ -199,6 +218,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true voxel_mask: name: voxel_mask description: 'Voxel masks for each ROI: a list of indices and weights for @@ -207,6 +227,7 @@ classes: range: PlaneSegmentation__voxel_mask required: false multivalued: false + inlined: true reference_images: name: reference_images description: Image stacks that the segmentation masks apply to. @@ -223,22 +244,11 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string tree_root: true - PlaneSegmentation__image_mask: - name: PlaneSegmentation__image_mask - description: ROI masks for each ROI. Each image mask is the size of the original - imaging plane (or volume) and members of the ROI are finite non-zero. - is_a: VectorData - attributes: - name: - name: name - ifabsent: string(image_mask) - range: string - required: true - equals_string: image_mask PlaneSegmentation__pixel_mask: name: PlaneSegmentation__pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for the @@ -249,6 +259,7 @@ classes: name: name: name ifabsent: string(pixel_mask) + identifier: true range: string required: true equals_string: pixel_mask @@ -286,6 +297,7 @@ classes: name: name: name ifabsent: string(voxel_mask) + identifier: true range: string required: true equals_string: voxel_mask @@ -328,6 +340,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -371,6 +384,7 @@ classes: range: ImagingPlane__manifold required: false multivalued: false + inlined: true origin_coords: name: origin_coords description: Physical location of the first element of the imaging plane (0, @@ -379,6 +393,7 @@ classes: range: ImagingPlane__origin_coords required: false multivalued: false + inlined: true grid_spacing: name: grid_spacing description: Space between pixels in (x, y) or voxels in (x, y, z) directions, @@ -387,6 +402,7 @@ classes: range: ImagingPlane__grid_spacing required: false multivalued: false + inlined: true reference_frame: name: reference_frame description: Describes reference frame of origin_coords and grid_spacing. @@ -415,6 +431,8 @@ classes: range: OpticalChannel required: true multivalued: true + inlined: true + inlined_as_list: false device: name: device annotations: @@ -423,6 +441,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -436,6 +455,7 @@ classes: name: name: name ifabsent: string(manifold) + identifier: true range: string required: true equals_string: manifold @@ -485,6 +505,7 @@ classes: name: name: name ifabsent: string(origin_coords) + identifier: true range: string required: true equals_string: origin_coords @@ -515,6 +536,7 @@ classes: name: name: name ifabsent: string(grid_spacing) + identifier: true range: string required: true equals_string: grid_spacing @@ -543,6 +565,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -579,6 +602,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true corrected: @@ -587,6 +611,8 @@ classes: range: ImageSeries required: true multivalued: false + inlined: true + inlined_as_list: false xy_translation: name: xy_translation description: Stores the x,y delta necessary to align each frame to the common @@ -594,6 +620,8 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true + inlined_as_list: false original: name: original annotations: @@ -602,6 +630,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.retinotopy.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.retinotopy.yaml index 3a624b1..26b6ed6 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.retinotopy.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_5_0/core.nwb.retinotopy.yaml @@ -29,6 +29,7 @@ classes: name: name: name ifabsent: string(ImagingRetinotopy) + identifier: true range: string required: true axis_1_phase_map: @@ -37,6 +38,7 @@ classes: range: ImagingRetinotopy__axis_1_phase_map required: true multivalued: false + inlined: true axis_1_power_map: name: axis_1_power_map description: Power response on the first measured axis. Response is scaled @@ -44,12 +46,14 @@ classes: range: ImagingRetinotopy__axis_1_power_map required: false multivalued: false + inlined: true axis_2_phase_map: name: axis_2_phase_map description: Phase response to stimulus on the second measured axis. range: ImagingRetinotopy__axis_2_phase_map required: true multivalued: false + inlined: true axis_2_power_map: name: axis_2_power_map description: Power response on the second measured axis. Response is scaled @@ -57,6 +61,7 @@ classes: range: ImagingRetinotopy__axis_2_power_map required: false multivalued: false + inlined: true axis_descriptions: name: axis_descriptions description: Two-element array describing the contents of the two response @@ -76,6 +81,7 @@ classes: range: ImagingRetinotopy__focal_depth_image required: false multivalued: false + inlined: true sign_map: name: sign_map description: Sine of the angle between the direction of the gradient in axis_1 @@ -83,6 +89,7 @@ classes: range: ImagingRetinotopy__sign_map required: false multivalued: false + inlined: true vasculature_image: name: vasculature_image description: 'Gray-scale anatomical image of cortical surface. Array structure: @@ -90,6 +97,7 @@ classes: range: ImagingRetinotopy__vasculature_image required: true multivalued: false + inlined: true tree_root: true ImagingRetinotopy__axis_1_phase_map: name: ImagingRetinotopy__axis_1_phase_map @@ -98,6 +106,7 @@ classes: name: name: name ifabsent: string(axis_1_phase_map) + identifier: true range: string required: true equals_string: axis_1_phase_map @@ -134,6 +143,7 @@ classes: name: name: name ifabsent: string(axis_1_power_map) + identifier: true range: string required: true equals_string: axis_1_power_map @@ -169,6 +179,7 @@ classes: name: name: name ifabsent: string(axis_2_phase_map) + identifier: true range: string required: true equals_string: axis_2_phase_map @@ -205,6 +216,7 @@ classes: name: name: name ifabsent: string(axis_2_power_map) + identifier: true range: string required: true equals_string: axis_2_power_map @@ -241,6 +253,7 @@ classes: name: name: name ifabsent: string(focal_depth_image) + identifier: true range: string required: true equals_string: focal_depth_image @@ -288,6 +301,7 @@ classes: name: name: name ifabsent: string(sign_map) + identifier: true range: string required: true equals_string: sign_map @@ -319,6 +333,7 @@ classes: name: name: name ifabsent: string(vasculature_image) + identifier: true range: string required: true equals_string: vasculature_image diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.base.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.base.yaml index bae736e..9aeec32 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.base.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -34,6 +35,7 @@ classes: name: name: name ifabsent: string(timeseries) + identifier: true range: string required: true idx_start: @@ -63,6 +65,7 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true tree_root: true Image: name: Image @@ -73,6 +76,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true resolution: @@ -113,6 +117,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -125,6 +130,8 @@ classes: range: Image required: true multivalued: true + inlined: true + inlined_as_list: true tree_root: true NWBContainer: name: NWBContainer @@ -134,6 +141,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -145,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -155,6 +164,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -180,6 +190,7 @@ classes: range: TimeSeries__data required: true multivalued: false + inlined: true starting_time: name: starting_time description: Timestamp of the first sample in seconds. When timestamps are @@ -188,6 +199,7 @@ classes: range: TimeSeries__starting_time required: false multivalued: false + inlined: true timestamps: name: timestamps description: Timestamps for samples stored in data, in seconds, relative to @@ -231,6 +243,8 @@ classes: range: TimeSeries__sync required: false multivalued: false + inlined: true + inlined_as_list: true tree_root: true TimeSeries__data: name: TimeSeries__data @@ -241,6 +255,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -327,6 +342,7 @@ classes: name: name: name ifabsent: string(starting_time) + identifier: true range: string required: true equals_string: starting_time @@ -358,6 +374,7 @@ classes: name: name: name ifabsent: string(sync) + identifier: true range: string required: true equals_string: sync @@ -384,6 +401,7 @@ classes: name: name: name ifabsent: string(Images) + identifier: true range: string required: true description: @@ -413,4 +431,5 @@ classes: range: ImageReferences required: false multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.behavior.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.behavior.yaml index 0f6f89e..9d96389 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.behavior.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.behavior.yaml @@ -29,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: SpatialSeries__data required: true multivalued: false + inlined: true reference_frame: name: reference_frame description: Description defining what exactly 'straight-ahead' means. @@ -53,6 +55,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.device.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.device.yaml index 4dd254b..f41ac54 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.device.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.device.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ecephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ecephys.yaml index d63fc10..6fba341 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ecephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ecephys.yaml @@ -25,6 +25,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true filtering: @@ -71,6 +72,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true channel_conversion: name: channel_conversion description: Channel-specific conversion factor. Multiply the data in the @@ -103,6 +105,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -143,6 +146,7 @@ classes: name: name: name ifabsent: string(FeatureExtraction) + identifier: true range: string required: true description: @@ -189,6 +193,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true EventDetection: name: EventDetection @@ -198,6 +203,7 @@ classes: name: name: name ifabsent: string(EventDetection) + identifier: true range: string required: true detection_method: @@ -236,6 +242,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ElectricalSeries - range: string @@ -297,6 +304,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -317,6 +325,7 @@ classes: range: ElectrodeGroup__position required: false multivalued: false + inlined: true device: name: device annotations: @@ -325,6 +334,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -336,6 +346,7 @@ classes: name: name: name ifabsent: string(position) + identifier: true range: string required: true equals_string: position @@ -376,6 +387,7 @@ classes: name: name: name ifabsent: string(ClusterWaveforms) + identifier: true range: string required: true waveform_filtering: @@ -416,6 +428,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Clustering - range: string @@ -429,6 +442,7 @@ classes: name: name: name ifabsent: string(Clustering) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.epoch.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.epoch.yaml index fb0df61..0a9685b 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.epoch.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.epoch.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true start_time: @@ -64,6 +65,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true timeseries: name: timeseries annotations: @@ -77,6 +79,7 @@ classes: range: TimeSeriesReferenceVectorData required: false multivalued: false + inlined: true timeseries_index: name: timeseries_index annotations: @@ -90,4 +93,5 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.file.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.file.yaml index f5d5d49..481256f 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.file.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.file.yaml @@ -28,6 +28,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true notes: @@ -45,6 +46,7 @@ classes: name: name: name ifabsent: string(root) + identifier: true range: string required: true equals_string: root @@ -184,6 +186,8 @@ classes: range: NWBFile__stimulus required: true multivalued: false + inlined: true + inlined_as_list: true general: name: general description: Experimental metadata, including protocol, notes and description @@ -204,6 +208,8 @@ classes: range: NWBFile__general required: true multivalued: false + inlined: true + inlined_as_list: true intervals: name: intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -213,12 +219,16 @@ classes: range: NWBFile__intervals required: false multivalued: false + inlined: true + inlined_as_list: true units: name: units description: Data about sorted spike units. range: Units required: false multivalued: false + inlined: true + inlined_as_list: false tree_root: true NWBFile__stimulus: name: NWBFile__stimulus @@ -238,6 +248,7 @@ classes: name: name: name ifabsent: string(stimulus) + identifier: true range: string required: true equals_string: stimulus @@ -281,6 +292,7 @@ classes: name: name: name ifabsent: string(general) + identifier: true range: string required: true equals_string: general @@ -376,6 +388,7 @@ classes: range: general__source_script required: false multivalued: false + inlined: true stimulus: name: stimulus description: Notes about stimuli, such as how and where they were presented. @@ -403,6 +416,8 @@ classes: range: LabMetaData required: false multivalued: true + inlined: true + inlined_as_list: false devices: name: devices description: Description of hardware devices used during experiment, e.g., @@ -419,18 +434,24 @@ classes: range: Subject required: false multivalued: false + inlined: true + inlined_as_list: false extracellular_ephys: name: extracellular_ephys description: Metadata related to extracellular electrophysiology. range: general__extracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true intracellular_ephys: name: intracellular_ephys description: Metadata related to intracellular electrophysiology. range: general__intracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true optogenetics: name: optogenetics description: Metadata describing optogenetic stimuluation. @@ -455,6 +476,7 @@ classes: name: name: name ifabsent: string(source_script) + identifier: true range: string required: true equals_string: source_script @@ -474,6 +496,7 @@ classes: name: name: name ifabsent: string(extracellular_ephys) + identifier: true range: string required: true equals_string: extracellular_ephys @@ -483,12 +506,16 @@ classes: range: ElectrodeGroup required: false multivalued: true + inlined: true + inlined_as_list: false electrodes: name: electrodes description: A table of all electrodes (i.e. channels) used for recording. range: extracellular_ephys__electrodes required: false multivalued: false + inlined: true + inlined_as_list: true extracellular_ephys__electrodes: name: extracellular_ephys__electrodes description: A table of all electrodes (i.e. channels) used for recording. @@ -497,6 +524,7 @@ classes: name: name: name ifabsent: string(electrodes) + identifier: true range: string required: true equals_string: electrodes @@ -560,9 +588,13 @@ classes: group: name: group description: Reference to the ElectrodeGroup this electrode is a part of. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: true - multivalued: true + multivalued: false + inlined: true group_name: name: group_name description: Name of the ElectrodeGroup this electrode is a part of. @@ -617,6 +649,7 @@ classes: name: name: name ifabsent: string(intracellular_ephys) + identifier: true range: string required: true equals_string: intracellular_ephys @@ -635,6 +668,8 @@ classes: range: IntracellularElectrode required: false multivalued: true + inlined: true + inlined_as_list: false sweep_table: name: sweep_table description: '[DEPRECATED] Table used to group different PatchClampSeries. @@ -644,6 +679,8 @@ classes: range: SweepTable required: false multivalued: false + inlined: true + inlined_as_list: false intracellular_recordings: name: intracellular_recordings description: A table to group together a stimulus and response from a single @@ -661,6 +698,8 @@ classes: range: IntracellularRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false simultaneous_recordings: name: simultaneous_recordings description: A table for grouping different intracellular recordings from @@ -669,6 +708,8 @@ classes: range: SimultaneousRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false sequential_recordings: name: sequential_recordings description: A table for grouping different sequential recordings from the @@ -678,6 +719,8 @@ classes: range: SequentialRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false repetitions: name: repetitions description: A table for grouping different sequential intracellular recordings @@ -687,6 +730,8 @@ classes: range: RepetitionsTable required: false multivalued: false + inlined: true + inlined_as_list: false experimental_conditions: name: experimental_conditions description: A table for grouping different intracellular recording repetitions @@ -694,6 +739,8 @@ classes: range: ExperimentalConditionsTable required: false multivalued: false + inlined: true + inlined_as_list: false NWBFile__intervals: name: NWBFile__intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -703,6 +750,7 @@ classes: name: name: name ifabsent: string(intervals) + identifier: true range: string required: true equals_string: intervals @@ -713,18 +761,24 @@ classes: range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false trials: name: trials description: Repeated experimental events that have a logical grouping. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false invalid_times: name: invalid_times description: Time intervals that should be removed from analysis. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false time_intervals: name: time_intervals description: Optional additional table(s) for describing other experimental @@ -732,6 +786,8 @@ classes: range: TimeIntervals required: false multivalued: true + inlined: true + inlined_as_list: false LabMetaData: name: LabMetaData description: Lab-specific meta-data. @@ -739,6 +795,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -749,6 +806,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true age: @@ -757,6 +815,7 @@ classes: range: Subject__age required: false multivalued: false + inlined: true date_of_birth: name: date_of_birth description: Date of birth of subject. Can be supplied instead of 'age'. @@ -815,6 +874,7 @@ classes: name: name: name ifabsent: string(age) + identifier: true range: string required: true equals_string: age diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.icephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.icephys.yaml index b3181bc..140e8c8 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.icephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.icephys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -41,6 +42,7 @@ classes: range: PatchClampSeries__data required: true multivalued: false + inlined: true gain: name: gain description: Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt @@ -56,6 +58,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: IntracellularElectrode - range: string @@ -67,6 +70,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -92,6 +96,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -100,6 +105,7 @@ classes: range: CurrentClampSeries__data required: true multivalued: false + inlined: true bias_current: name: bias_current description: Bias current, in amps. @@ -126,6 +132,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -153,6 +160,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -189,6 +197,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -197,6 +206,7 @@ classes: range: CurrentClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true CurrentClampStimulusSeries__data: name: CurrentClampStimulusSeries__data @@ -205,6 +215,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -231,6 +242,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -239,48 +251,56 @@ classes: range: VoltageClampSeries__data required: true multivalued: false + inlined: true capacitance_fast: name: capacitance_fast description: Fast capacitance, in farads. range: VoltageClampSeries__capacitance_fast required: false multivalued: false + inlined: true capacitance_slow: name: capacitance_slow description: Slow capacitance, in farads. range: VoltageClampSeries__capacitance_slow required: false multivalued: false + inlined: true resistance_comp_bandwidth: name: resistance_comp_bandwidth description: Resistance compensation bandwidth, in hertz. range: VoltageClampSeries__resistance_comp_bandwidth required: false multivalued: false + inlined: true resistance_comp_correction: name: resistance_comp_correction description: Resistance compensation correction, in percent. range: VoltageClampSeries__resistance_comp_correction required: false multivalued: false + inlined: true resistance_comp_prediction: name: resistance_comp_prediction description: Resistance compensation prediction, in percent. range: VoltageClampSeries__resistance_comp_prediction required: false multivalued: false + inlined: true whole_cell_capacitance_comp: name: whole_cell_capacitance_comp description: Whole cell capacitance compensation, in farads. range: VoltageClampSeries__whole_cell_capacitance_comp required: false multivalued: false + inlined: true whole_cell_series_resistance_comp: name: whole_cell_series_resistance_comp description: Whole cell series resistance compensation, in ohms. range: VoltageClampSeries__whole_cell_series_resistance_comp required: false multivalued: false + inlined: true tree_root: true VoltageClampSeries__data: name: VoltageClampSeries__data @@ -289,6 +309,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -313,6 +334,7 @@ classes: name: name: name ifabsent: string(capacitance_fast) + identifier: true range: string required: true equals_string: capacitance_fast @@ -334,6 +356,7 @@ classes: name: name: name ifabsent: string(capacitance_slow) + identifier: true range: string required: true equals_string: capacitance_slow @@ -355,6 +378,7 @@ classes: name: name: name ifabsent: string(resistance_comp_bandwidth) + identifier: true range: string required: true equals_string: resistance_comp_bandwidth @@ -377,6 +401,7 @@ classes: name: name: name ifabsent: string(resistance_comp_correction) + identifier: true range: string required: true equals_string: resistance_comp_correction @@ -399,6 +424,7 @@ classes: name: name: name ifabsent: string(resistance_comp_prediction) + identifier: true range: string required: true equals_string: resistance_comp_prediction @@ -421,6 +447,7 @@ classes: name: name: name ifabsent: string(whole_cell_capacitance_comp) + identifier: true range: string required: true equals_string: whole_cell_capacitance_comp @@ -443,6 +470,7 @@ classes: name: name: name ifabsent: string(whole_cell_series_resistance_comp) + identifier: true range: string required: true equals_string: whole_cell_series_resistance_comp @@ -465,6 +493,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -473,6 +502,7 @@ classes: range: VoltageClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true VoltageClampStimulusSeries__data: name: VoltageClampStimulusSeries__data @@ -481,6 +511,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -505,6 +536,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true cell_id: @@ -565,6 +597,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -579,6 +612,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true sweep_number: @@ -593,9 +627,13 @@ classes: series: name: series description: The PatchClampSeries with the sweep number in that row. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: PatchClampSeries required: true - multivalued: true + multivalued: false + inlined: true series_index: name: series_index annotations: @@ -609,6 +647,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true IntracellularElectrodesTable: name: IntracellularElectrodesTable @@ -617,6 +656,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -629,9 +669,13 @@ classes: electrode: name: electrode description: Column for storing the reference to the intracellular electrode. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: IntracellularElectrode required: true - multivalued: true + multivalued: false + inlined: true tree_root: true IntracellularStimuliTable: name: IntracellularStimuliTable @@ -640,6 +684,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -663,6 +708,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularResponsesTable: name: IntracellularResponsesTable @@ -671,6 +717,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -694,6 +741,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularRecordingsTable: name: IntracellularRecordingsTable @@ -713,6 +761,7 @@ classes: name: name: name ifabsent: string(intracellular_recordings) + identifier: true range: string required: true equals_string: intracellular_recordings @@ -734,18 +783,24 @@ classes: range: IntracellularElectrodesTable required: true multivalued: false + inlined: true + inlined_as_list: false stimuli: name: stimuli description: Table for storing intracellular stimulus related metadata. range: IntracellularStimuliTable required: true multivalued: false + inlined: true + inlined_as_list: false responses: name: responses description: Table for storing intracellular response related metadata. range: IntracellularResponsesTable required: true multivalued: false + inlined: true + inlined_as_list: false tree_root: true SimultaneousRecordingsTable: name: SimultaneousRecordingsTable @@ -757,6 +812,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -767,6 +823,7 @@ classes: range: SimultaneousRecordingsTable__recordings required: true multivalued: false + inlined: true recordings_index: name: recordings_index annotations: @@ -780,6 +837,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true SimultaneousRecordingsTable__recordings: name: SimultaneousRecordingsTable__recordings @@ -790,6 +848,7 @@ classes: name: name: name ifabsent: string(recordings) + identifier: true range: string required: true equals_string: recordings @@ -800,6 +859,7 @@ classes: to fix the type of table that can be referenced here. range: IntracellularRecordingsTable required: true + inlined: true SequentialRecordingsTable: name: SequentialRecordingsTable description: A table for grouping different sequential recordings from the SimultaneousRecordingsTable @@ -811,6 +871,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -821,6 +882,7 @@ classes: range: SequentialRecordingsTable__simultaneous_recordings required: true multivalued: false + inlined: true simultaneous_recordings_index: name: simultaneous_recordings_index annotations: @@ -834,6 +896,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true stimulus_type: name: stimulus_type description: The type of stimulus used for the sequential recording. @@ -853,6 +916,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -863,6 +927,7 @@ classes: to fix the type of table that can be referenced here. range: SimultaneousRecordingsTable required: true + inlined: true RepetitionsTable: name: RepetitionsTable description: A table for grouping different sequential intracellular recordings @@ -874,6 +939,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -884,6 +950,7 @@ classes: range: RepetitionsTable__sequential_recordings required: true multivalued: false + inlined: true sequential_recordings_index: name: sequential_recordings_index annotations: @@ -897,6 +964,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true RepetitionsTable__sequential_recordings: name: RepetitionsTable__sequential_recordings @@ -907,6 +975,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -917,6 +986,7 @@ classes: to fix the type of table that can be referenced here. range: SequentialRecordingsTable required: true + inlined: true ExperimentalConditionsTable: name: ExperimentalConditionsTable description: A table for grouping different intracellular recording repetitions @@ -926,6 +996,7 @@ classes: name: name: name ifabsent: string(experimental_conditions) + identifier: true range: string required: true equals_string: experimental_conditions @@ -935,6 +1006,7 @@ classes: range: ExperimentalConditionsTable__repetitions required: true multivalued: false + inlined: true repetitions_index: name: repetitions_index annotations: @@ -948,6 +1020,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true ExperimentalConditionsTable__repetitions: name: ExperimentalConditionsTable__repetitions @@ -957,6 +1030,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -967,3 +1041,4 @@ classes: to fix the type of table that can be referenced here. range: RepetitionsTable required: true + inlined: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.image.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.image.yaml index cc3d8bb..4406284 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.image.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.image.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -38,6 +39,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -57,6 +59,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -81,6 +84,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: range: ImageSeries__external_file required: false multivalued: false + inlined: true format: name: format description: Format of image. If this is 'external', then the attribute 'external_file' @@ -138,6 +143,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Device - range: string @@ -152,6 +158,7 @@ classes: name: name: name ifabsent: string(external_file) + identifier: true range: string required: true equals_string: external_file @@ -189,6 +196,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true masked_imageseries: @@ -199,6 +207,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -214,6 +223,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true distance: @@ -277,6 +287,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -297,6 +308,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -308,6 +320,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Images - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.misc.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.misc.yaml index 56f8824..ced8985 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.misc.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.misc.yaml @@ -30,6 +30,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: AbstractFeatureSeries__data required: true multivalued: false + inlined: true feature_units: name: feature_units description: Units of each feature. @@ -64,6 +66,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -96,6 +99,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -140,6 +145,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -148,6 +154,7 @@ classes: range: DecompositionSeries__data required: true multivalued: false + inlined: true metric: name: metric description: The metric used, e.g. phase, amplitude, power. @@ -168,6 +175,7 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true bands: name: bands description: Table for describing the bands that this series was generated @@ -175,6 +183,8 @@ classes: range: DecompositionSeries__bands required: true multivalued: false + inlined: true + inlined_as_list: true source_timeseries: name: source_timeseries annotations: @@ -183,6 +193,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: TimeSeries - range: string @@ -194,6 +205,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -222,6 +234,7 @@ classes: name: name: name ifabsent: string(bands) + identifier: true range: string required: true equals_string: bands @@ -273,6 +286,7 @@ classes: name: name: name ifabsent: string(Units) + identifier: true range: string required: true spike_times_index: @@ -288,12 +302,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true spike_times: name: spike_times description: Spike times for each unit in seconds. range: Units__spike_times required: false multivalued: false + inlined: true obs_intervals_index: name: obs_intervals_index annotations: @@ -307,6 +323,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true obs_intervals: name: obs_intervals description: Observation intervals for each unit. @@ -331,6 +348,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true electrodes: name: electrodes annotations: @@ -344,12 +362,17 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true electrode_group: name: electrode_group description: Electrode group that each spike unit came from. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: false - multivalued: true + multivalued: false + inlined: true waveform_mean: name: waveform_mean description: Spike waveform mean for each spike unit. @@ -428,6 +451,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true waveforms_index_index: name: waveforms_index_index annotations: @@ -442,6 +466,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true Units__spike_times: name: Units__spike_times @@ -451,6 +476,7 @@ classes: name: name: name ifabsent: string(spike_times) + identifier: true range: string required: true equals_string: spike_times diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ogen.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ogen.yaml index 93ab4af..b485822 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ogen.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ogen.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -40,6 +41,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: OptogeneticStimulusSite - range: string @@ -51,6 +53,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -81,6 +84,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ophys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ophys.yaml index 730ece0..3da9ec5 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ophys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.ophys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -65,6 +66,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -76,6 +78,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -113,6 +116,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -125,6 +129,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -155,6 +160,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true DfOverF: name: DfOverF @@ -209,15 +215,28 @@ classes: attributes: name: name: name + identifier: true range: string required: true image_mask: name: image_mask description: ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - range: PlaneSegmentation__image_mask + range: AnyType required: false multivalued: false + any_of: + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - alias: num_z pixel_mask_index: name: pixel_mask_index annotations: @@ -231,6 +250,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true pixel_mask: name: pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for @@ -239,6 +259,7 @@ classes: range: PlaneSegmentation__pixel_mask required: false multivalued: false + inlined: true voxel_mask_index: name: voxel_mask_index annotations: @@ -252,6 +273,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true voxel_mask: name: voxel_mask description: 'Voxel masks for each ROI: a list of indices and weights for @@ -260,6 +282,7 @@ classes: range: PlaneSegmentation__voxel_mask required: false multivalued: false + inlined: true reference_images: name: reference_images description: Image stacks that the segmentation masks apply to. @@ -276,22 +299,11 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string tree_root: true - PlaneSegmentation__image_mask: - name: PlaneSegmentation__image_mask - description: ROI masks for each ROI. Each image mask is the size of the original - imaging plane (or volume) and members of the ROI are finite non-zero. - is_a: VectorData - attributes: - name: - name: name - ifabsent: string(image_mask) - range: string - required: true - equals_string: image_mask PlaneSegmentation__pixel_mask: name: PlaneSegmentation__pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for the @@ -302,6 +314,7 @@ classes: name: name: name ifabsent: string(pixel_mask) + identifier: true range: string required: true equals_string: pixel_mask @@ -339,6 +352,7 @@ classes: name: name: name ifabsent: string(voxel_mask) + identifier: true range: string required: true equals_string: voxel_mask @@ -381,6 +395,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -424,6 +439,7 @@ classes: range: ImagingPlane__manifold required: false multivalued: false + inlined: true origin_coords: name: origin_coords description: Physical location of the first element of the imaging plane (0, @@ -432,6 +448,7 @@ classes: range: ImagingPlane__origin_coords required: false multivalued: false + inlined: true grid_spacing: name: grid_spacing description: Space between pixels in (x, y) or voxels in (x, y, z) directions, @@ -440,6 +457,7 @@ classes: range: ImagingPlane__grid_spacing required: false multivalued: false + inlined: true reference_frame: name: reference_frame description: Describes reference frame of origin_coords and grid_spacing. @@ -468,6 +486,8 @@ classes: range: OpticalChannel required: true multivalued: true + inlined: true + inlined_as_list: false device: name: device annotations: @@ -476,6 +496,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -489,6 +510,7 @@ classes: name: name: name ifabsent: string(manifold) + identifier: true range: string required: true equals_string: manifold @@ -538,6 +560,7 @@ classes: name: name: name ifabsent: string(origin_coords) + identifier: true range: string required: true equals_string: origin_coords @@ -568,6 +591,7 @@ classes: name: name: name ifabsent: string(grid_spacing) + identifier: true range: string required: true equals_string: grid_spacing @@ -596,6 +620,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -632,6 +657,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true corrected: @@ -640,6 +666,8 @@ classes: range: ImageSeries required: true multivalued: false + inlined: true + inlined_as_list: false xy_translation: name: xy_translation description: Stores the x,y delta necessary to align each frame to the common @@ -647,6 +675,8 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true + inlined_as_list: false original: name: original annotations: @@ -655,6 +685,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.retinotopy.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.retinotopy.yaml index dc790f3..c1fce82 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.retinotopy.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_6_0_alpha/core.nwb.retinotopy.yaml @@ -29,6 +29,7 @@ classes: name: name: name ifabsent: string(ImagingRetinotopy) + identifier: true range: string required: true axis_1_phase_map: @@ -37,6 +38,7 @@ classes: range: ImagingRetinotopy__axis_1_phase_map required: true multivalued: false + inlined: true axis_1_power_map: name: axis_1_power_map description: Power response on the first measured axis. Response is scaled @@ -44,12 +46,14 @@ classes: range: ImagingRetinotopy__axis_1_power_map required: false multivalued: false + inlined: true axis_2_phase_map: name: axis_2_phase_map description: Phase response to stimulus on the second measured axis. range: ImagingRetinotopy__axis_2_phase_map required: true multivalued: false + inlined: true axis_2_power_map: name: axis_2_power_map description: Power response on the second measured axis. Response is scaled @@ -57,6 +61,7 @@ classes: range: ImagingRetinotopy__axis_2_power_map required: false multivalued: false + inlined: true axis_descriptions: name: axis_descriptions description: Two-element array describing the contents of the two response @@ -76,6 +81,7 @@ classes: range: ImagingRetinotopy__focal_depth_image required: false multivalued: false + inlined: true sign_map: name: sign_map description: Sine of the angle between the direction of the gradient in axis_1 @@ -83,6 +89,7 @@ classes: range: ImagingRetinotopy__sign_map required: false multivalued: false + inlined: true vasculature_image: name: vasculature_image description: 'Gray-scale anatomical image of cortical surface. Array structure: @@ -90,6 +97,7 @@ classes: range: ImagingRetinotopy__vasculature_image required: true multivalued: false + inlined: true tree_root: true ImagingRetinotopy__axis_1_phase_map: name: ImagingRetinotopy__axis_1_phase_map @@ -98,6 +106,7 @@ classes: name: name: name ifabsent: string(axis_1_phase_map) + identifier: true range: string required: true equals_string: axis_1_phase_map @@ -134,6 +143,7 @@ classes: name: name: name ifabsent: string(axis_1_power_map) + identifier: true range: string required: true equals_string: axis_1_power_map @@ -169,6 +179,7 @@ classes: name: name: name ifabsent: string(axis_2_phase_map) + identifier: true range: string required: true equals_string: axis_2_phase_map @@ -205,6 +216,7 @@ classes: name: name: name ifabsent: string(axis_2_power_map) + identifier: true range: string required: true equals_string: axis_2_power_map @@ -241,6 +253,7 @@ classes: name: name: name ifabsent: string(focal_depth_image) + identifier: true range: string required: true equals_string: focal_depth_image @@ -288,6 +301,7 @@ classes: name: name: name ifabsent: string(sign_map) + identifier: true range: string required: true equals_string: sign_map @@ -319,6 +333,7 @@ classes: name: name: name ifabsent: string(vasculature_image) + identifier: true range: string required: true equals_string: vasculature_image diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.base.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.base.yaml index ca7cfe1..7c3450a 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.base.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -34,6 +35,7 @@ classes: name: name: name ifabsent: string(timeseries) + identifier: true range: string required: true idx_start: @@ -63,6 +65,7 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true tree_root: true Image: name: Image @@ -73,6 +76,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true resolution: @@ -113,6 +117,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -125,6 +130,8 @@ classes: range: Image required: true multivalued: true + inlined: true + inlined_as_list: true tree_root: true NWBContainer: name: NWBContainer @@ -134,6 +141,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -145,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -155,6 +164,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -180,6 +190,7 @@ classes: range: TimeSeries__data required: true multivalued: false + inlined: true starting_time: name: starting_time description: Timestamp of the first sample in seconds. When timestamps are @@ -188,6 +199,7 @@ classes: range: TimeSeries__starting_time required: false multivalued: false + inlined: true timestamps: name: timestamps description: Timestamps for samples stored in data, in seconds, relative to @@ -231,6 +243,8 @@ classes: range: TimeSeries__sync required: false multivalued: false + inlined: true + inlined_as_list: true tree_root: true TimeSeries__data: name: TimeSeries__data @@ -241,6 +255,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -327,6 +342,7 @@ classes: name: name: name ifabsent: string(starting_time) + identifier: true range: string required: true equals_string: starting_time @@ -358,6 +374,7 @@ classes: name: name: name ifabsent: string(sync) + identifier: true range: string required: true equals_string: sync @@ -384,6 +401,7 @@ classes: name: name: name ifabsent: string(Images) + identifier: true range: string required: true description: @@ -413,4 +431,5 @@ classes: range: ImageReferences required: false multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.behavior.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.behavior.yaml index f0b74b7..32ff4f8 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.behavior.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.behavior.yaml @@ -29,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: SpatialSeries__data required: true multivalued: false + inlined: true reference_frame: name: reference_frame description: Description defining what exactly 'straight-ahead' means. @@ -53,6 +55,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.device.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.device.yaml index ab2fc92..3116431 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.device.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.device.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ecephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ecephys.yaml index f2be1a3..71eadb4 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ecephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ecephys.yaml @@ -25,6 +25,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true filtering: @@ -71,6 +72,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true channel_conversion: name: channel_conversion description: Channel-specific conversion factor. Multiply the data in the @@ -103,6 +105,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -143,6 +146,7 @@ classes: name: name: name ifabsent: string(FeatureExtraction) + identifier: true range: string required: true description: @@ -189,6 +193,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true EventDetection: name: EventDetection @@ -198,6 +203,7 @@ classes: name: name: name ifabsent: string(EventDetection) + identifier: true range: string required: true detection_method: @@ -236,6 +242,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ElectricalSeries - range: string @@ -297,6 +304,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -317,6 +325,7 @@ classes: range: ElectrodeGroup__position required: false multivalued: false + inlined: true device: name: device annotations: @@ -325,6 +334,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -336,6 +346,7 @@ classes: name: name: name ifabsent: string(position) + identifier: true range: string required: true equals_string: position @@ -376,6 +387,7 @@ classes: name: name: name ifabsent: string(ClusterWaveforms) + identifier: true range: string required: true waveform_filtering: @@ -416,6 +428,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Clustering - range: string @@ -429,6 +442,7 @@ classes: name: name: name ifabsent: string(Clustering) + identifier: true range: string required: true description: diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.epoch.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.epoch.yaml index 1885024..471b87a 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.epoch.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.epoch.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true start_time: @@ -64,6 +65,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true timeseries: name: timeseries annotations: @@ -77,6 +79,7 @@ classes: range: TimeSeriesReferenceVectorData required: false multivalued: false + inlined: true timeseries_index: name: timeseries_index annotations: @@ -90,4 +93,5 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.file.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.file.yaml index 1b56d9d..a6b27f5 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.file.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.file.yaml @@ -28,6 +28,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true notes: @@ -45,6 +46,7 @@ classes: name: name: name ifabsent: string(root) + identifier: true range: string required: true equals_string: root @@ -184,6 +186,8 @@ classes: range: NWBFile__stimulus required: true multivalued: false + inlined: true + inlined_as_list: true general: name: general description: Experimental metadata, including protocol, notes and description @@ -204,6 +208,8 @@ classes: range: NWBFile__general required: true multivalued: false + inlined: true + inlined_as_list: true intervals: name: intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -213,12 +219,16 @@ classes: range: NWBFile__intervals required: false multivalued: false + inlined: true + inlined_as_list: true units: name: units description: Data about sorted spike units. range: Units required: false multivalued: false + inlined: true + inlined_as_list: false tree_root: true NWBFile__stimulus: name: NWBFile__stimulus @@ -238,6 +248,7 @@ classes: name: name: name ifabsent: string(stimulus) + identifier: true range: string required: true equals_string: stimulus @@ -283,6 +294,7 @@ classes: name: name: name ifabsent: string(general) + identifier: true range: string required: true equals_string: general @@ -378,6 +390,7 @@ classes: range: general__source_script required: false multivalued: false + inlined: true stimulus: name: stimulus description: Notes about stimuli, such as how and where they were presented. @@ -405,6 +418,8 @@ classes: range: LabMetaData required: false multivalued: true + inlined: true + inlined_as_list: false devices: name: devices description: Description of hardware devices used during experiment, e.g., @@ -421,18 +436,24 @@ classes: range: Subject required: false multivalued: false + inlined: true + inlined_as_list: false extracellular_ephys: name: extracellular_ephys description: Metadata related to extracellular electrophysiology. range: general__extracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true intracellular_ephys: name: intracellular_ephys description: Metadata related to intracellular electrophysiology. range: general__intracellular_ephys required: false multivalued: false + inlined: true + inlined_as_list: true optogenetics: name: optogenetics description: Metadata describing optogenetic stimuluation. @@ -457,6 +478,7 @@ classes: name: name: name ifabsent: string(source_script) + identifier: true range: string required: true equals_string: source_script @@ -476,6 +498,7 @@ classes: name: name: name ifabsent: string(extracellular_ephys) + identifier: true range: string required: true equals_string: extracellular_ephys @@ -485,12 +508,16 @@ classes: range: ElectrodeGroup required: false multivalued: true + inlined: true + inlined_as_list: false electrodes: name: electrodes description: A table of all electrodes (i.e. channels) used for recording. range: extracellular_ephys__electrodes required: false multivalued: false + inlined: true + inlined_as_list: true extracellular_ephys__electrodes: name: extracellular_ephys__electrodes description: A table of all electrodes (i.e. channels) used for recording. @@ -499,6 +526,7 @@ classes: name: name: name ifabsent: string(electrodes) + identifier: true range: string required: true equals_string: electrodes @@ -562,9 +590,13 @@ classes: group: name: group description: Reference to the ElectrodeGroup this electrode is a part of. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: true - multivalued: true + multivalued: false + inlined: true group_name: name: group_name description: Name of the ElectrodeGroup this electrode is a part of. @@ -619,6 +651,7 @@ classes: name: name: name ifabsent: string(intracellular_ephys) + identifier: true range: string required: true equals_string: intracellular_ephys @@ -637,6 +670,8 @@ classes: range: IntracellularElectrode required: false multivalued: true + inlined: true + inlined_as_list: false sweep_table: name: sweep_table description: '[DEPRECATED] Table used to group different PatchClampSeries. @@ -646,6 +681,8 @@ classes: range: SweepTable required: false multivalued: false + inlined: true + inlined_as_list: false intracellular_recordings: name: intracellular_recordings description: A table to group together a stimulus and response from a single @@ -663,6 +700,8 @@ classes: range: IntracellularRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false simultaneous_recordings: name: simultaneous_recordings description: A table for grouping different intracellular recordings from @@ -671,6 +710,8 @@ classes: range: SimultaneousRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false sequential_recordings: name: sequential_recordings description: A table for grouping different sequential recordings from the @@ -680,6 +721,8 @@ classes: range: SequentialRecordingsTable required: false multivalued: false + inlined: true + inlined_as_list: false repetitions: name: repetitions description: A table for grouping different sequential intracellular recordings @@ -689,6 +732,8 @@ classes: range: RepetitionsTable required: false multivalued: false + inlined: true + inlined_as_list: false experimental_conditions: name: experimental_conditions description: A table for grouping different intracellular recording repetitions @@ -696,6 +741,8 @@ classes: range: ExperimentalConditionsTable required: false multivalued: false + inlined: true + inlined_as_list: false NWBFile__intervals: name: NWBFile__intervals description: Experimental intervals, whether that be logically distinct sub-experiments @@ -705,6 +752,7 @@ classes: name: name: name ifabsent: string(intervals) + identifier: true range: string required: true equals_string: intervals @@ -715,18 +763,24 @@ classes: range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false trials: name: trials description: Repeated experimental events that have a logical grouping. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false invalid_times: name: invalid_times description: Time intervals that should be removed from analysis. range: TimeIntervals required: false multivalued: false + inlined: true + inlined_as_list: false time_intervals: name: time_intervals description: Optional additional table(s) for describing other experimental @@ -734,6 +788,8 @@ classes: range: TimeIntervals required: false multivalued: true + inlined: true + inlined_as_list: false LabMetaData: name: LabMetaData description: Lab-specific meta-data. @@ -741,6 +797,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -751,6 +808,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true age: @@ -759,6 +817,7 @@ classes: range: Subject__age required: false multivalued: false + inlined: true date_of_birth: name: date_of_birth description: Date of birth of subject. Can be supplied instead of 'age'. @@ -817,6 +876,7 @@ classes: name: name: name ifabsent: string(age) + identifier: true range: string required: true equals_string: age diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.icephys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.icephys.yaml index 710ba36..a8662e7 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.icephys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.icephys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -41,6 +42,7 @@ classes: range: PatchClampSeries__data required: true multivalued: false + inlined: true gain: name: gain description: Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt @@ -56,6 +58,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: IntracellularElectrode - range: string @@ -67,6 +70,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -92,6 +96,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -100,6 +105,7 @@ classes: range: CurrentClampSeries__data required: true multivalued: false + inlined: true bias_current: name: bias_current description: Bias current, in amps. @@ -126,6 +132,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -153,6 +160,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true stimulus_description: @@ -189,6 +197,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -197,6 +206,7 @@ classes: range: CurrentClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true CurrentClampStimulusSeries__data: name: CurrentClampStimulusSeries__data @@ -205,6 +215,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -231,6 +242,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -239,48 +251,56 @@ classes: range: VoltageClampSeries__data required: true multivalued: false + inlined: true capacitance_fast: name: capacitance_fast description: Fast capacitance, in farads. range: VoltageClampSeries__capacitance_fast required: false multivalued: false + inlined: true capacitance_slow: name: capacitance_slow description: Slow capacitance, in farads. range: VoltageClampSeries__capacitance_slow required: false multivalued: false + inlined: true resistance_comp_bandwidth: name: resistance_comp_bandwidth description: Resistance compensation bandwidth, in hertz. range: VoltageClampSeries__resistance_comp_bandwidth required: false multivalued: false + inlined: true resistance_comp_correction: name: resistance_comp_correction description: Resistance compensation correction, in percent. range: VoltageClampSeries__resistance_comp_correction required: false multivalued: false + inlined: true resistance_comp_prediction: name: resistance_comp_prediction description: Resistance compensation prediction, in percent. range: VoltageClampSeries__resistance_comp_prediction required: false multivalued: false + inlined: true whole_cell_capacitance_comp: name: whole_cell_capacitance_comp description: Whole cell capacitance compensation, in farads. range: VoltageClampSeries__whole_cell_capacitance_comp required: false multivalued: false + inlined: true whole_cell_series_resistance_comp: name: whole_cell_series_resistance_comp description: Whole cell series resistance compensation, in ohms. range: VoltageClampSeries__whole_cell_series_resistance_comp required: false multivalued: false + inlined: true tree_root: true VoltageClampSeries__data: name: VoltageClampSeries__data @@ -289,6 +309,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -313,6 +334,7 @@ classes: name: name: name ifabsent: string(capacitance_fast) + identifier: true range: string required: true equals_string: capacitance_fast @@ -334,6 +356,7 @@ classes: name: name: name ifabsent: string(capacitance_slow) + identifier: true range: string required: true equals_string: capacitance_slow @@ -355,6 +378,7 @@ classes: name: name: name ifabsent: string(resistance_comp_bandwidth) + identifier: true range: string required: true equals_string: resistance_comp_bandwidth @@ -377,6 +401,7 @@ classes: name: name: name ifabsent: string(resistance_comp_correction) + identifier: true range: string required: true equals_string: resistance_comp_correction @@ -399,6 +424,7 @@ classes: name: name: name ifabsent: string(resistance_comp_prediction) + identifier: true range: string required: true equals_string: resistance_comp_prediction @@ -421,6 +447,7 @@ classes: name: name: name ifabsent: string(whole_cell_capacitance_comp) + identifier: true range: string required: true equals_string: whole_cell_capacitance_comp @@ -443,6 +470,7 @@ classes: name: name: name ifabsent: string(whole_cell_series_resistance_comp) + identifier: true range: string required: true equals_string: whole_cell_series_resistance_comp @@ -465,6 +493,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -473,6 +502,7 @@ classes: range: VoltageClampStimulusSeries__data required: true multivalued: false + inlined: true tree_root: true VoltageClampStimulusSeries__data: name: VoltageClampStimulusSeries__data @@ -481,6 +511,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -505,6 +536,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true cell_id: @@ -565,6 +597,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -579,6 +612,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true sweep_number: @@ -593,9 +627,13 @@ classes: series: name: series description: The PatchClampSeries with the sweep number in that row. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: PatchClampSeries required: true - multivalued: true + multivalued: false + inlined: true series_index: name: series_index annotations: @@ -609,6 +647,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true IntracellularElectrodesTable: name: IntracellularElectrodesTable @@ -617,6 +656,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -629,9 +669,13 @@ classes: electrode: name: electrode description: Column for storing the reference to the intracellular electrode. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: IntracellularElectrode required: true - multivalued: true + multivalued: false + inlined: true tree_root: true IntracellularStimuliTable: name: IntracellularStimuliTable @@ -640,6 +684,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -663,6 +708,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true stimulus_template: name: stimulus_template annotations: @@ -677,6 +723,7 @@ classes: range: TimeSeriesReferenceVectorData required: false multivalued: false + inlined: true tree_root: true IntracellularResponsesTable: name: IntracellularResponsesTable @@ -685,6 +732,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -708,6 +756,7 @@ classes: range: TimeSeriesReferenceVectorData required: true multivalued: false + inlined: true tree_root: true IntracellularRecordingsTable: name: IntracellularRecordingsTable @@ -727,6 +776,7 @@ classes: name: name: name ifabsent: string(intracellular_recordings) + identifier: true range: string required: true equals_string: intracellular_recordings @@ -748,18 +798,24 @@ classes: range: IntracellularElectrodesTable required: true multivalued: false + inlined: true + inlined_as_list: false stimuli: name: stimuli description: Table for storing intracellular stimulus related metadata. range: IntracellularStimuliTable required: true multivalued: false + inlined: true + inlined_as_list: false responses: name: responses description: Table for storing intracellular response related metadata. range: IntracellularResponsesTable required: true multivalued: false + inlined: true + inlined_as_list: false tree_root: true SimultaneousRecordingsTable: name: SimultaneousRecordingsTable @@ -771,6 +827,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -781,6 +838,7 @@ classes: range: SimultaneousRecordingsTable__recordings required: true multivalued: false + inlined: true recordings_index: name: recordings_index annotations: @@ -794,6 +852,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true SimultaneousRecordingsTable__recordings: name: SimultaneousRecordingsTable__recordings @@ -804,6 +863,7 @@ classes: name: name: name ifabsent: string(recordings) + identifier: true range: string required: true equals_string: recordings @@ -814,6 +874,7 @@ classes: to fix the type of table that can be referenced here. range: IntracellularRecordingsTable required: true + inlined: true SequentialRecordingsTable: name: SequentialRecordingsTable description: A table for grouping different sequential recordings from the SimultaneousRecordingsTable @@ -825,6 +886,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -835,6 +897,7 @@ classes: range: SequentialRecordingsTable__simultaneous_recordings required: true multivalued: false + inlined: true simultaneous_recordings_index: name: simultaneous_recordings_index annotations: @@ -848,6 +911,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true stimulus_type: name: stimulus_type description: The type of stimulus used for the sequential recording. @@ -867,6 +931,7 @@ classes: name: name: name ifabsent: string(simultaneous_recordings) + identifier: true range: string required: true equals_string: simultaneous_recordings @@ -877,6 +942,7 @@ classes: to fix the type of table that can be referenced here. range: SimultaneousRecordingsTable required: true + inlined: true RepetitionsTable: name: RepetitionsTable description: A table for grouping different sequential intracellular recordings @@ -888,6 +954,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -898,6 +965,7 @@ classes: range: RepetitionsTable__sequential_recordings required: true multivalued: false + inlined: true sequential_recordings_index: name: sequential_recordings_index annotations: @@ -911,6 +979,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true RepetitionsTable__sequential_recordings: name: RepetitionsTable__sequential_recordings @@ -921,6 +990,7 @@ classes: name: name: name ifabsent: string(sequential_recordings) + identifier: true range: string required: true equals_string: sequential_recordings @@ -931,6 +1001,7 @@ classes: to fix the type of table that can be referenced here. range: SequentialRecordingsTable required: true + inlined: true ExperimentalConditionsTable: name: ExperimentalConditionsTable description: A table for grouping different intracellular recording repetitions @@ -940,6 +1011,7 @@ classes: name: name: name ifabsent: string(experimental_conditions) + identifier: true range: string required: true equals_string: experimental_conditions @@ -949,6 +1021,7 @@ classes: range: ExperimentalConditionsTable__repetitions required: true multivalued: false + inlined: true repetitions_index: name: repetitions_index annotations: @@ -962,6 +1035,7 @@ classes: range: VectorIndex required: true multivalued: false + inlined: true tree_root: true ExperimentalConditionsTable__repetitions: name: ExperimentalConditionsTable__repetitions @@ -971,6 +1045,7 @@ classes: name: name: name ifabsent: string(repetitions) + identifier: true range: string required: true equals_string: repetitions @@ -981,3 +1056,4 @@ classes: to fix the type of table that can be referenced here. range: RepetitionsTable required: true + inlined: true diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.image.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.image.yaml index e465501..603c351 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.image.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.image.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -38,6 +39,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -57,6 +59,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true value: @@ -81,6 +84,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: range: ImageSeries__external_file required: false multivalued: false + inlined: true format: name: format description: Format of image. If this is 'external', then the attribute 'external_file' @@ -138,6 +143,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Device - range: string @@ -152,6 +158,7 @@ classes: name: name: name ifabsent: string(external_file) + identifier: true range: string required: true equals_string: external_file @@ -189,6 +196,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true masked_imageseries: @@ -199,6 +207,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -214,6 +223,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true distance: @@ -277,6 +287,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -297,6 +308,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: ImageSeries - range: string @@ -308,6 +320,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: Images - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.misc.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.misc.yaml index 9395fd9..b30070d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.misc.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.misc.yaml @@ -30,6 +30,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -38,6 +39,7 @@ classes: range: AbstractFeatureSeries__data required: true multivalued: false + inlined: true feature_units: name: feature_units description: Units of each feature. @@ -64,6 +66,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -96,6 +99,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -121,6 +125,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -140,6 +145,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -148,6 +154,7 @@ classes: range: DecompositionSeries__data required: true multivalued: false + inlined: true metric: name: metric description: The metric used, e.g. phase, amplitude, power. @@ -168,6 +175,7 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true bands: name: bands description: Table for describing the bands that this series was generated @@ -175,6 +183,8 @@ classes: range: DecompositionSeries__bands required: true multivalued: false + inlined: true + inlined_as_list: true source_timeseries: name: source_timeseries annotations: @@ -183,6 +193,7 @@ classes: value: link required: false multivalued: false + inlined: true any_of: - range: TimeSeries - range: string @@ -194,6 +205,7 @@ classes: name: name: name ifabsent: string(data) + identifier: true range: string required: true equals_string: data @@ -222,6 +234,7 @@ classes: name: name: name ifabsent: string(bands) + identifier: true range: string required: true equals_string: bands @@ -273,6 +286,7 @@ classes: name: name: name ifabsent: string(Units) + identifier: true range: string required: true spike_times_index: @@ -288,12 +302,14 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true spike_times: name: spike_times description: Spike times for each unit in seconds. range: Units__spike_times required: false multivalued: false + inlined: true obs_intervals_index: name: obs_intervals_index annotations: @@ -307,6 +323,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true obs_intervals: name: obs_intervals description: Observation intervals for each unit. @@ -331,6 +348,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true electrodes: name: electrodes annotations: @@ -344,12 +362,17 @@ classes: range: DynamicTableRegion required: false multivalued: false + inlined: true electrode_group: name: electrode_group description: Electrode group that each spike unit came from. + array: + minimum_number_dimensions: 1 + maximum_number_dimensions: false range: ElectrodeGroup required: false - multivalued: true + multivalued: false + inlined: true waveform_mean: name: waveform_mean description: Spike waveform mean for each spike unit. @@ -428,6 +451,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true waveforms_index_index: name: waveforms_index_index annotations: @@ -442,6 +466,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true tree_root: true Units__spike_times: name: Units__spike_times @@ -451,6 +476,7 @@ classes: name: name: name ifabsent: string(spike_times) + identifier: true range: string required: true equals_string: spike_times diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ogen.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ogen.yaml index 085004d..9cc7b0d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ogen.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ogen.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -47,6 +48,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: OptogeneticStimulusSite - range: string @@ -58,6 +60,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -88,6 +91,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ophys.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ophys.yaml index 8452b74..b5d3676 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ophys.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.ophys.yaml @@ -23,6 +23,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -65,6 +66,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -76,6 +78,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true pmt_gain: @@ -113,6 +116,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string @@ -125,6 +129,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true data: @@ -155,6 +160,7 @@ classes: range: DynamicTableRegion required: true multivalued: false + inlined: true tree_root: true DfOverF: name: DfOverF @@ -209,15 +215,28 @@ classes: attributes: name: name: name + identifier: true range: string required: true image_mask: name: image_mask description: ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. - range: PlaneSegmentation__image_mask + range: AnyType required: false multivalued: false + any_of: + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - array: + dimensions: + - alias: num_roi + - alias: num_x + - alias: num_y + - alias: num_z pixel_mask_index: name: pixel_mask_index annotations: @@ -231,6 +250,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true pixel_mask: name: pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for @@ -239,6 +259,7 @@ classes: range: PlaneSegmentation__pixel_mask required: false multivalued: false + inlined: true voxel_mask_index: name: voxel_mask_index annotations: @@ -252,6 +273,7 @@ classes: range: VectorIndex required: false multivalued: false + inlined: true voxel_mask: name: voxel_mask description: 'Voxel masks for each ROI: a list of indices and weights for @@ -260,6 +282,7 @@ classes: range: PlaneSegmentation__voxel_mask required: false multivalued: false + inlined: true reference_images: name: reference_images description: Image stacks that the segmentation masks apply to. @@ -276,22 +299,11 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImagingPlane - range: string tree_root: true - PlaneSegmentation__image_mask: - name: PlaneSegmentation__image_mask - description: ROI masks for each ROI. Each image mask is the size of the original - imaging plane (or volume) and members of the ROI are finite non-zero. - is_a: VectorData - attributes: - name: - name: name - ifabsent: string(image_mask) - range: string - required: true - equals_string: image_mask PlaneSegmentation__pixel_mask: name: PlaneSegmentation__pixel_mask description: 'Pixel masks for each ROI: a list of indices and weights for the @@ -302,6 +314,7 @@ classes: name: name: name ifabsent: string(pixel_mask) + identifier: true range: string required: true equals_string: pixel_mask @@ -339,6 +352,7 @@ classes: name: name: name ifabsent: string(voxel_mask) + identifier: true range: string required: true equals_string: voxel_mask @@ -381,6 +395,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -424,6 +439,7 @@ classes: range: ImagingPlane__manifold required: false multivalued: false + inlined: true origin_coords: name: origin_coords description: Physical location of the first element of the imaging plane (0, @@ -432,6 +448,7 @@ classes: range: ImagingPlane__origin_coords required: false multivalued: false + inlined: true grid_spacing: name: grid_spacing description: Space between pixels in (x, y) or voxels in (x, y, z) directions, @@ -440,6 +457,7 @@ classes: range: ImagingPlane__grid_spacing required: false multivalued: false + inlined: true reference_frame: name: reference_frame description: Describes reference frame of origin_coords and grid_spacing. @@ -468,6 +486,8 @@ classes: range: OpticalChannel required: true multivalued: true + inlined: true + inlined_as_list: false device: name: device annotations: @@ -476,6 +496,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: Device - range: string @@ -489,6 +510,7 @@ classes: name: name: name ifabsent: string(manifold) + identifier: true range: string required: true equals_string: manifold @@ -538,6 +560,7 @@ classes: name: name: name ifabsent: string(origin_coords) + identifier: true range: string required: true equals_string: origin_coords @@ -568,6 +591,7 @@ classes: name: name: name ifabsent: string(grid_spacing) + identifier: true range: string required: true equals_string: grid_spacing @@ -596,6 +620,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -632,6 +657,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true corrected: @@ -640,6 +666,8 @@ classes: range: ImageSeries required: true multivalued: false + inlined: true + inlined_as_list: false xy_translation: name: xy_translation description: Stores the x,y delta necessary to align each frame to the common @@ -647,6 +675,8 @@ classes: range: TimeSeries required: true multivalued: false + inlined: true + inlined_as_list: false original: name: original annotations: @@ -655,6 +685,7 @@ classes: value: link required: true multivalued: false + inlined: true any_of: - range: ImageSeries - range: string diff --git a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.retinotopy.yaml b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.retinotopy.yaml index 6416821..8cc1810 100644 --- a/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.retinotopy.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/core/v2_7_0/core.nwb.retinotopy.yaml @@ -29,6 +29,7 @@ classes: name: name: name ifabsent: string(ImagingRetinotopy) + identifier: true range: string required: true axis_1_phase_map: @@ -37,6 +38,7 @@ classes: range: ImagingRetinotopy__axis_1_phase_map required: true multivalued: false + inlined: true axis_1_power_map: name: axis_1_power_map description: Power response on the first measured axis. Response is scaled @@ -44,12 +46,14 @@ classes: range: ImagingRetinotopy__axis_1_power_map required: false multivalued: false + inlined: true axis_2_phase_map: name: axis_2_phase_map description: Phase response to stimulus on the second measured axis. range: ImagingRetinotopy__axis_2_phase_map required: true multivalued: false + inlined: true axis_2_power_map: name: axis_2_power_map description: Power response on the second measured axis. Response is scaled @@ -57,6 +61,7 @@ classes: range: ImagingRetinotopy__axis_2_power_map required: false multivalued: false + inlined: true axis_descriptions: name: axis_descriptions description: Two-element array describing the contents of the two response @@ -76,6 +81,7 @@ classes: range: ImagingRetinotopy__focal_depth_image required: false multivalued: false + inlined: true sign_map: name: sign_map description: Sine of the angle between the direction of the gradient in axis_1 @@ -83,6 +89,7 @@ classes: range: ImagingRetinotopy__sign_map required: false multivalued: false + inlined: true vasculature_image: name: vasculature_image description: 'Gray-scale anatomical image of cortical surface. Array structure: @@ -90,6 +97,7 @@ classes: range: ImagingRetinotopy__vasculature_image required: true multivalued: false + inlined: true tree_root: true ImagingRetinotopy__axis_1_phase_map: name: ImagingRetinotopy__axis_1_phase_map @@ -98,6 +106,7 @@ classes: name: name: name ifabsent: string(axis_1_phase_map) + identifier: true range: string required: true equals_string: axis_1_phase_map @@ -134,6 +143,7 @@ classes: name: name: name ifabsent: string(axis_1_power_map) + identifier: true range: string required: true equals_string: axis_1_power_map @@ -169,6 +179,7 @@ classes: name: name: name ifabsent: string(axis_2_phase_map) + identifier: true range: string required: true equals_string: axis_2_phase_map @@ -205,6 +216,7 @@ classes: name: name: name ifabsent: string(axis_2_power_map) + identifier: true range: string required: true equals_string: axis_2_power_map @@ -241,6 +253,7 @@ classes: name: name: name ifabsent: string(focal_depth_image) + identifier: true range: string required: true equals_string: focal_depth_image @@ -288,6 +301,7 @@ classes: name: name: name ifabsent: string(sign_map) + identifier: true range: string required: true equals_string: sign_map @@ -319,6 +333,7 @@ classes: name: name: name ifabsent: string(vasculature_image) + identifier: true range: string required: true equals_string: vasculature_image diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml index 266d216..6ba8106 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml @@ -18,6 +18,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -28,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml index 13b5f58..bbf0b5d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true shape: @@ -52,17 +53,10 @@ classes: data: name: data description: The non-zero values in the matrix. - range: CSRMatrix__data + array: + dimensions: + - alias: number_of_non_zero_values + range: AnyType required: true multivalued: false tree_root: true - CSRMatrix__data: - name: CSRMatrix__data - description: The non-zero values in the matrix. - attributes: - name: - name: name - ifabsent: string(data) - range: string - required: true - equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml index e3f3be3..7e1a614 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml @@ -27,6 +27,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -68,6 +69,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true target: @@ -75,6 +77,7 @@ classes: description: Reference to the target dataset that this index applies to. range: VectorData required: true + inlined: true tree_root: true ElementIdentifiers: name: ElementIdentifiers @@ -85,6 +88,7 @@ classes: name: name: name ifabsent: string(element_id) + identifier: true range: string required: true value: @@ -109,6 +113,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true table: @@ -117,6 +122,7 @@ classes: to. range: DynamicTable required: true + inlined: true description: name: description description: Description of what this table region points to. @@ -147,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true colnames: @@ -176,4 +183,5 @@ classes: range: VectorData required: false multivalued: true + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.base.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.base.yaml index 36edb0a..91de7c2 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.base.yaml @@ -18,6 +18,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -28,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.sparse.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.sparse.yaml index 24ea8fd..b256bbe 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.sparse.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.sparse.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true shape: @@ -52,17 +53,10 @@ classes: data: name: data description: The non-zero values in the matrix. - range: CSRMatrix__data + array: + dimensions: + - alias: number_of_non_zero_values + range: AnyType required: true multivalued: false tree_root: true - CSRMatrix__data: - name: CSRMatrix__data - description: The non-zero values in the matrix. - attributes: - name: - name: name - ifabsent: string(data) - range: string - required: true - equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.table.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.table.yaml index 94ba93b..f8adba6 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.table.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_0/hdmf-common.table.yaml @@ -27,6 +27,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -68,6 +69,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true target: @@ -75,6 +77,7 @@ classes: description: Reference to the target dataset that this index applies to. range: VectorData required: true + inlined: true tree_root: true ElementIdentifiers: name: ElementIdentifiers @@ -85,6 +88,7 @@ classes: name: name: name ifabsent: string(element_id) + identifier: true range: string required: true value: @@ -109,6 +113,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true table: @@ -117,6 +122,7 @@ classes: to. range: DynamicTable required: true + inlined: true description: name: description description: Description of what this table region points to. @@ -147,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true colnames: @@ -176,6 +183,7 @@ classes: range: VectorData required: false multivalued: true + inlined: true tree_root: true AlignedDynamicTable: name: AlignedDynamicTable diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml index 8685dc7..4fd80e6 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml @@ -18,6 +18,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -28,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml index 21654df..bde18eb 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true shape: @@ -52,17 +53,10 @@ classes: data: name: data description: The non-zero values in the matrix. - range: CSRMatrix__data + array: + dimensions: + - alias: number_of_non_zero_values + range: AnyType required: true multivalued: false tree_root: true - CSRMatrix__data: - name: CSRMatrix__data - description: The non-zero values in the matrix. - attributes: - name: - name: name - ifabsent: string(data) - range: string - required: true - equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml index 0047b7c..52b119d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml @@ -27,6 +27,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -68,6 +69,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true target: @@ -75,6 +77,7 @@ classes: description: Reference to the target dataset that this index applies to. range: VectorData required: true + inlined: true tree_root: true ElementIdentifiers: name: ElementIdentifiers @@ -85,6 +88,7 @@ classes: name: name: name ifabsent: string(element_id) + identifier: true range: string required: true value: @@ -109,6 +113,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true table: @@ -117,6 +122,7 @@ classes: to. range: DynamicTable required: true + inlined: true description: name: description description: Description of what this table region points to. @@ -147,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true colnames: @@ -176,6 +183,7 @@ classes: range: VectorData required: false multivalued: true + inlined: true tree_root: true AlignedDynamicTable: name: AlignedDynamicTable diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml index 5ba5b73..beb539c 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml @@ -18,6 +18,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -28,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml index 7ed736f..6085aa3 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true shape: @@ -52,17 +53,10 @@ classes: data: name: data description: The non-zero values in the matrix. - range: CSRMatrix__data + array: + dimensions: + - alias: number_of_non_zero_values + range: AnyType required: true multivalued: false tree_root: true - CSRMatrix__data: - name: CSRMatrix__data - description: The non-zero values in the matrix. - attributes: - name: - name: name - ifabsent: string(data) - range: string - required: true - equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml index 7cf669d..85675e7 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml @@ -27,6 +27,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -68,6 +69,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true target: @@ -75,6 +77,7 @@ classes: description: Reference to the target dataset that this index applies to. range: VectorData required: true + inlined: true tree_root: true ElementIdentifiers: name: ElementIdentifiers @@ -85,6 +88,7 @@ classes: name: name: name ifabsent: string(element_id) + identifier: true range: string required: true value: @@ -109,6 +113,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true table: @@ -117,6 +122,7 @@ classes: to. range: DynamicTable required: true + inlined: true description: name: description description: Description of what this table region points to. @@ -147,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true colnames: @@ -176,6 +183,7 @@ classes: range: VectorData required: false multivalued: true + inlined: true tree_root: true AlignedDynamicTable: name: AlignedDynamicTable diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml index 4ad36b7..f65f22b 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml @@ -18,6 +18,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -28,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml index 6167b42..8974bc0 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true shape: @@ -52,17 +53,10 @@ classes: data: name: data description: The non-zero values in the matrix. - range: CSRMatrix__data + array: + dimensions: + - alias: number_of_non_zero_values + range: AnyType required: true multivalued: false tree_root: true - CSRMatrix__data: - name: CSRMatrix__data - description: The non-zero values in the matrix. - attributes: - name: - name: name - ifabsent: string(data) - range: string - required: true - equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml index e1e7b63..9ffb97d 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml @@ -27,6 +27,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -68,6 +69,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true target: @@ -75,6 +77,7 @@ classes: description: Reference to the target dataset that this index applies to. range: VectorData required: true + inlined: true tree_root: true ElementIdentifiers: name: ElementIdentifiers @@ -85,6 +88,7 @@ classes: name: name: name ifabsent: string(element_id) + identifier: true range: string required: true value: @@ -109,6 +113,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true table: @@ -117,6 +122,7 @@ classes: to. range: DynamicTable required: true + inlined: true description: name: description description: Description of what this table region points to. @@ -147,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true colnames: @@ -176,6 +183,7 @@ classes: range: VectorData required: false multivalued: true + inlined: true tree_root: true AlignedDynamicTable: name: AlignedDynamicTable diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.base.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.base.yaml index bb9b324..ea83af3 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.base.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.base.yaml @@ -18,6 +18,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true @@ -28,6 +29,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.sparse.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.sparse.yaml index 842d1d6..9fd8ddd 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.sparse.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.sparse.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true shape: @@ -52,17 +53,10 @@ classes: data: name: data description: The non-zero values in the matrix. - range: CSRMatrix__data + array: + dimensions: + - alias: number_of_non_zero_values + range: AnyType required: true multivalued: false tree_root: true - CSRMatrix__data: - name: CSRMatrix__data - description: The non-zero values in the matrix. - attributes: - name: - name: name - ifabsent: string(data) - range: string - required: true - equals_string: data diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.table.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.table.yaml index a47fd09..940f1b7 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.table.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_common/v1_8_0/hdmf-common.table.yaml @@ -27,6 +27,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true description: @@ -68,6 +69,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true target: @@ -75,6 +77,7 @@ classes: description: Reference to the target dataset that this index applies to. range: VectorData required: true + inlined: true tree_root: true ElementIdentifiers: name: ElementIdentifiers @@ -85,6 +88,7 @@ classes: name: name: name ifabsent: string(element_id) + identifier: true range: string required: true value: @@ -109,6 +113,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true table: @@ -117,6 +122,7 @@ classes: to. range: DynamicTable required: true + inlined: true description: name: description description: Description of what this table region points to. @@ -147,6 +153,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true colnames: @@ -176,6 +183,7 @@ classes: range: VectorData required: false multivalued: true + inlined: true tree_root: true AlignedDynamicTable: name: AlignedDynamicTable diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml index 064f647..2a10ba2 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true elements: @@ -29,4 +30,5 @@ classes: elements range: VectorData required: true + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml index 05dc855..a962b8f 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true keys: @@ -31,18 +32,21 @@ classes: range: ExternalResources__keys required: true multivalued: false + inlined: true entities: name: entities description: A table for mapping user terms (i.e., keys) to resource entities. range: ExternalResources__entities required: true multivalued: false + inlined: true resources: name: resources description: A table for mapping user terms (i.e., keys) to resource entities. range: ExternalResources__resources required: true multivalued: false + inlined: true objects: name: objects description: A table for identifying which objects in a file contain references @@ -50,12 +54,14 @@ classes: range: ExternalResources__objects required: true multivalued: false + inlined: true object_keys: name: object_keys description: A table for identifying which objects use which keys. range: ExternalResources__object_keys required: true multivalued: false + inlined: true tree_root: true ExternalResources__keys: name: ExternalResources__keys @@ -66,6 +72,7 @@ classes: name: name: name ifabsent: string(keys) + identifier: true range: string required: true equals_string: keys @@ -86,6 +93,7 @@ classes: name: name: name ifabsent: string(entities) + identifier: true range: string required: true equals_string: entities @@ -130,6 +138,7 @@ classes: name: name: name ifabsent: string(resources) + identifier: true range: string required: true equals_string: resources @@ -158,6 +167,7 @@ classes: name: name: name ifabsent: string(objects) + identifier: true range: string required: true equals_string: objects @@ -186,6 +196,7 @@ classes: name: name: name ifabsent: string(object_keys) + identifier: true range: string required: true equals_string: object_keys diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml index 94b3194..1f49508 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true elements: @@ -29,4 +30,5 @@ classes: elements range: VectorData required: true + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml index a1b6ec0..89023ae 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true keys: @@ -31,18 +32,21 @@ classes: range: ExternalResources__keys required: true multivalued: false + inlined: true entities: name: entities description: A table for mapping user terms (i.e., keys) to resource entities. range: ExternalResources__entities required: true multivalued: false + inlined: true resources: name: resources description: A table for mapping user terms (i.e., keys) to resource entities. range: ExternalResources__resources required: true multivalued: false + inlined: true objects: name: objects description: A table for identifying which objects in a file contain references @@ -50,12 +54,14 @@ classes: range: ExternalResources__objects required: true multivalued: false + inlined: true object_keys: name: object_keys description: A table for identifying which objects use which keys. range: ExternalResources__object_keys required: true multivalued: false + inlined: true tree_root: true ExternalResources__keys: name: ExternalResources__keys @@ -66,6 +72,7 @@ classes: name: name: name ifabsent: string(keys) + identifier: true range: string required: true equals_string: keys @@ -86,6 +93,7 @@ classes: name: name: name ifabsent: string(entities) + identifier: true range: string required: true equals_string: entities @@ -130,6 +138,7 @@ classes: name: name: name ifabsent: string(resources) + identifier: true range: string required: true equals_string: resources @@ -158,6 +167,7 @@ classes: name: name: name ifabsent: string(objects) + identifier: true range: string required: true equals_string: objects @@ -198,6 +208,7 @@ classes: name: name: name ifabsent: string(object_keys) + identifier: true range: string required: true equals_string: object_keys diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml index 4991b33..96f372c 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true elements: @@ -29,4 +30,5 @@ classes: elements range: VectorData required: true + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml index ca25659..d0909a2 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true keys: @@ -31,18 +32,21 @@ classes: range: ExternalResources__keys required: true multivalued: false + inlined: true files: name: files description: A table for storing object ids of files used in external resources. range: ExternalResources__files required: true multivalued: false + inlined: true entities: name: entities description: A table for mapping user terms (i.e., keys) to resource entities. range: ExternalResources__entities required: true multivalued: false + inlined: true objects: name: objects description: A table for identifying which objects in a file contain references @@ -50,12 +54,14 @@ classes: range: ExternalResources__objects required: true multivalued: false + inlined: true object_keys: name: object_keys description: A table for identifying which objects use which keys. range: ExternalResources__object_keys required: true multivalued: false + inlined: true tree_root: true ExternalResources__keys: name: ExternalResources__keys @@ -66,6 +72,7 @@ classes: name: name: name ifabsent: string(keys) + identifier: true range: string required: true equals_string: keys @@ -86,6 +93,7 @@ classes: name: name: name ifabsent: string(files) + identifier: true range: string required: true equals_string: files @@ -106,6 +114,7 @@ classes: name: name: name ifabsent: string(entities) + identifier: true range: string required: true equals_string: entities @@ -144,6 +153,7 @@ classes: name: name: name ifabsent: string(objects) + identifier: true range: string required: true equals_string: objects @@ -201,6 +211,7 @@ classes: name: name: name ifabsent: string(object_keys) + identifier: true range: string required: true equals_string: object_keys diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml index 6332939..28ae7e8 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true elements: @@ -29,4 +30,5 @@ classes: elements range: VectorData required: true + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml index e2acf65..75f3938 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml @@ -22,6 +22,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true keys: @@ -31,18 +32,21 @@ classes: range: ExternalResources__keys required: true multivalued: false + inlined: true files: name: files description: A table for storing object ids of files used in external resources. range: ExternalResources__files required: true multivalued: false + inlined: true entities: name: entities description: A table for mapping user terms (i.e., keys) to resource entities. range: ExternalResources__entities required: true multivalued: false + inlined: true objects: name: objects description: A table for identifying which objects in a file contain references @@ -50,18 +54,21 @@ classes: range: ExternalResources__objects required: true multivalued: false + inlined: true object_keys: name: object_keys description: A table for identifying which objects use which keys. range: ExternalResources__object_keys required: true multivalued: false + inlined: true entity_keys: name: entity_keys description: A table for identifying which keys use which entity. range: ExternalResources__entity_keys required: true multivalued: false + inlined: true tree_root: true ExternalResources__keys: name: ExternalResources__keys @@ -72,6 +79,7 @@ classes: name: name: name ifabsent: string(keys) + identifier: true range: string required: true equals_string: keys @@ -92,6 +100,7 @@ classes: name: name: name ifabsent: string(files) + identifier: true range: string required: true equals_string: files @@ -112,6 +121,7 @@ classes: name: name: name ifabsent: string(entities) + identifier: true range: string required: true equals_string: entities @@ -142,6 +152,7 @@ classes: name: name: name ifabsent: string(objects) + identifier: true range: string required: true equals_string: objects @@ -199,6 +210,7 @@ classes: name: name: name ifabsent: string(object_keys) + identifier: true range: string required: true equals_string: object_keys @@ -227,6 +239,7 @@ classes: name: name: name ifabsent: string(entity_keys) + identifier: true range: string required: true equals_string: entity_keys diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.experimental.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.experimental.yaml index c6cf1d4..208be72 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.experimental.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.experimental.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true elements: @@ -29,4 +30,5 @@ classes: elements range: VectorData required: true + inlined: true tree_root: true diff --git a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.resources.yaml b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.resources.yaml index 7478fe1..dcaf960 100644 --- a/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.resources.yaml +++ b/nwb_models/src/nwb_models/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.resources.yaml @@ -21,6 +21,7 @@ classes: attributes: name: name: name + identifier: true range: string required: true keys: @@ -30,18 +31,21 @@ classes: range: HERD__keys required: true multivalued: false + inlined: true files: name: files description: A table for storing object ids of files used in external resources. range: HERD__files required: true multivalued: false + inlined: true entities: name: entities description: A table for mapping user terms (i.e., keys) to resource entities. range: HERD__entities required: true multivalued: false + inlined: true objects: name: objects description: A table for identifying which objects in a file contain references @@ -49,18 +53,21 @@ classes: range: HERD__objects required: true multivalued: false + inlined: true object_keys: name: object_keys description: A table for identifying which objects use which keys. range: HERD__object_keys required: true multivalued: false + inlined: true entity_keys: name: entity_keys description: A table for identifying which keys use which entity. range: HERD__entity_keys required: true multivalued: false + inlined: true tree_root: true HERD__keys: name: HERD__keys @@ -71,6 +78,7 @@ classes: name: name: name ifabsent: string(keys) + identifier: true range: string required: true equals_string: keys @@ -91,6 +99,7 @@ classes: name: name: name ifabsent: string(files) + identifier: true range: string required: true equals_string: files @@ -111,6 +120,7 @@ classes: name: name: name ifabsent: string(entities) + identifier: true range: string required: true equals_string: entities @@ -141,6 +151,7 @@ classes: name: name: name ifabsent: string(objects) + identifier: true range: string required: true equals_string: objects @@ -198,6 +209,7 @@ classes: name: name: name ifabsent: string(object_keys) + identifier: true range: string required: true equals_string: object_keys @@ -226,6 +238,7 @@ classes: name: name: name ifabsent: string(entity_keys) + identifier: true range: string required: true equals_string: entity_keys