diff --git a/docs/source/HedMatlabTools.md b/docs/source/HedMatlabTools.md index ce27df8..7fdfce9 100644 --- a/docs/source/HedMatlabTools.md +++ b/docs/source/HedMatlabTools.md @@ -5,21 +5,14 @@ The HED MATLAB tools allow validation, summary, search, factorization, data epoc and other HED processing in MATLAB by providing MATLAB wrappers for the Python HEDTools. These MATLAB wrappers allow MATLAB users to use HED without learning Python. -The HED MATLAB tools provide the following interface to HEDTools as explained in more detail in the following sections. - -| Matlab | Purpose | Returns | -| ------ |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------| -| `getHedAnnotations` | Assemble a cell array of HED string annotations
from data such as events. | cell array of `char` | -| `getHedFactors` | Given a cell array of *n* HED string annotations and
a cell array of *m* of HED queries
return an *n* x *m* array indicating whether
each annotation satisfied the respective query. | *n* x *m* array of 1's and 0's. | -| `validateEvents` | Validate HED events data in various formats
and return a printable string with HED issues. | `char` array | -| `validateSidecar` | Validate HED in a BIDS sidecar (in various formats)
and return a printable string with HED issues. | `char` array | -| `validateTags` | Validate a HED annotation (in various formats)
and return a printable string with HED issues. | `char` array | +## Tool overview +The HED MATLAB tools provide the following interface to HEDTools as explained in more detail in the following sections. The MATLAB HEDTools package provides two interchangeable implementations of these functions -- calling the HED Python tools through a web service or directly calling the Python HEDTools. -![HED MATLAB tools](./_static/images/HedToolsSchematic.png). +![HED MATLAB tools](./_static/images/MatLabHedToolsCloseFit.png). -Both approaches take as input, MATLAB data and translate these values as needed to access the HEDTools. +Both approaches take MATLAB data as input and translate these values as needed to access the HEDTools. After making the call, the implementation translates the results back into MATLAB data types. Calling the HED MATLAB tools using web services requires no installation beyond downloading the @@ -28,39 +21,421 @@ HED MATLAB package, but its use requires Internet access. Using direct calls to the Python HEDTools from MATLAB is more efficient and provides additional functionality not available through the web service interface. However, direct calls require a one-time setup of Python for your MATLAB installation. -You will need at least MATLAB version R2020b, -since the Python HEDTools require at least Python 3.8. -See [**MathWorks Python Compatibility Docs**](https://www.mathworks.com/support/requirements/python-compatibility.html) for additional information. +See [**MATLAB Python install**](#matlab-python-install) for additional setup information. -Another option is to use the [**EEGLAB HEDTools plug-in**](./HedAndEEGLAB.md) -integration for HED. +Another option is to use the [**EEGLAB HEDTools plug-in**](./HedAndEEGLAB.md) integration for HED. The EEGLAB plug-ins provide easy access through the EEGLAB GUI interface +### The HED MATLAB interface + +The HED MATLAB tools provide the following routines for accessing HED: + +```{list-table} MATLAB HEDTools interface (* indicates optional Name-Value parameter) +:header-rows: 1 +:name: matlab-hedtools-interface + +* - MATLAB + - Purpose + - Parameters + - Returns +* - `getHedAnnotations` + - Assemble a cell array of HED string
annotations from data such as events. + - `events`
`sidecar`
`removeTypesOn`
`includeContext`
`replaceDefs` + - cell array of `char` +* - `searchHed` + - Given a cell array of *n* HED string
annotations and a cell array of
*m* of HED queries return an
*n* x *m* array indicating whether
each annotation satisfied the
respective query. + - `annotations`
`queries` + - *n* x *m* array of 1's and 0's. +* - `validateEvents` + - Validate HED events data in various
format sand return a printable
string with HED issues. + - `events`
`sidecar`
`checkWarnings`* + - `char` +* - `validateSidecar` + - Validate HED in a BIDS sidecar
(in various formats) and return a
printable string with HED issues. + - `sidecar`
`checkWarnings`* + - `char` +* - `validateTags` + - Validate a HED annotation
(in various formats) and return a
printable string with HED issues. + - `hedTags`
`checkWarnings`* + - `char` +``` + + +The parameters are defined as follows: + +```{list-table} Parameters for MATLAB HED tools (* indicates optional Name-Value parameter). +:header-rows: 1 +:name: matlab-hedtools-parameters + +* - Parameter + - Description + - MATLAB type +* - `annotations` + - Cell array with hed annotations. + - cell array of `char` or `string`,
(or of `HedString` if Python) +* - `events` + - Tabular data with HED tags
ex: EEGLAB EEG.events or BIDS _events.tsv. + - `char`, `string`, `struct`
(or `TabularInput` if Python) +* - `sidecar` + - Data representing a BIDS-type sidecar
containing metadata for tabular data. + - `char`, `string`, `struct`
(or `Sidecar` if Python) +* - `hedtags` + - A HED annotation string. + - `char`, `string`
(or `HedString` if Python) +* - `queries` + - Valid HED query strings. + - Cell array of `char` or `string` +* - `checkWarnings`* + - Optional flag indicating whether to check
for warnings during validation. + - `true` or `false` +* - `includeContext`* + - Optional flag indicating whether to
include the event context in assembling HED tags. + - `true` or `false` +* - `removeTypesOn`* + - Optional flag indicating whether to
remove *Condition-variable* and *Task* tags. + - `true` or `false` +* - `replaceDefs`* + - Optional flag indicataing whether to
eplace *Def* tags with contents
of their definitions. + - `true` or `false` +``` + (what-to-download-anchor)= -## What to download +### What to download The HED MATLAB tools can be downloaded from the [**hed-matlab**](https://github.com/hed-standard/hed-matlab) GitHub repository. -You should add the **hedmat** directory and all of its subdirectories to your path. +**Add the hedmat directory and all of its subdirectories to your path.** The following table describes the directories of this repository: -| Directory | Description | -|-------------------------------|-----------------------| -| *data* | Data used for the demos and tests. | -| *docs* | Source code for the documentation. | -| *hedmat/hedtools* | Matlab interface for the HED tools. | -| *hedmat/remodeling_demos* | Demos of calling the HED remodeling tools. | -| *hedmat/utilities* | General purpose utilities. | -| *hedmat/web_services_demos* | Demos of directly using the HED web services (without hedtools). | -| *tests* | Unit tests for MATLAB. (Execute `run_tests.m` to run all unit tests.)
Note | +```{list-table} Directories in hed-matlab GitHub repo. +:header-rows: 1 +:name: matlab-hedtools-download + +* - Directory + - Description +* - Directory + - Description +* - *data* + - Data used for the demos and tests. +* - *docs* + - Source code for the documentation. +* - *hedmat/hedtools* + - MATLAB interface for the HED tools. +* - *hedmat/remodeling_demos* + - Demos of calling the HED remodeling tools. +* - *hedmat/utilities* + - General purpose utilities. +* - *hedmat/web_services_demos* + - Demos of directly using the HED web services (without hedtools). +* - *tests* + - Unit tests for MATLAB. (Execute `run_tests.m` to run all unit tests.) +``` + +## Using MATLAB HEDTools + +This section gives some examples of using the HED MATLAB tools. + +### Getting a HEDTools object + +The HED MATLAB tools are all called by getting a `HedTools` object and then making the calls through this object. +Use the `getHedTools` function to get a `HedTools` object. + +The following example gets a `HedTools` object using version 8.2.0 of the HED schema (standard vocabulary) +and the webservice available at [**https://hedtools.org/hed**](https://hedtools.org/hed). + +````{admonition} Access HED tools through web services. +:class: tip + +```matlab +hed = getHedTools('8.2.0', 'https://hedtools.org/hed'); +``` +```` + +The first parameter is number of the HED version to use, and the second parameter is the URL of the web service. +The `hed` returned by this call is `HedToolsService`, which implements the interface by calls to HED web services. +The [**https://hedtools.org/hed**](https://hedtools.org/hed) is the primary server for the HED online tools. +An alternative server for the web services is [**https://hedtools.org/hed_dev**](https://hedtools.org/hed_dev). +This is the HED development server, which deploys the latest features. + +If you have installed the HED Python tools, you can access the HED MATLAB interface using direct calls to Python. + +````{admonition} Access HED tools through direct Python calls. +:class: tip + +```matlab +hed = getHedTools('8.2.0'); +``` +```` +If you call the `getHedTools` with only the HED version number parameter, +`getHedTools` assumes you are using direct calls to Python and returns a `HedToolsPython` object. +The HED MATLAB interface calls and behavior are identical whether you use the services or direct calls. +You must have the HED Python tools installed to use direct calls. +See [**MATLAB Python install**](#matlab-python-install). + +### Calling a tool +Once you have the HED tools object, you can use it to call the tools listed above +as illustrated in the following example: + +````{admonition} Validate a string containing HED tags. +:class: tip + +```matlab +issues = validateTags(hed, 'Sensory-event,Red,Blech,(Image, Banana)'); +``` +```` +The `issues` is a printable `char` array. +The HED tags string in the above example has two unrecognized tags: *Blech* and *Banana*. +The call to `validateTags` produces the following `issues` message: + +````{admonition} Validate a string containing HED tags. +```text + TAG_INVALID: 'Blech' in Blech is not a valid base hed tag. + TAG_INVALID: 'Banana' in Banana is not a valid base hed tag. +``` +```` + +### Input of events + +Events are markers on the experimental timeline that indicate something of interest. +One of HED's primary purposes is to annotation event markers with usable metadata to assist in analysis. + +Events are generally represented in a tabular input form with column names. +Each row in such a table corresponds to information associated with one event marker. + +```{list-table} Example events input +:header-rows: 1 +:name: example-events-tabular-input + +* - row # + - onset + - latency + - type + - modifier +* - 1 + - 8.50 + - 851 + - show + - A2 +* - 2 + - 8.90 + - 891 + - show + - A5 +* - 3 + - 9.00 + - 901 + - resp + - key1 +* - 4 + - onset + - 1001 + - resp + - key2 +``` + +HED expects that event files have an `onset` column that provides the +time in seconds of the event marker relative to the experimental timeline. + +When this data is stored in a tab-separated value file called `test.tsv`, +it can be read as: + +````{admonition} Read a tab-separated value file into a char array. +:class: tip + +```matlab +events = fileread('test.tsv') +``` +The events is a char array: + +```text + 'onset latency type modifier + 8.50 851 show A2 + 8.90 891 show A5 + 9.00 901 resp but1 + 10.00 1001 resp but2' +``` +```` + +The same data can also be stored in MATLAB `struct` array: + +````{admonition} Read a tab-separated value file into a char array. +:class: tip + +```matlab +events(1) = struct('onset', 8.5, 'latency', 851, 'type', 'show', 'modifier', 'A2'); +events(2) = struct('onset', 8.9, 'latency', 891, 'type', 'show', 'modifier', 'A5'); +events(3) = struct('onset', 9, 'latency', 901, 'type', 'resp', 'modifier', 'but1'); +events(4) = struct('onset', 10, 'latency', 1001, 'type', 'resp', 'modifier', 'but2'); +``` +Displaying events on the MATLAB command line just gives the overall structure: + +```text + 1×4 struct array with fields: + + onset + latency + type + modifier +``` +but the MATLAB workspace editor provides a more informative view: +![ExampeMATLABStruct](./_static/images/MatLabEventsStruct.png). + +```` + +Once you have events data, it is easy to validate the HED +associated with this data as shown by the following example: + +````{admonition} Validate events data. +:class: tip + +```matlab +issues = hed.validateEvents(events, sidecar); +``` +```` + +### Input of sidecars +A tabular dataset may have a `HED` column whose entries provide HED +annotations for the individual event markers represented by the rows. +However, a more typical approach to annotation is to provide +an additional dictionary, often called a sidecar. +In [**BIDS**](https://bids.neuroimaging.io/) the sidecar dictionaries +are represented in [**JSON**](https://www.json.org/json-en.html) format. +The MATLAB `jsonencode` and `jsondecode` translate various MATLAB data +types into a JSON-compliant `char` value. + +HED expects that sidecars will follow the BIDS format for sidecars associated +with tabular files. The top-level JSON is a dictionary with keys that are the column +names of the associated tabular file. Each key points to a dictionary of metadata for a column. +One of the keys in this second dictionary can be `HED` as illustrated by the following example. + +````{admonition} JSON sidecar for the events of previous section. +:class: tip + +```json +{ + "type": { + "HED": { + "show": "Sensory-presentation, Experimental-stimulus", + "resp": "Agent-action, Participant-response" + } + }, + "modifier": { + "HED": { + "A2": "(Face, Image)", + "A5": "(Animal/Horse, Image)", + "but1": "(Press, (Left, Mouse-button))", + "but2": "(Press, (Right, Mouse-button))" + } + } +} +``` +```` + +### Assembling HED annotations + +The `getHedAnnotations` tool assembles the complete HED annotation for each +event marker (i.e., each row in an event structure). -(wrappers-for-hedtools-anchor)= -## Wrappers for HEDTools + +````{admonition} Assembling HED annotations. +:class: tip + +```matlab +annotations = hed.getHedAnnotations(events, sidecar); +``` +```` +Here `annotations` is a cell array with the same number of rows as `events` +containing the `char` HED annotations for the `events`. +The HEDTools look up the HED annotation for each element in a row of an events table and concatenate +those together (comma-separated) to form the HED annotation for the event marker. + +For the event data and sidecar defined in the previous sections, +the sidecar provides HED annotations for the `type` and `modifier` columns. +The resulting HED annotations for each row are given in the following table: + +```{list-table} Example assembled HED annotations +:header-rows: 1 +:name: assembled-hed-annotation-example + +* - row + - HED annotation +* - 1 + - *Sensory-presentation,Experimental-stimulus,(Face,Image)* +* - 2 + - *Sensory-presentation,Experimental-stimulus,(Animal/Horse,Image)* +* - 3 + - *Agent-action,Participant-response,(Press,(Left,Mouse-button))* +* - 4 + - *Agent-action,Participant-response,(Press,(Right,Mouse-button))* +``` + +The annotation for the first row consists of the HED tags for the `show` value in column `type` +(i.e., *Sensory-presentation,Experimental-stimulus*) concatenated with the +HED tags for the `A2` value in column `modifier` (i.e., *(Face,Image)*). +Note: HED annotations are unordered, so the tags may appear in any order. +Use parentheses to group tags as appropriate. + +The example annotations do not use any `Def` tags, so the `replaceDefs` option is not relevant. +Users can give a name to particular string of HED tags using the `Definition` tag and then use +the `Def` tag with that name as a shortcut in annotations. The `Def` tags are useful for +tag strings that are frequently used and are required to define event processes. +However, when assembling annotations to search for tags, you usually want to replace these shortcut `Def` +tags with the actual HED tags they represent. Thus, the default value of `replaceDefs` is true. +See [**Advanced annotation**](https://hed-specification.readthedocs.io/en/latest/05_Advanced_annotation.html) +for details. + +Since no special tags defining extended event processes are used (i.e., `Onset`, `Offset`, `Duration`) +in the example annotations, the `includeContext` option has no effect in this example. +If `includeContext` is true (the default) event processes contribute their tags to the +`Event-context` of time markers that fall in their scope. See +[**Advanced annotation**](https://hed-specification.readthedocs.io/en/latest/05_Advanced_annotation.html) +for an explanation of this. + +The example annotation does not contain any `Condition-variable` or `Task` tags, so `removeTypesOn` has no effect. +Typically `removeTypesOn` should be true to remove the effects of these tags for ordinary assembly and +searching as extraction of design matrices is a separate operation. +See [**HED conditions and design matrices**](https://www.hed-resources.org/en/latest/HedConditionsAndDesignMatrices.html) +for additional information. + +### Searching HED annotations + +The `searchHed` tool takes a cell array of *n* HED annotations and a cell array of *m* HED search queries +and returns an *n* x *m* array of 1's and 0's indicating whether the annotations satisfy the queries. + +````{admonition} Example search of HED annotations +:class: tip + +```matlab +factors = hed.searchHed(annotations, {'Sensory-event', 'Agent-action'}); +``` +```` + +The result of this query is the following: + +```{list-table} Query result for the sample annotation. +:header-rows: 0 +:name: query-results + +* - 1 + - 0 +* - 1 + - 0 +* - 0 + - 1 +* - 0 + - 1 +``` +The queries can be quite complex as described in the +[**HED search guide**](https://www.hed-resources.org/en/latest/HedSearchGuide.html). + +(matlab-python-install-anchor)= +## MATLAB Python install Although MATLAB began python support of python in 2014, **you must be using MATLAB version 2020b or later** with the HEDTools because the current version of the HEDTools requires Python 3.8 or later. -See [**compatible version of Python**](https://www.mathworks.com/support/requirements/python-compatibility.html) for a listing of which +See [**compatible version of Python**](https://www.mathworks.com/support/requirements/python-compatibility.html) +for a listing of which Python versions are compatible with which versions of MATLAB. **Note:** For your reference, the source for `hedtools` is the @@ -71,6 +446,10 @@ but the tutorials and tool documentation for `hedtools` on [**HED Resources**](https://www.hed-resources.org/en/latest/index.html) site provides more examples of use. +You will need at least MATLAB version R2020b, +since the Python HEDTools require at least Python 3.8. +See [**MathWorks Python Compatibility Docs**](https://www.mathworks.com/support/requirements/python-compatibility.html) for additional information. + ### Installing Python The greatest difficulty for users who are unfamiliar with Python is @@ -127,7 +506,7 @@ with executable located at `C:\Program Files\Python\Python39\python.EXE`. ``` ```` -If MATLAB has already knows about a suitable Python version that is at least 3.7, +If MATLAB has already knows about a suitable Python version that is at least 3.8, you are ready to go to [**Step 4: Install HEDTools**](step-4-install-hedtools-anchor). Keep track of the location of the Python executable. @@ -290,47 +669,26 @@ Using HEDTOOLS version: {'date': '2022-06-20T14:40:24-0500', 'dirty': False, 'er ``` ```` -### Calling HEDTools from MATLAB - -The wrapper functions provide the basic functionality for the common operations -Wrapper functions are provided to some of the more commonly used -functions in the HEDTools suite. -The following example shows the MATLAB wrapper function -[**validateHedInBids.m**](https://raw.githubusercontent.com/hed-standard/hed-matlab/main/hedmat/hedtools_wrappers/validateHedInBids.m), -which contains the underlying calls to HEDTools Python BIDs validation. - - -````{admonition} A MATLAB wrapper function for validating HED in a BIDS dataset. -:class: tip +### MATLAB functions for Python -```matlab -function issueString = validateHedInBids(dataPath) - py.importlib.import_module('hed'); - bids = py.hed.tools.BidsDataset(dataPath); - issues = bids.validate(); - issueString = string(py.hed.get_printable_issue_string(issues)); +The following table lists the relevant MATLAB functions that are available. +You should refer to the help facility for your version of MATLAB to get the details of what is +supported for your version of MATLAB. -``` +| MATLAB command | Purpose | +| -------------- | --------| +| `pyenv` | Setup your Python environment in MATLAB.
Without arguments outputs information about your current Python environment. | +| `pyrun` | Run a Python statement and return results. | +| `pyargs` | A recent addition for more advanced argument handling. | +| `pyrunfile` | Run a Python script from MATLAB. | -Example MATLAB calling code for this function: +The MATLAB `matlab.exception.PyException` captures error information generated during Python execution. -```matlab -dataPath = 'H:\datasets\eeg_ds003645s_hed'; -issueString = validateHedInBids(dataPath); -if isempty(issueString) - fprintf('Dataset %s has no HED validation errors\n', dataPath); -else - fprintf('Validation errors for dataset %s:\n%s\n', dataPath, issueString); -end -``` -```` -In above example assumes that the BIDS dataset was located at `H:\datasets\eeg_ds003645s_hed`. -We tested it with the [**eeg_ds003645s_hed**](https://github.com/hed-standard/hed-examples/tree/main/datasets/eeg_ds003645s_hed) available on GitHub. -You can download and use this test data or set `dataPath` to the root directory of your own dataset. +## Additional demos -#### Calls to HED remodeling tools +### Calling HED remodeling tools Many of the most useful HEDTools functions are packaged in the HED remodeling tool suite. @@ -372,24 +730,8 @@ additional information. The wrapper functions are available on GitHub in the [**hedtools_wrappers**](https://github.com/hed-standard/hed-examples/tree/develop/src/matlab_scripts/hedtools_wrappers) directory. - -### MATLAB functions for Python - -The following table lists the relevant MATLAB functions that are available. -You should refer to the help facility for your version of MATLAB to get the details of what is -supported for your version of MATLAB. - -| MATLAB command | Purpose | -| -------------- | --------| -| `pyenv` | Setup your Python environment in MATLAB.
Without arguments outputs information about your current Python environment. | -| `pyrun` | Run a Python statement and return results. | -| `pyargs` | A recent addition for more advanced argument handling. | -| `pyrunfile` | Run a Python script from MATLAB. | - -The MATLAB `matlab.exception.PyException` captures error information generated during Python execution. - -(web-services-for-hedtools-anchor)= -## Web services for HEDTools +(web-service-matlab-demos-anchor)= +### Web service MATLAB demos MATLAB HED web services allow MATLAB programs to request the same services that are available through the online tools. @@ -430,7 +772,7 @@ function, which returns a MATLAB `struct` containing all needed test data. The individual demo scripts illustrate how to call each type of available web service. (overview-of-service-requests-anchor)= -### Overview of service requests +#### Overview of service requests Calling HED services from MATLAB requires the following steps: @@ -443,7 +785,7 @@ Usually, you will do the first step (the session setup) once at the beginning of to construct a fixed session header that can be used in subsequent requests in your script. (setting-up-a-session-from-matlab-anchor)= -### Setting up a session from MATLAB +#### Setting up a session from MATLAB The goal of the session setup is to construct a header that can be used in subsequent web requests. The first step is to call the [**getHostOptions.m**](https://raw.githubusercontent.com/hed-standard/hed-matlab/main/hedmat/web_services/getHostOptions.m). @@ -499,7 +841,7 @@ However, this can be adjusted upward or downward to suit the user. The `HeaderFields` sets the parameters of HTTP request. (creating-a-request-structure-anchor)= -### Creating a request structure +#### Creating a request structure The request structure is a MATLAB `struct` which must have a `service` field and can have an arbitrary number of fields depending on which service is being requested. @@ -526,7 +868,6 @@ The MATLAB `webwrite` returns a JSON structure as specified in the `options`. The MATLAB `jsondecode` function returns a MATLAB `struct` whose format is explained below in [**Decoding a service response**](decoding-a-service-response-anchor). - Except for `get_services`, all other services are of the form *target_command* where *target* is the primary type of data acted on (events, schema, sidecar, spreadsheet, or string). The possible values for *command* depend on the value of *target*. @@ -580,7 +921,7 @@ The [**demoSidecarServices.m**](https://raw.githubusercontent.com/hed-standard/h function shows complete examples of the various HED services for JSON sidecars. (making-a-service-request-anchor)= -### Making a service request +#### Making a service request The HED services all use the MATLAB `webwrite` to make HED web service requests. The following call uses the @@ -607,7 +948,7 @@ If the connection completes successfully, the response will set. The next section explains the response structure in more detail. (decoding-a-service-response-anchor)= -### Decoding a service response +#### Decoding a service response All HED web services return a response consisting of a JSON dictionary with 4 keys as summarized in this table. diff --git a/docs/source/_static/images/HedToolsSchematic.png b/docs/source/_static/images/HedToolsSchematic.png deleted file mode 100644 index 74b961a..0000000 Binary files a/docs/source/_static/images/HedToolsSchematic.png and /dev/null differ diff --git a/docs/source/_static/images/MatLabHedToolsCloseFit.png b/docs/source/_static/images/MatLabHedToolsCloseFit.png new file mode 100644 index 0000000..6751fba Binary files /dev/null and b/docs/source/_static/images/MatLabHedToolsCloseFit.png differ diff --git a/docs/source/_static/images/MatlabEventsStruct.png b/docs/source/_static/images/MatlabEventsStruct.png new file mode 100644 index 0000000..ad6a0da Binary files /dev/null and b/docs/source/_static/images/MatlabEventsStruct.png differ diff --git a/docs/source/_static/images/MatlabHedTools.png b/docs/source/_static/images/MatlabHedTools.png deleted file mode 100644 index c9ac2b3..0000000 Binary files a/docs/source/_static/images/MatlabHedTools.png and /dev/null differ diff --git a/docs/source/_templates/layout.html b/docs/source/_templates/layout.html index 8bb914b..d2fde98 100644 --- a/docs/source/_templates/layout.html +++ b/docs/source/_templates/layout.html @@ -11,8 +11,8 @@
  • HED YouTube channel
  • - Specification
  • -
  • Online tools
  • + HED specification +
  • Online tools
  • HEDTools API
  • Web deployment
  • Code of conduct