mirror of
https://github.com/p2p-ld/nwb-linkml.git
synced 2024-11-10 00:34:29 +00:00
89 lines
No EOL
3.2 KiB
Python
89 lines
No EOL
3.2 KiB
Python
"""
|
|
Adapter for NWB groups to linkml Classes
|
|
"""
|
|
import pdb
|
|
from typing import List
|
|
from linkml_runtime.linkml_model import ClassDefinition, SlotDefinition
|
|
|
|
from nwb_schema_language import Dataset, Group, ReferenceDtype, CompoundDtype, DTypeType
|
|
from nwb_linkml.adapters.classes import ClassAdapter
|
|
from nwb_linkml.adapters.dataset import DatasetAdapter
|
|
from nwb_linkml.adapters.adapter import BuildResult
|
|
from nwb_linkml.maps import QUANTITY_MAP
|
|
|
|
class GroupAdapter(ClassAdapter):
|
|
cls: Group
|
|
|
|
def build(self) -> BuildResult:
|
|
|
|
|
|
nested_res = self.build_subclasses()
|
|
# we don't propagate slots up to the next level since they are meant for this
|
|
# level (ie. a way to refer to our children)
|
|
res = self.build_base(extra_attrs=nested_res.slots)
|
|
# we do propagate classes tho
|
|
res.classes.extend(nested_res.classes)
|
|
|
|
return res
|
|
|
|
def handle_children(self, children: List[Group]) -> BuildResult:
|
|
"""
|
|
Make a special LinkML `children` slot that can
|
|
have any number of the objects that are of `neurodata_type_inc` class
|
|
|
|
Args:
|
|
children (List[:class:`.Group`]): Child groups
|
|
|
|
"""
|
|
child_slot = SlotDefinition(
|
|
name='children',
|
|
multivalued=True,
|
|
any_of=[{'range': cls.neurodata_type_inc} for cls in children]
|
|
)
|
|
return BuildResult(slots=[child_slot])
|
|
|
|
def build_subclasses(self) -> BuildResult:
|
|
"""
|
|
Build nested groups and datasets
|
|
|
|
Create ClassDefinitions for each, but then also create SlotDefinitions that
|
|
will be used as attributes linking the main class to the subclasses
|
|
"""
|
|
# Datasets are simple, they are terminal classes, and all logic
|
|
# for creating slots vs. classes is handled by the adapter class
|
|
dataset_res = BuildResult()
|
|
for dset in self.cls.datasets:
|
|
# if dset.name == 'timestamps':
|
|
# pdb.set_trace()
|
|
dset_adapter = DatasetAdapter(cls=dset, parent=self)
|
|
dataset_res += dset_adapter.build()
|
|
|
|
# Actually i'm not sure we have to special case this, we could handle it in
|
|
# i/o instead
|
|
|
|
# Groups are a bit more complicated because they can also behave like
|
|
# range declarations:
|
|
# eg. a group can have multiple groups with `neurodata_type_inc`, no name, and quantity of *,
|
|
# the group can then contain any number of groups of those included types as direct children
|
|
|
|
# group_res = BuildResult()
|
|
# children = []
|
|
# for group in self.cls.groups:
|
|
# if not group.name and \
|
|
# group.quantity == '*' and \
|
|
# group.neurodata_type_inc:
|
|
# children.append(group)
|
|
# else:
|
|
# group_adapter = GroupAdapter(cls=group, parent=self)
|
|
# group_res += group_adapter.build()
|
|
#
|
|
# group_res += self.handle_children(children)
|
|
|
|
group_res = BuildResult()
|
|
for group in self.cls.groups:
|
|
group_adapter = GroupAdapter(cls=group, parent=self)
|
|
group_res += group_adapter.build()
|
|
|
|
res = dataset_res + group_res
|
|
|
|
return res |