diff --git a/docs/best_practices/extensions.rst b/docs/best_practices/extensions.rst index c6f7b43c9..5f5d0298f 100644 --- a/docs/best_practices/extensions.rst +++ b/docs/best_practices/extensions.rst @@ -16,15 +16,104 @@ If an extension is required, tutorials for the process may be found through the It is also encouraged for extensions to contain their own check functions for their own best practices. See the` :ref:`adding_custom_checks` section of the Developer Guide for how to do this. +Define new ``neurodata_types`` at the top-level (do not nest type definitions) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Rather than nesting definitions of ``neurodata_types``, all new types should be defined +at the top-level of the schema. To include a specific ``neurodata_type`` in another type +use the ``neurodata_type_inc`` key instead. For example: -Use Existing Neurodata Types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. tabs:: + + .. tab:: Do This + + .. tabs:: + + .. code-tab:: py Python + + from pynwb.spec import NWBGroupSpec + + # Define the first type + type1_ext = NWBGroupSpec( + name='custom_type1', + doc='Example extension type 1', + neurodata_type_def='MyNewType1', + neurodata_type_inc='LabMetaData', + ) + + # Then define the second type and include the first type + type2_ext = NWBGroupSpec( + name='custom_type2', + doc='Example extension type 2', + neurodata_type_def='MyNewType2', + groups=[NWBGroupSpec(neurodata_type_inc='MyNewType1', + doc='Included group of ype MyNewType1')] + ) + + .. code-tab:: yaml YAML + + groups: + - neurodata_type_def: MyNewType1 + neurodata_type_inc: LabMetaData + name: custom_type1 + doc: Example extension type 1 + - neurodata_type_def: MyNewType2 + neurodata_type_inc: NWBContainer + name: custom_type2 + doc: Example extension type 2 + groups: + - neurodata_type_inc: MyNewType1 + doc: Included group of ype MyNewType1 + + .. tab:: DON'T do this + + .. tabs:: + + .. code-tab:: py Python + + from pynwb.spec import NWBGroupSpec + + # Do NOT define a new type via ``neurodata_type_def`` within the + # definition of another type. Always define the types separately + # and use ``neurodata_type_inc`` to include the type + type2_ext = NWBGroupSpec( + name='custom_type2', + doc='Example extension type 2', + neurodata_type_def='MyNewType2', + groups=[NWBGroupSpec( + name='custom_type1', + doc='Example extension type 1', + neurodata_type_def='MyNewType1', + neurodata_type_inc='LabMetaData', + )] + ) + + .. code-tab:: yaml YAML + + groups: + - neurodata_type_def: MyNewType2 + neurodata_type_inc: NWBContainer + name: custom_type2 + doc: Example extension type 2 + groups: + - neurodata_type_def: MyNewType1 + neurodata_type_inc: LabMetaData + name: custom_type1 + doc: Example extension type 1 + +Build on and Reuse Existing Neurodata Types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When possible, use existing types when creating extensions either by creating new ``neurodata_types`` that inherit from existing ones, or by creating ``neurodata_types`` that contain existing ones. Building on existing types facilitates the reuse of existing functionality and interpretation of the data. If a community extension already exists that has a -similar scope, it is preferable to use that extension rather than creating a new one. +similar scope, it is preferable to use that extension rather than creating a new one. For example: + +* Extend ``TimeSeries`` for storing timeseries data. NWB provides main types of ``TimeSeries`` + and you should identify the most specific type of ``TimeSeries`` relevant for your use case + (e.g., extend ``ElectricalSeries`` to define a new kind of electrical recording). +* Extend ``DynamicTable`` to store tabular data. +* Extend ``TimeIntervals`` to store specific annotations of intervals in time. Provide Documentation @@ -45,3 +134,68 @@ anybody who receives the data also receives the necessary data to interpret it. .. note:: In :pynwb-docs:`PyNWB <>`, the extension is cached automatically. This can be specified explicitly with ``io.write(filepath, cache_spec=True)`` + + +Use Attributes for small metadata related to a particular data object (Group or Dataset) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Attributes should be used mainly to store small metadata (usually less than 64 Kbytes) that +is associated with a particular Group or Dataset. Typical uses of attributes are, e.g., to +define the ``unit`` of measurement of a dataset or to store a short ``description`` of +a group or dataset. For larger data, datasets should be used instead. + +In practice, the main difference is that in PyNWB and MatNWB all attributes are read into memory when reading the +NWB file. If you would like to allow users to read a file without reading all of these particular data values, use a +Dataset. + + +Link data to relevant metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often metadata relevant to a particular type of data is stored elsewhere, e.g., information +about the ``Device`` used. To ensure relevant metadata can be uniquely identified, the data +should include links to the relevant metadata. NWB provides a few key mechanisms for linking: + +* Use ``links`` (defined via ``NWBLinkSpec``) to link to a particular dataset or group +* Use ``DynamicTableRegion`` to link to a set of rows in a ``DynamicTable`` +* Use a ``dataset`` with an object reference data type to store collections of links + to other objects, e.g., the following dtype to define a dataset of links to ``TimeSeries`` + +.. code-block:: yaml + + dtype: + target_type: TimeSeries + reftype: object + + +Best practices for object names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Names for groups, datasets, attributes, or links should typically: + +* **Use lowercase letters only** +* **Use ``_`` instead of `` `` to separate parts in names**. E.g., use the name + ``starting_time`` instead of ``starting time`` +* **Explicit**. E.g., avoid the use of ambiguous abbreviation in names. + + +Best practices for naming ``neurodata_types`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For defining new types via ``neurodata_type_def`` use: + +* **Use camelcase:** notation, i.e., names of types should NOT include spaces, + always start with an uppercase letter, and use a single capitalized letter to + separate parts of the name. E.g,. ``neurodata_type_def: LaserMeasurement`` +* **Use the postfix ``Series`` when extending a ``TimeSeries`` type.** E.g., when + creating a new ``TimeSeries`` for laser measurements then add ``Series`` to + the type name, e.g,. ``neurodata_type_def: LaserMeasurementSeries`` +* **Use the postfix ``Table`` when extending a ``DynamicTable`` type.** e.g., + ``neurodata_type_def: LaserSettingsTable`` +* **Explicit**. E.g., avoid the use of ambiguous abbreviation in names. + +Use the ``ndx-template`` to create new extensions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By using the :nwb_extension_git:`ndx-template` to create new extensions helps ensure +that extensions can be easily shared and reused and published via the :ndx-catalog:`NDX Catalog <>`. diff --git a/docs/conf.py b/docs/conf.py index 1cbab3818..65b87412f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,6 +19,7 @@ "sphinx.ext.intersphinx", "sphinx.ext.extlinks", "sphinx_copybutton", + "sphinx_tabs.tabs", ] templates_path = ["_templates"] master_doc = "index" diff --git a/docs/conf_extlinks.py b/docs/conf_extlinks.py index be70195f8..ea42b6279 100644 --- a/docs/conf_extlinks.py +++ b/docs/conf_extlinks.py @@ -33,6 +33,8 @@ "black-coding-style": ("https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html%s", None), "ncbi": ("https://www.ncbi.nlm.nih.gov/taxonomy%s", None), "ontobee": ("https://ontobee.org/%s", None), + "ndx-catalog": ("https://nwb-extensions.github.io/%s", None), + "nwb_extension_git": ("https://github.com/nwb-extensions/%s", None), } # Use this for mapping for links to commonly used documentation diff --git a/requirements-rtd.txt b/requirements-rtd.txt index 71bd1014d..e426f6b24 100644 --- a/requirements-rtd.txt +++ b/requirements-rtd.txt @@ -5,3 +5,4 @@ sphinx==5.1.1 sphinx_rtd_theme==0.5.1 readthedocs-sphinx-search==0.1.0rc3 sphinx-copybutton==0.5.0 +sphinx-tabs