diff --git a/.gitignore b/.gitignore index f630fa5..d2db749 100644 --- a/.gitignore +++ b/.gitignore @@ -57,7 +57,7 @@ cover/ *.pot # Django stuff: -*.log +**.log local_settings.py db.sqlite3 db.sqlite3-journal diff --git a/mars-cli/README.md b/mars-cli/README.md index 6a816c0..1b079cf 100644 --- a/mars-cli/README.md +++ b/mars-cli/README.md @@ -1,9 +1,11 @@ # Installing the mars-cli +This installation procedure describes a typical Linux installation. This application can perfectly work on Windows and MacOS but some of the steps might be different. + Installing the mars-cli from source: ```sh -cd mars-cli # Assuming you are in the root folder +cd mars-cli # Assuming you are in the root folder of this project pip install . ``` @@ -13,6 +15,197 @@ If you want to install the optional testing dependencies as well, useful when co pip install .[test] ``` +If you want to overwrite the `settings.ini` file when reinstalling, you need to set the environmental variable `OVERWRITE_SETTINGS` to `True`: + +```sh +OVERWRITE_SETTINGS=True pip install .[test] +``` +Installing the MARS-cli, will by default create a `.mars` directory in the home directory to store settings and log files. +If you wish to create the `.mars` directory in another place, you must specify the `MARS_SETTINGS_DIR` variable and set it to the desired path: + +```sh +export MARS_SETTINGS_DIR= +``` + +If you want to make it permanent, you can run to following commands in the terminal. +Note: replace `.bashrc` by the config file of your shell. + +```sh +echo '# Add MARS setting directory to PATH' >> $HOME/.bashrc +echo 'export MARS_SETTINGS_DIR=' >> $HOME/.bashrc +``` + +Once installed, the CLI application will be available from the terminal. + +# Configuration + +Installing this application will also generate a `settings.ini` file in `$HOME/.mars/`. + +``` +[logging] +log_level = ERROR +log_file = /my/logging/directory/.mars/app.log +log_max_size = 1024 +log_max_files = 5 +``` + +## Logging + +The MARS-CLI will automatically log events to a `.log` file. + +__log_level__: The verbosity of logging can be set to three different levels +- CRITICAL: Only critical messages will be logged. __Not recommended!__ +- ERROR: Errors and critical messages will be logged. +- WARNING: Warnings, errors and critical messages will be logged. +- INFO: All events are logged. +- DEBUG: For debugging purpose only. __Not recommended as it might log more sensitive information!__ +The default setting is ERROR. So only errors are logged! + +__log_file__: The path to the log file. By default this will be in `$HOME/.mars/app.log`. + +__log_max_size__: The maximum size in kB for the log file. By default the maximum size is set to 1024 kB or 1 MB. + +__log_max_files__: The maximum number of old log files to keep. By default, this is set to 5 + +## Target repository settings + +Each of the target repositories have a set of settings: + +- development-url: URL to the development server when performing a health-check +- development-submission-url: URL to the development server when performing a submission +- production-url: URL to the production server when performing a health-check +- production-submission-url: URL to the production server when performing a submissionW + +# Using the MARS-CLI + +If you wish to use a different location for the `.mars' folder: + +```sh +export MARS_SETTINGS_DIR= +mars-cli [options] ARGUMENT +``` + +## Help + +The mars-cli's help text can be found from the command line as such: + +```sh +mars-cli --help +``` + +Output: + +``` +➜ mars-cli --help +Usage: mars-cli [OPTIONS] COMMAND [ARGS]... + +Options: + -d, --development Boolean indicating the usage of the development + environment of the target repositories. If not present, + the production instances will be used. + --help Show this message and exit. + +Commands: + health-check Check the health of the target repositories. + submit Start a submission to the target repositories. + validate-isa-json Validate the ISA JSON file. +``` + +or for a specific command: + +```sh +mars-cli submit --help +``` + +Output: + +``` +➜ mars-cli submit --help +############# Welcome to the MARS CLI. ############# +Running in Production environment +Usage: mars-cli submit [OPTIONS] CREDENTIALS_FILE ISA_JSON_FILE + + Start a submission to the target repositories. + +Options: + --submit-to-ena BOOLEAN Submit to ENA. + --submit-to-metabolights BOOLEAN + Submit to Metabolights. + --investigation-is-root BOOLEAN + Boolean indicating if the investigation is + the root of the ISA JSON. Set this to True + if the ISA-JSON does not contain a + 'investigation' field. + --help Show this message and exit. +``` + +## Development + +By default the mars-CLI will try to submit the ISA-JSON's metadata towards the repositories' production servers. Passing the development flag will run it in development mode and substitute the production servers with the development servers. + +## Health check repository services + +You can check whether the supported repositories are healthy, prior to submission, by doing a health-check. + +```sh +mars-cli health-check +``` + +Output: + +``` +➜ mars-cli health-check +############# Welcome to the MARS CLI. ############# +Running in Production environment +Checking the health of the target repositories. +Checking production instances. +Webin (https://www.ebi.ac.uk/ena/submit/webin/auth) is healthy. +ENA (https://www.ebi.ac.uk/ena/submit/webin-v2/) is healthy. +Biosamples (https://www.ebi.ac.uk/biosamples/samples/) is healthy. +``` + +## Submitting to repository services + +TODO + +### Options + +- `--submit-to-ena`: By default set to `True`. Will try submit ISA-JSON metadata towards ENA. Setting it to `False` will skip sending the ISA-JSON's metadata to ENA. + +```sh +mars-cli submit --submit-to-ena False my-credentials my-isa-json.json +``` + +- `--submit-to-metabolights`: By default set to `True`. Will try submit ISA-JSON metadata towards Metabolights. Setting it to `False` will skip sending the ISA-JSON's metadata to Metabolights. + +```sh +mars-cli submit --submit-to-metabolights False my-credentials my-isa-json.json +``` + +`--investigation-is-root`: By default this flag is set to false, maening the ISA-JSON should have the `investigation` key at the root level. In case the root level __IS__ the investigation (`investigation` level is omitted), you need set the flag `--investigation-is-root` to `True` in order to validate the ISA-JSON. + +```sh +mars-cli submit --investigation-is-root True my-credentials my-isa-json.json +``` + +## Validation of the ISA JSON + +You can perform a syntactic validation of the ISA-JSON, without submitting to the target repositories. + +__Note:__ This does not take validation into account from the repository's side. This does not guarantee successful submission. + +```sh +mars-cli validate-isa-json --investigation-is-root True ../test-data/biosamples-input-isa.json +``` + +### Options + +`--investigation-is-root`: By default this flag is set to false, maening the ISA-JSON should have the `investigation` key at the root level. In case the root level __IS__ the investigation (`investigation` level is omitted), you need set the flag `--investigation-is-root` to `True` in order to validate the ISA-JSON. + +```sh +mars-cli validate-isa-json my-isa-investigation.json +``` + # Extending BioSamples' records The Python script ``biosamples-externalReferences.py`` defines a class BiosamplesRecord for managing biosample records. This class is designed to interact with the BioSamples database, allowing operations like fetching, updating, and extending biosample records. The script takes in a dictionary of BioSamples' accessions and their associated external references, and expands the former with the latter. diff --git a/mars-cli/generate_config.py b/mars-cli/generate_config.py new file mode 100644 index 0000000..99e0f66 --- /dev/null +++ b/mars-cli/generate_config.py @@ -0,0 +1,71 @@ +import configparser +import pathlib + + +def create_settings_file(settings_dir): + """ + Create a settings file with the specified log path and settings path. + + Args: + settings_path (str): The path to the settings file. + + Returns: + None + """ + log_path = settings_dir / "app.log" + settings_path = settings_dir / "settings.ini" + config = configparser.ConfigParser() + config["logging"] = { + "log_level": "ERROR", + "log_file": log_path, + "log_max_size": "1024", + "log_max_files": "5", + } + + config["webin"] = { + "development-url": "https://wwwdev.ebi.ac.uk/ena/submit/webin/auth", + "development-token-url": "https://wwwdev.ebi.ac.uk/ena/submit/webin/auth/token", + "production-url": "https://www.ebi.ac.uk/ena/submit/webin/auth", + "production-token-url": "https://www.ebi.ac.uk/ena/submit/webin/auth/token", + } + + config["ena"] = { + "development-url": "https://wwwdev.ebi.ac.uk/ena/submit/webin-v2/", + "development-submission-url": "https://wwwdev.ebi.ac.uk/ena/submit/drop-box/submit/?auth=ENA", + "production-url": "https://www.ebi.ac.uk/ena/submit/webin-v2/", + "production-submission-url": "https://www.ebi.ac.uk/ena/submit/drop-box/submit/?auth=ENA", + } + + config["biosamples"] = { + "development-url": "https://wwwdev.ebi.ac.uk/biosamples/samples/", + "development-submission-url": "https://wwwdev.ebi.ac.uk/biosamples/samples/", + "production-url": "https://www.ebi.ac.uk/biosamples/samples/", + "production-submission-url": "https://www.ebi.ac.uk/biosamples/samples/", + } + + with open(settings_path, "w") as config_file: + config.write(config_file) + + +def generate_config(overwrite, mars_home_dir): + """ + Generate the configuration file for the MARS CLI. + + Returns: + None + """ + settings_dir = ( + pathlib.Path.home() / ".mars" + if mars_home_dir == "HOME" + else pathlib.Path(mars_home_dir) / ".mars" + ) + + if not settings_dir.exists(): + settings_dir.mkdir() + + settings_path = settings_dir / "settings.ini" + + if settings_path.exists() and not overwrite: + return + + create_settings_file(settings_dir) diff --git a/mars-cli/mars_cli.py b/mars-cli/mars_cli.py index d615b31..41e4c06 100644 --- a/mars-cli/mars_cli.py +++ b/mars-cli/mars_cli.py @@ -1,83 +1,205 @@ -from mars_lib.authentication import get_webin_auth_token -from mars_lib.biosamples_external_references import ( - get_header, - biosamples_endpoints, - BiosamplesRecord, - validate_json_against_schema, - handle_input_dict, - input_json_schema_filepath, +import click +import logging +import pathlib +from configparser import ConfigParser +from mars_lib.target_repo import TargetRepository +from mars_lib.model import Investigation, IsaJson +from mars_lib.isa_json import load_isa_json +from logging.handlers import RotatingFileHandler +import requests +import sys +import os +import json + +# Load CLI configuration +home_dir = ( + pathlib.Path(os.getenv("MARS_SETTINGS_DIR")) + if os.getenv("MARS_SETTINGS_DIR") + else pathlib.Path.home() ) -import argparse -from argparse import RawTextHelpFormatter +config_file = home_dir / ".mars" / "settings.ini" +fallback_log_file = home_dir / ".mars" / "app.log" -def create_external_references( - biosamples_credentials, biosamples_externalReferences, production -): - """ - Main function to be executed when script is run. - - Args: - biosamples_credentials: Dictionary with the credentials of the submitter of the existing Biosamples records. - biosamples_externalReferences: Dictionary containing the mapping between the - production: Boolean indicating the environment of BioSamples to use. - """ - validate_json_against_schema( - json_doc=biosamples_externalReferences, json_schema=input_json_schema_filepath - ) - token = get_webin_auth_token(biosamples_credentials) - header = get_header(token) +config = ConfigParser() +config.read(config_file) + +# Logging configuration +log_level = config.get("logging", "log_level", fallback="ERROR") +log_file = config.get("logging", "log_file", fallback=fallback_log_file) +log_max_size = int( + config.get("logging", "log_max_size", fallback="1024") +) # in kilobytes. 1 MB by default. +log_max_files = int( + config.get("logging", "log_max_files", fallback="5") +) # number of backup files. 5 by default. + +handler = RotatingFileHandler( + log_file, maxBytes=log_max_size * 1024, backupCount=log_max_files +) +handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) - if production: - biosamples_endpoint = biosamples_endpoints["prod"] +logging.basicConfig( + handlers=[handler], + level=log_level, +) + + +def print_and_log(msg, level="info"): + if level == "info": + click.echo(msg) + logging.info(msg) + elif level == "error": + click.echo(msg, file=sys.stderr) + logging.error(msg) + elif level == "warning": + click.echo(msg) + logging.warning(msg) else: - biosamples_endpoint = biosamples_endpoints["dev"] - - for biosample_r in biosamples_externalReferences["biosampleExternalReferences"]: - bs_accession = biosample_r["biosampleAccession"] - BSrecord = BiosamplesRecord(bs_accession) - BSrecord.fetch_bs_json(biosamples_endpoint) - # To test it without the fetching, you can download it manually and then use: - # BSrecord.load_bs_json(bs_json_file="downloaded-json.json") - new_ext_refs_list = biosample_r["externalReferences"] - BSrecord.extend_externalReferences(new_ext_refs_list) - BSrecord.update_remote_record(header) - - -def main(): - """Main function that handles the argument parsing and passes those to `create_external_references`""" - # Command-line argument parsing - parser = argparse.ArgumentParser(description="Handle biosamples records.") - description = "This script extends a set of existing Biosamples records with a list of provided external references." - parser = argparse.ArgumentParser( - prog="biosamples-externalReferences.py", - description=description, - formatter_class=RawTextHelpFormatter, - ) - parser.add_argument( - "biosamples_credentials", - help="Either a dictionary or filepath to the BioSamples credentials.", - ) - parser.add_argument( - "biosamples_externalReferences", - help="Either a dictionary or filepath to the BioSamples' accessions mapping with external references.", - ) - parser.add_argument( - "--production", - action="store_true", - help="Boolean indicating the usage of the production environment of BioSamples. If not present, the development instance will be used.", - ) - # Handle inputs - parsed_args = parser.parse_args() - biosamples_credentials = handle_input_dict(parsed_args.biosamples_credentials) - biosamples_externalReferences = handle_input_dict( - parsed_args.biosamples_externalReferences + click.echo(msg) + logging.debug(msg) + + +@click.group() +@click.option( + "--development", + "-d", + is_flag=True, + help="Boolean indicating the usage of the development environment of the target repositories. If not present, the production instances will be used.", +) +@click.pass_context +def cli(ctx, development): + print_and_log("############# Welcome to the MARS CLI. #############") + print_and_log( + f"Running in {'Development environment' if development else 'Production environment'}" ) - create_external_references( - biosamples_credentials, biosamples_externalReferences, parsed_args.production + ctx.ensure_object(dict) + ctx.obj["DEVELOPMENT"] = development + + +@cli.command() +@click.argument( + "credentials_file", + type=click.File("r"), +) +@click.argument( + "isa_json_file", + type=click.File("r"), +) +@click.option("--submit-to-ena", type=click.BOOL, default=True, help="Submit to ENA.") +@click.option( + "--submit-to-metabolights", + type=click.BOOL, + default=True, + help="Submit to Metabolights.", +) +@click.option( + "--investigation-is-root", + default=False, + type=click.BOOL, + help="Boolean indicating if the investigation is the root of the ISA JSON. Set this to True if the ISA-JSON does not contain a 'investigation' field.", +) +def submit( + credentials_file, + isa_json_file, + submit_to_ena, + submit_to_metabolights, + investigation_is_root, +): + """Start a submission to the target repositories.""" + target_repositories = ["biosamples"] + + investigation = load_isa_json(isa_json_file, investigation_is_root) + + print_and_log(f"ISA JSON with investigation '{investigation.title}' is valid.") + + if submit_to_ena: + target_repositories.append(TargetRepository.ENA) + + if submit_to_metabolights: + target_repositories.append(TargetRepository.METABOLIGHTS) + + print_and_log( + f"Staring submission of the ISA JSON to the target repositories: {', '.join(target_repositories)}." ) + # TODO: Entry point for the submission logic + + +@cli.command() +@click.pass_context +def health_check(ctx): + """Check the health of the target repositories.""" + print_and_log("Checking the health of the target repositories.") + + if ctx.obj["DEVELOPMENT"]: + print_and_log("Checking development instances.") + webin_url = config.get("webin", "development-url") + ena_url = config.get("ena", "development-url") + biosamples_url = config.get("biosamples", "development-url") + else: + print_and_log("Checking production instances.") + webin_url = config.get("webin", "production-url") + ena_url = config.get("ena", "production-url") + biosamples_url = config.get("biosamples", "production-url") + + # Check webin service + webin_health = requests.get(webin_url) + if webin_health.status_code != 200: + print_and_log( + f"Webin ({webin_url}): Could not reach service! Status code '{webin_health.status_code}'.", + level="error", + ) + else: + print_and_log(f"Webin ({webin_url}) is healthy.") + + # Check ENA service + ena_health = requests.get(ena_url) + if ena_health.status_code != 200: + print_and_log( + f"ENA ({ena_url}): Could not reach service! Status code '{ena_health.status_code}'.", + level="error", + ) + else: + print_and_log(f"ENA ({ena_url}) is healthy.") + + # Check Biosamples service + biosamples_health = requests.get(biosamples_url) + if biosamples_health.status_code != 200: + print_and_log( + f"Biosamples ({biosamples_url}): Could not reach service! Status code '{biosamples_health.status_code}'.", + level="error", + ) + else: + print_and_log(f"Biosamples ({biosamples_url}) is healthy.") + + +@cli.command() +@click.argument( + "isa_json_file", + type=click.Path(exists=True), +) +@click.option( + "--investigation-is-root", + default=False, + type=click.BOOL, + help="Boolean indicating if the investigation is the root of the ISA JSON. Set this to True if the ISA-JSON does not contain a 'investigation' field.", +) +def validate_isa_json(isa_json_file, investigation_is_root): + """Validate the ISA JSON file.""" + print_and_log(f"Validating {isa_json_file}.") + + with open(isa_json_file) as f: + json_data = json.load(f) + + if investigation_is_root: + investigation = Investigation.model_validate(json_data) + else: + investigation = IsaJson.model_validate(json_data).investigation + + print_and_log(f"ISA JSON with investigation '{investigation.title}' is valid.") + if __name__ == "__main__": - main() + cli() diff --git a/mars-cli/mars_lib/isa_json.py b/mars-cli/mars_lib/isa_json.py index 04ac51e..af53897 100644 --- a/mars-cli/mars_lib/isa_json.py +++ b/mars-cli/mars_lib/isa_json.py @@ -1,75 +1,54 @@ import json -from typing import Dict, Union, List -import copy - -TARGET_REPO_KEY = "target repository" - - -class IsaJsonValidationError(ValueError): - """ - Custom Error object to be used when the validation fails. - This class extends the ValueError class. - """ - - def __init__(self, report, message="The Provided ISA JSON is invalid!"): - self.message = message + "\n" + str(report["errors"]) - super().__init__(self.message) - - -class TargetRepository: - """ - Holds constants, tied to the target repositories. - """ - - ENA = "ena" - METABOLIGHTS = "metabolights" - BIOSAMPLES = "biosamples" +from typing import Union, List +from mars_lib.model import Investigation, Assay, Comment, IsaJson +from pydantic import ValidationError +from mars_lib.target_repo import TARGET_REPO_KEY def reduce_isa_json_for_target_repo( - input_isa_json: Dict, target_repo: str -) -> Dict[str, str]: + input_isa_json: Investigation, target_repo: str +) -> Investigation: """ Filters out assays that are not meant to be sent to the specified target repository. Args: - input_isa_json (Dict[str, str]): Input ISA JSON that contains the original information. + input_isa_json (Investigation): Input ISA JSON that contains the original information. target_repo (TargetRepository): Target repository as a constant. Returns: - Dict[str, str]: Filtered ISA JSON. + Investigation: Filtered ISA JSON. """ - filtered_isa_json = copy.deepcopy(input_isa_json) + filtered_isa_json = input_isa_json.model_copy(deep=True) new_studies = [] - studies = filtered_isa_json.pop("studies") + studies = filtered_isa_json.studies for study in studies: - assays = study.pop("assays") + assays = study.assays filtered_assays = [ assay for assay in assays if is_assay_for_target_repo(assay, target_repo) ] if len(filtered_assays) > 0: - study["assays"] = filtered_assays + study.assays = filtered_assays new_studies.append(study) - filtered_isa_json["studies"] = new_studies + filtered_isa_json.studies = new_studies return filtered_isa_json -def detect_target_repo_comment(comments: List[Dict[str, str]]) -> Dict[str, str]: - """_summary_ +def detect_target_repo_comment(comments: List[Comment]) -> Comment: + """Will detect the comment that contains the target repository. Args: - comments (List[Dict[str, str]]): Dictionary of comments. + comments (List[Comment]): List of comments. Returns: - Dict[str, str]: The comment where the name corresponds with the name of the provided target repo. + Comment: The comment where the name corresponds with the name of the provided target repo. """ for comment in comments: - if comment["name"] == TARGET_REPO_KEY: + if comment.name == TARGET_REPO_KEY: return comment -def is_assay_for_target_repo(assay_dict: Dict, target_repo: str) -> bool: +def is_assay_for_target_repo(assay: Assay, target_repo: str) -> bool: """ Defines whether the assays is meant for the target repository. @@ -80,26 +59,30 @@ def is_assay_for_target_repo(assay_dict: Dict, target_repo: str) -> bool: Returns: bool: Boolean defining whether the assay is destined for the provided target repo. """ - target_repo_comment = detect_target_repo_comment(assay_dict["comments"]) - if target_repo_comment["value"] == target_repo: + target_repo_comment = detect_target_repo_comment(assay.comments) + if target_repo_comment.value == target_repo: return True else: return False -def load_isa_json(file_path: str) -> Union[Dict[str, str], IsaJsonValidationError]: +def load_isa_json( + file_path: str, investigation_is_root: bool +) -> Union[Investigation, ValidationError]: """ Reads the file and validates it as a valid ISA JSON. Args: file_path (str): Path to ISA JSON as string. + investigation_is_root (bool): Boolean indicating if the investigation is the root of the ISA JSON. Set this to True if the ISA-JSON does not contain a 'investigation' field. Returns: - Union[Dict[str, str], IsaJsonValidationError]: Depending on the validation, returns a filtered ISA JSON or an Error. + Union[Dict[str, str], ValidationError]: Depending on the validation, returns a filtered ISA JSON or a pydantic validation error. """ with open(file_path, "r") as json_file: isa_json = json.load(json_file) - # TODO: Once we have an idea on what / how to validate, it should be added here - - return isa_json + if investigation_is_root: + return Investigation.model_validate(isa_json) + else: + return IsaJson.model_validate(isa_json).investigation diff --git a/mars-cli/mars_lib/model.py b/mars-cli/mars_lib/model.py new file mode 100644 index 0000000..9149224 --- /dev/null +++ b/mars-cli/mars_lib/model.py @@ -0,0 +1,296 @@ +from __future__ import annotations + +from enum import Enum +from typing import List, Optional, Union + +from pydantic import BaseModel, Field, field_validator, ConfigDict +from mars_lib.target_repo import TargetRepository, TARGET_REPO_KEY + + +class IsaBase(BaseModel): + # model_config = ConfigDict(extra="allow") + model_config = ConfigDict(extra="forbid") + + +class Comment(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + name: Optional[str] = Field(default=None) + value: Optional[str] = Field(default=None) + + +class OntologySourceReference(IsaBase): + comments: Optional[List[Comment]] = Field(default=[]) + description: Optional[str] = Field(default=None) + file: Optional[str] = Field(default=None) + name: Optional[str] = Field(default=None) + version: Optional[str] = Field(default=None) + + +# TODO: Question: Should these be case-sensitive? +class DataTypeEnum(str, Enum): + RAW_DATA_FILE = "Raw Data File" + DERIVED_DATA_FILE = "Derived Data File" + IMAGE_FILE = "Image File" + SPECTRAL_RAW_DATA_FILE = "Spectral Raw Data File" # TODO: QUESTION: This is not mentioned in the specs (https://isa-specs.readthedocs.io/) + FREE_INDUCTION_DECAY_FILE = "Free Induction Decay File" # TODO: QUESTION: This is not mentioned in the specs (https://isa-specs.readthedocs.io/) + + +class Data(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + comments: Optional[List[Comment]] = Field(default=[]) + name: Optional[str] = Field(default=None) + type: Optional[DataTypeEnum] = Field(default=None) + + @field_validator("type") + def apply_enum(cls, v): + if v not in [item.value for item in DataTypeEnum]: + raise ValueError("Invalid material type") + return v + + +class OntologyAnnotation(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + annotationValue: Union[Optional[str], Optional[float], Optional[int]] = Field( + default=[] + ) + comments: Optional[List[Comment]] = Field(default=[]) + termAccession: Optional[str] = Field(default=None) + termSource: Optional[str] = Field( + description="The abbreviated ontology name. It should correspond to one of the sources as specified in the ontologySourceReference section of the Investigation.", + default=None, + ) + + +class MaterialAttributeValue(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + category: Optional[MaterialAttribute] = Field(default=None) + value: Union[ + Optional[OntologyAnnotation], Optional[str], Optional[float], Optional[int] + ] = Field(default=None) + unit: Optional[OntologyAnnotation] = Field(default=None) + comments: Optional[List[Comment]] = Field( + default=[] + ) # TODO: QUESTION: This is not mentioned in the specs (https://isa-specs.readthedocs.io/en/latest/isajson.html#material-attribute-value-schema-json) + + +class Factor(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + comments: Optional[List[Comment]] = Field(default=[]) + factorName: Optional[str] = Field(default=None) + factorType: Optional[OntologyAnnotation] = Field(default=None) + + +class FactorValue(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + category: Optional[Factor] = Field(default=None) + value: Union[ + Optional[str], Optional[float], Optional[int], Optional[OntologyAnnotation] + ] = Field(default=[]) + unit: Optional[OntologyAnnotation] = Field(default=None) + + +class Source(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + characteristics: Optional[List[MaterialAttributeValue]] = Field(default=[]) + name: Optional[str] = Field(default=None) + comments: Optional[List[Comment]] = Field( + default=[] + ) # TODO: QUESTION: This is not mentioned in the specs (https://isa-specs.readthedocs.io/en/latest/isajson.html#source-schema-json) + + +class Sample(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + name: Optional[str] = Field(default=None) + characteristics: Optional[List[MaterialAttributeValue]] = Field(default=[]) + factorValues: Optional[List[FactorValue]] = Field(default=[]) + derivesFrom: Optional[List[Source]] = Field(default=[]) + comments: Optional[List[Comment]] = Field( + default=[] + ) # TODO: QUESTION: This is not mentioned in the specs (https://isa-specs.readthedocs.io/en/latest/isajson.html#sample-schema-json) + + +class ProtocolParameter(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + parameterName: Optional[OntologyAnnotation] = Field(default=None) + + +class ProcessParameterValue(IsaBase): + category: Optional[ProtocolParameter] = Field(default=None) + value: Union[ + Optional[str], Optional[float], Optional[int], Optional[OntologyAnnotation] + ] = Field(default=[]) + unit: Optional[OntologyAnnotation] = Field(default=None) + + +# Helper class for protocol -> components +class Component(IsaBase): + componentName: Optional[str] = Field(default=None) + componentType: Optional[OntologyAnnotation] = Field(default=None) + + +class Protocol(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + comments: Optional[List[Comment]] = Field(default=[]) + components: Optional[List[Component]] = Field(default=[]) + description: Optional[str] = Field(default=None) + name: Optional[str] = Field(default=None) + parameters: Optional[List[ProtocolParameter]] = Field(default=[]) + protocolType: Optional[OntologyAnnotation] = Field(default=None) + uri: Optional[str] = Field(default=None) + version: Optional[str] = Field(default=None) + + +# Enum for material -> type +# TODO: Question: Should these be case-sensitive? +class MaterialTypeEnum(str, Enum): + EXTRACT_NAME = "Extract Name" + LABELED_EXTRACT_NAME = "Labeled Extract Name" + LIBRARY_NAME = "library name" # TODO: QUESTION: This is not mentioned in the specs (https://isa-specs.readthedocs.io/en/latest/isajson.html#material-schema-json) but was found in DataHub ISA-JSON and ARC ISA-JSON. + + +class Material(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + characteristics: List[MaterialAttributeValue] = Field(default=[]) + comments: Optional[List[Comment]] = Field(default=[]) + name: Optional[str] = Field(default=None) + type: Optional[str] = Field(default=None) + derivesFrom: Optional[List[Material]] = Field(default=[]) + + @field_validator("type") + def apply_enum(cls, v): + if v not in [item.value for item in MaterialTypeEnum]: + raise ValueError("Invalid material type") + return v + + +class Process(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + comments: Optional[List[Comment]] = Field(default=[]) + date: Optional[str] = Field(default=None) + executesProtocol: Optional[Protocol] = Field(default=None) + inputs: Optional[Union[List[Source], List[Sample], List[Material], list[Data]]] = ( + Field(default=[]) + ) + name: Optional[str] = Field(default=None) + nextProcess: Optional[Process] = Field(default=None) + outputs: Optional[Union[List[Sample], List[Material], list[Data]]] = Field( + default=[] + ) + parameterValues: Optional[List[ProcessParameterValue]] = Field(default=[]) + performer: Optional[str] = Field(default=None) + previousProcess: Optional[Process] = Field(default=None) + + +# Helper for assay -> materials +class AssayMaterialType(IsaBase): + samples: Optional[List[Sample]] = Field(default=[]) + otherMaterials: Optional[List[Material]] = Field(default=[]) + + +class Assay(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + characteristicCategories: Optional[List[MaterialAttribute]] = Field(default=[]) + comments: Optional[List[Comment]] = Field(default=[]) + dataFiles: Optional[List[Data]] = Field(default=[]) + filename: Optional[str] = Field(default=None) + materials: Optional[AssayMaterialType] = Field(default=None) + measurementType: Optional[OntologyAnnotation] = Field(default=None) + processSequence: Optional[List[Process]] = Field(default=[]) + technologyPlatform: Optional[str] = Field(default=None) + technologyType: Optional[OntologyAnnotation] = Field(default=None) + unitCategories: Optional[List[OntologyAnnotation]] = Field(default=[]) + + @field_validator("comments") + def detect_target_repo_comments(cls, v): + target_repo_comments = [ + comment for comment in v if comment.name == TARGET_REPO_KEY + ] + if len(target_repo_comments) == 0: + raise ValueError("'target repository' comment is missing") + elif len(target_repo_comments) > 1: + raise ValueError("Multiple 'target repository' comments found") + else: + if target_repo_comments[0].value in [ + item.value for item in TargetRepository + ]: + return v + else: + raise ValueError( + f"Invalid 'target repository' value: '{target_repo_comments[0].value}'" + ) + + +class Person(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + address: Optional[str] = Field(default=None) + affiliation: Optional[str] = Field(default=None) + comments: Optional[List[Comment]] = Field(default=[]) + email: Optional[str] = Field(default=None) + fax: Optional[str] = Field(default=None) + firstName: Optional[str] = Field(default=None) + lastName: Optional[str] = Field(default=None) + midInitials: Optional[str] = Field(default=None) + phone: Optional[str] = Field(default=None) + roles: Optional[List[OntologyAnnotation]] = Field(default=[]) + + +class Publication(IsaBase): + authorList: Optional[str] = Field(default=None) + comments: Optional[List[Comment]] = Field(default=[]) + doi: Optional[str] = Field(default=None) + pubMedID: Optional[str] = Field(default=None) + status: Optional[OntologyAnnotation] = Field(default=None) + title: Optional[str] = Field(default=None) + + +class StudyMaterialType(IsaBase): + sources: Optional[List[Source]] = Field(default=[]) + samples: Optional[List[Sample]] = Field(default=[]) + otherMaterials: Optional[List[Material]] = Field(default=[]) + + +class MaterialAttribute(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + characteristicType: Optional[OntologyAnnotation] = Field(default=None) + + +class Study(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + assays: Optional[List[Assay]] = Field(default=[]) + characteristicCategories: Optional[List[MaterialAttribute]] = Field(default=[]) + comments: Optional[List[Comment]] = Field(default=[]) + description: Optional[str] = Field(default=None) + factors: Optional[List[Factor]] = Field(default=[]) + filename: Optional[str] = Field(default=None) + identifier: Optional[str] = Field(default=None) + materials: Optional[StudyMaterialType] + people: Optional[List[Person]] = Field(default=[]) + processSequence: Optional[List[Process]] = Field(default=[]) + protocols: Optional[List[Protocol]] = Field(default=[]) + publicReleaseDate: Optional[str] = Field(default=None) + publications: Optional[List[Publication]] = Field(default=[]) + studyDesignDescriptors: Optional[List[OntologyAnnotation]] = Field(default=[]) + submissionDate: Optional[str] = Field(default=None) + title: Optional[str] = Field(default=None) + unitCategories: Optional[List[OntologyAnnotation]] = Field(default=[]) + + +class Investigation(IsaBase): + id: Optional[str] = Field(alias="@id", default=None) + comments: Optional[List[Comment]] = Field(default=[]) + description: Optional[str] = Field(default=None) + filename: Optional[str] = Field(default=None) + identifier: Optional[str] = Field(default=None) + ontologySourceReferences: Optional[List[OntologySourceReference]] = Field( + default=[] + ) + people: Optional[List[Person]] = Field(default=[]) + publicReleaseDate: Optional[str] = Field(default=None) + publications: Optional[List[Publication]] = Field(default=[]) + studies: Optional[List[Study]] = Field(default=[]) + submissionDate: Optional[str] = Field(default=None) + title: Optional[str] = Field(default=None) + + +class IsaJson(IsaBase): + investigation: Investigation diff --git a/mars-cli/mars_lib/submit.py b/mars-cli/mars_lib/submit.py new file mode 100644 index 0000000..7c095be --- /dev/null +++ b/mars-cli/mars_lib/submit.py @@ -0,0 +1,41 @@ +from mars_lib.authentication import get_webin_auth_token +from mars_lib.biosamples_external_references import ( + get_header, + biosamples_endpoints, + BiosamplesRecord, + validate_json_against_schema, + input_json_schema_filepath, +) + + +def create_external_references( + biosamples_credentials, biosamples_externalReferences, production +): + """ + Main function to be executed when script is run. + + Args: + biosamples_credentials: Dictionary with the credentials of the submitter of the existing Biosamples records. + biosamples_externalReferences: Dictionary containing the mapping between the + production: Boolean indicating the environment of BioSamples to use. + """ + if production: + biosamples_endpoint = biosamples_endpoints["prod"] + else: + biosamples_endpoint = biosamples_endpoints["dev"] + + validate_json_against_schema( + json_doc=biosamples_externalReferences, json_schema=input_json_schema_filepath + ) + token = get_webin_auth_token(biosamples_credentials) + header = get_header(token) + + for biosample_r in biosamples_externalReferences["biosampleExternalReferences"]: + bs_accession = biosample_r["biosampleAccession"] + BSrecord = BiosamplesRecord(bs_accession) + BSrecord.fetch_bs_json(biosamples_endpoint) + # To test it without the fetching, you can download it manually and then use: + # BSrecord.load_bs_json(bs_json_file="downloaded-json.json") + new_ext_refs_list = biosample_r["externalReferences"] + BSrecord.extend_externalReferences(new_ext_refs_list) + BSrecord.update_remote_record(header) diff --git a/mars-cli/mars_lib/target_repo.py b/mars-cli/mars_lib/target_repo.py new file mode 100644 index 0000000..361f4ee --- /dev/null +++ b/mars-cli/mars_lib/target_repo.py @@ -0,0 +1,15 @@ +from enum import Enum + + +TARGET_REPO_KEY = "target repository" + + +class TargetRepository(str, Enum): + """ + Holds constants, tied to the target repositories. + """ + + ENA = "ena" + METABOLIGHTS = "metabolights" + BIOSAMPLES = "biosamples" + EVA = "eva" diff --git a/mars-cli/mars_lib/validation.py b/mars-cli/mars_lib/validation.py deleted file mode 100644 index e69de29..0000000 diff --git a/mars-cli/requirements.txt b/mars-cli/requirements.txt index bd4c238..b2f2f14 100644 --- a/mars-cli/requirements.txt +++ b/mars-cli/requirements.txt @@ -1,3 +1,5 @@ requests jsonschema -keyring \ No newline at end of file +keyring +pydantic +click diff --git a/mars-cli/setup.py b/mars-cli/setup.py index 69c1ffd..6dd6f34 100644 --- a/mars-cli/setup.py +++ b/mars-cli/setup.py @@ -1,6 +1,9 @@ -import pathlib +from setuptools.command.install import install from _version import __version__ from setuptools import find_packages, setup +import pathlib +import os +from generate_config import generate_config with open("requirements.txt", "r") as file: required_deps = file.read().splitlines() @@ -8,8 +11,22 @@ parent_folder = pathlib.Path(__file__).parent.resolve() long_description = (parent_folder / "README.md").read_text(encoding="utf-8") + +class custom_install(install): + def run(self): + # Default install command + install.run(self) + + overwrite_settings = os.getenv("OVERWRITE_SETTINGS", "False").lower() == "true" + mars_home_dir = os.getenv("MARS_SETTINGS_DIR", "HOME") + generate_config(overwrite_settings, mars_home_dir) + + setup( name="mars", + cmdclass={ + "install": custom_install, + }, description="Multi-omics Adapter for Repository Submissions", long_description=long_description, long_description_content_type="text/markdown", @@ -33,7 +50,7 @@ }, entry_points={ # Optional "console_scripts": [ - "mars-cli=mars_cli:main", + "mars-cli=mars_cli:cli", ], }, python_requires=">=3.9, <4", diff --git a/mars-cli/tests/fixtures/invalid_investigation.json b/mars-cli/tests/fixtures/invalid_investigation.json new file mode 100644 index 0000000..db9f7dd --- /dev/null +++ b/mars-cli/tests/fixtures/invalid_investigation.json @@ -0,0 +1,12 @@ +{ + "comments": [], + "description": "This is a minimal test investigation for testing purposes", + "identifier": "INV test 001", + "ontologySourceReferences": [], + "people": [], + "publicReleaseDate": "", + "publications": [], + "studies": "study 1", + "submissionDate": "", + "title": "test investigation 001" +} \ No newline at end of file diff --git a/mars-cli/tests/fixtures/minimal_valid_investigation.json b/mars-cli/tests/fixtures/minimal_valid_investigation.json new file mode 100644 index 0000000..9f04b19 --- /dev/null +++ b/mars-cli/tests/fixtures/minimal_valid_investigation.json @@ -0,0 +1,12 @@ +{ + "comments": [], + "description": "This is a minimal test investigation for testing purposes", + "identifier": "INV test 001", + "ontologySourceReferences": [], + "people": [], + "publicReleaseDate": "", + "publications": [], + "studies": [], + "submissionDate": "", + "title": "test investigation 001" +} \ No newline at end of file diff --git a/mars-cli/tests/test_isa_json.py b/mars-cli/tests/test_isa_json.py index cee4c56..730629c 100644 --- a/mars-cli/tests/test_isa_json.py +++ b/mars-cli/tests/test_isa_json.py @@ -1,25 +1,148 @@ from mars_lib.isa_json import ( reduce_isa_json_for_target_repo, load_isa_json, - TargetRepository, ) +from mars_lib.target_repo import TargetRepository, TARGET_REPO_KEY +import pytest +from pydantic import ValidationError +from mars_lib.model import Data, Material, Assay, Person def test_load_isa_json(): - # Should test the validation process - pass + # Should test the validation process of the ISA JSON file where root level = investigation. + valid_isa_json01 = load_isa_json( + "../test-data/ISA-BH2023-ALL/isa-bh2023-all.json", True + ) + assert len(valid_isa_json01.studies) == 1 + assert valid_isa_json01.studies[0].identifier == "BH2023" + + # Should test the validation process of the ISA JSON file where root has 'investigation' as key. + valid_isa_json02 = load_isa_json("../test-data/biosamples-input-isa.json", False) + assert len(valid_isa_json02.studies) == 1 + assert valid_isa_json02.studies[0].title == "Arabidopsis thaliana" + + with pytest.raises(ValidationError): + load_isa_json("./tests/fixtures/invalid_investigation.json", True) def test_reduce_isa_json_for_target_repo(): - good_isa_json = load_isa_json("../test-data/ISA-BH2023-ALL/isa-bh2023-all.json") + good_isa_json = load_isa_json( + "../test-data/ISA-BH2023-ALL/isa-bh2023-all.json", True + ) filtered_isa_json = reduce_isa_json_for_target_repo( good_isa_json, TargetRepository.ENA ) - good_isa_json_study = good_isa_json["studies"][0] + good_isa_json_study = good_isa_json.studies[0] + + filtered_isa_json_study = filtered_isa_json.studies[0] + + assert len(good_isa_json_study.assays) == 5 + assert len(filtered_isa_json_study.assays) == 1 + + +def test_data_type_validator(): + valid_data_json = {"@id": "data_001", "name": "data 1", "type": "Image File"} + + invalid_data_json = { + "@id": "data_001", + "name": "data 1", + "type": "Custom File", # This is not a valid data type + } + + assert Data.model_validate(valid_data_json) + + with pytest.raises(ValidationError): + Data.model_validate(invalid_data_json) + + +def test_material_type_validator(): + valid_material_json = { + "@id": "material_001", + "name": "material 1", + "type": "Extract Name", + } + + invalid_material_json = { + "@id": "material_002", + "name": "material 2", + "type": "Custom Material", # This is not a valid material type + } + + assert Material.model_validate(valid_material_json) + + with pytest.raises(ValidationError): + Material.model_validate(invalid_material_json) + + +def test_target_repo_comment_validator(): + valid_assay_json = { + "@id": "assay_001", + "comments": [ + { + "@id": "comment_001", + "name": "target repository", + "value": TargetRepository.ENA, + } + ], + } + + invalid_assay_json = { + "@id": "assay_002", + "comments": [ + { + "@id": "comment_002", + "name": "target repository", + "value": "my special repo", + } + ], + } + + second_invalid_assay_json = {"@id": "assay_003", "comments": []} + + third_invalid_assay_json = { + "@id": "assay_004", + "comments": [ + { + "@id": "comment_003", + "name": "target repository", + "value": TargetRepository.ENA, + }, + { + "@id": "comment_004", + "name": "target repository", + "value": TargetRepository.METABOLIGHTS, + }, + ], + } + + assert Assay.model_validate(valid_assay_json) + with pytest.raises( + ValidationError, match="Invalid 'target repository' value: 'my special repo'" + ): + Assay.model_validate(invalid_assay_json) + + with pytest.raises(ValidationError, match="'target repository' comment is missing"): + Assay.model_validate(second_invalid_assay_json) + + with pytest.raises( + ValidationError, match="Multiple 'target repository' comments found" + ): + Assay.model_validate(third_invalid_assay_json) + + def test_person_phone_nr_validator(): + valid_person_json = { + "@id": "person_001", + "phone_nr": "+49123456789", + } + + invalid_person_json = { + "@id": "person_002", + "phone_nr": "123456789", + } - filtered_isa_json_study = filtered_isa_json["studies"][0] + assert Person.model_validate(valid_person_json) - assert len(good_isa_json_study["assays"]) == 5 - assert len(filtered_isa_json_study["assays"]) == 1 + with pytest.raises(ValidationError, match="Invalid number format"): + Person.model_validate(invalid_person_json) diff --git a/test-data/biosamples-input-isa.json b/test-data/biosamples-input-isa.json index ed6a8f8..bbd6121 100644 --- a/test-data/biosamples-input-isa.json +++ b/test-data/biosamples-input-isa.json @@ -1,1089 +1,1005 @@ { - "investigation":{ - "identifier":"", - "title":"Bob's investigation", - "description":"", - "submissionDate":"", - "publicReleaseDate":"", - "ontologySourceReferences":[ - - ], - "filename":"Bob's investigation.txt", - "comments":[ + "investigation": { + "identifier": "", + "title": "Bob's investigation", + "description": "", + "submissionDate": "", + "publicReleaseDate": "", + "ontologySourceReferences": [], + "filename": "Bob's investigation.txt", + "comments": [ { - "name":"ISAjson export time", - "value":"2022-11-07T08:09:59Z" + "name": "ISAjson export time", + "value": "2022-11-07T08:09:59Z" }, { - "name":"SEEK Project name", - "value":"Bob's PhD project" + "name": "SEEK Project name", + "value": "Bob's PhD project" }, { - "name":"SEEK Project ID", - "value":"http://localhost:3000/single_pages/2" + "name": "SEEK Project ID", + "value": "http://localhost:3000/single_pages/2" }, { - "name":"SEEK Investigation ID", - "value":"19" + "name": "SEEK Investigation ID", + "value": "19" } ], - "publications":[ - - ], - "people":[ + "publications": [], + "people": [ { - "@id":"#people/5", - "lastName":"Bob", - "firstName":"Bob", - "midInitials":"", - "email":"bob@testing.com", - "phone":"", - "fax":"", - "address":"", - "affiliation":"", - "roles":[ + "@id": "#people/5", + "lastName": "Bob", + "firstName": "Bob", + "midInitials": "", + "email": "bob@testing.com", + "phone": "", + "fax": "", + "address": "", + "affiliation": "", + "roles": [ { - "termAccession":"", - "termSource":"", - "annotationValue":"" + "termAccession": "", + "termSource": "", + "annotationValue": "" } ], - "comments":[ + "comments": [ { - "@id":"", - "value":"", - "name":"" + "@id": "", + "value": "", + "name": "" } ] } ], - "studies":[ + "studies": [ { - "identifier":"", - "title":"Arabidopsis thaliana", - "description":"Nucleic acid sequencing and metabolomics and proteomics of Arabidopsis thaliana in specific experimental conditions to test a specific hypothesis.\r\n", - "submissionDate":"", - "publicReleaseDate":"", - "filename":"Arabidopsis thaliana.txt", - "comments":[ + "identifier": "", + "title": "Arabidopsis thaliana", + "description": "Nucleic acid sequencing and metabolomics and proteomics of Arabidopsis thaliana in specific experimental conditions to test a specific hypothesis.\r\n", + "submissionDate": "", + "publicReleaseDate": "", + "filename": "Arabidopsis thaliana.txt", + "comments": [ { - "name":"SEEK Study ID", - "value":"10" + "name": "SEEK Study ID", + "value": "10" }, { - "name":"SEEK creation date", - "value":"2022-11-03T16:20:49Z" + "name": "SEEK creation date", + "value": "2022-11-03T16:20:49Z" } ], - "publications":[ - - ], - "people":[ + "publications": [], + "people": [ { - "@id":"#people/5", - "lastName":"Bob", - "firstName":"Bob", - "midInitials":"", - "email":"bob@testing.com", - "phone":"", - "fax":"", - "address":"", - "affiliation":"", - "roles":[ + "@id": "#people/5", + "lastName": "Bob", + "firstName": "Bob", + "midInitials": "", + "email": "bob@testing.com", + "phone": "", + "fax": "", + "address": "", + "affiliation": "", + "roles": [ { - "termAccession":"", - "termSource":"", - "annotationValue":"" + "termAccession": "", + "termSource": "", + "annotationValue": "" } ], - "comments":[ + "comments": [ { - "@id":"", - "value":"", - "name":"" + "@id": "", + "value": "", + "name": "" } ] } ], - "studyDesignDescriptors":[ - - ], - "characteristicCategories":[ + "studyDesignDescriptors": [], + "characteristicCategories": [ { - "@id":"#characteristic_category/Title_317", - "characteristicType":{ - "annotationValue":"Title", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/Title_317", + "characteristicType": { + "annotationValue": "Title", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/Description_318", - "characteristicType":{ - "annotationValue":"Description", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/Description_318", + "characteristicType": { + "annotationValue": "Description", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/tax_id_319", - "characteristicType":{ - "annotationValue":"tax_id", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/tax_id_319", + "characteristicType": { + "annotationValue": "tax_id", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/organism_320", - "characteristicType":{ - "annotationValue":"organism", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/organism_320", + "characteristicType": { + "annotationValue": "organism", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/cell_type_321", - "characteristicType":{ - "annotationValue":"cell_type", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/cell_type_321", + "characteristicType": { + "annotationValue": "cell_type", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/dev_stage_322", - "characteristicType":{ - "annotationValue":"dev_stage", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/dev_stage_322", + "characteristicType": { + "annotationValue": "dev_stage", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/collection_date_323", - "characteristicType":{ - "annotationValue":"collection_date", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/collection_date_323", + "characteristicType": { + "annotationValue": "collection_date", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/isolation_source_324", - "characteristicType":{ - "annotationValue":"isolation_source", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/isolation_source_324", + "characteristicType": { + "annotationValue": "isolation_source", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/collected_by_325", - "characteristicType":{ - "annotationValue":"collected_by", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/collected_by_325", + "characteristicType": { + "annotationValue": "collected_by", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/geographic_location_(country_and/or_sea)_326", - "characteristicType":{ - "annotationValue":"geographic location (country and/or sea)", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/geographic_location_(country_and/or_sea)_326", + "characteristicType": { + "annotationValue": "geographic location (country and/or sea)", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/submission_date_327", - "characteristicType":{ - "annotationValue":"submission date", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/submission_date_327", + "characteristicType": { + "annotationValue": "submission date", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/status_328", - "characteristicType":{ - "annotationValue":"status", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/status_328", + "characteristicType": { + "annotationValue": "status", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/accession_329", - "characteristicType":{ - "annotationValue":"accession", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/accession_329", + "characteristicType": { + "annotationValue": "accession", + "termAccession": "", + "termSource": "" } } ], - "materials":{ - "sources":[ + "materials": { + "sources": [ { - "@id":"#source/330", - "name":"plant 1", - "characteristics":[ + "@id": "#source/330", + "name": "plant 1", + "characteristics": [ { - "category":{ - "@id":"#characteristic_category/Title_317" - }, - "value":{ - "annotationValue":"plant 1", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/Title_317" + }, + "value": { + "annotationValue": "plant 1", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/Description_318" - }, - "value":{ - "annotationValue":"plant in the lab", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/Description_318" + }, + "value": { + "annotationValue": "plant in the lab", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/tax_id_319" - }, - "value":{ - "annotationValue":"NCBI:txid3702", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/tax_id_319" + }, + "value": { + "annotationValue": "NCBI:txid3702", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/organism_320" - }, - "value":{ - "annotationValue":"Arabidopsis thaliana", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/organism_320" + }, + "value": { + "annotationValue": "Arabidopsis thaliana", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/cell_type_321" - }, - "value":{ - "annotationValue":"na", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/cell_type_321" + }, + "value": { + "annotationValue": "na", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/dev_stage_322" - }, - "value":{ - "annotationValue":"budding", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/dev_stage_322" + }, + "value": { + "annotationValue": "budding", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/collection_date_323" - }, - "value":{ - "annotationValue":"01/01/2022", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/collection_date_323" + }, + "value": { + "annotationValue": "01/01/2022", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/isolation_source_324" - }, - "value":{ - "annotationValue":"seed", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/isolation_source_324" + }, + "value": { + "annotationValue": "seed", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/collected_by_325" - }, - "value":{ - "annotationValue":"Bob", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/collected_by_325" + }, + "value": { + "annotationValue": "Bob", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/geographic_location_(country_and/or_sea)_326" - }, - "value":{ - "annotationValue":"Belgium", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/geographic_location_(country_and/or_sea)_326" + }, + "value": { + "annotationValue": "Belgium", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/submission_date_327" - }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/submission_date_327" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/status_328" - }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/status_328" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/accession_329" - }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "#characteristic_category/accession_329" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } } ] } ], - "samples":[ + "samples": [ { - "@id":"#sample/331", - "name":"leaf 1", - "derivesFrom":[ + "@id": "#sample/331", + "name": "leaf 1", + "derivesFrom": [ { - "@id":"#source/330" + "@id": "#source/330" } ], - "characteristics":[ - - ], - "factorValues":[ + "characteristics": [], + "factorValues": [ { - "category":{ - "@id":"" - }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" - }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "category": { + "@id": "" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } } ] } ] }, - "protocols":[ + "protocols": [ { - "@id":"#protocol/18_10", - "name":"sample collection", - "protocolType":{ - "annotationValue":"sample collection", - "termAccession":"", - "termSource":"" + "@id": "#protocol/18_10", + "name": "sample collection", + "protocolType": { + "annotationValue": "sample collection", + "termAccession": "", + "termSource": "" }, - "description":"", - "uri":"", - "version":"", - "parameters":[ - - ], - "components":[ + "description": "", + "uri": "", + "version": "", + "parameters": [], + "components": [ { - "componentName":"", - "componentType":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" } } ] }, { - "@id":"#protocol/19_18", - "name":"nucleic acid extraction", - "protocolType":{ - "annotationValue":"nucleic acid extraction", - "termAccession":"", - "termSource":"" + "@id": "#protocol/19_18", + "name": "nucleic acid extraction", + "protocolType": { + "annotationValue": "nucleic acid extraction", + "termAccession": "", + "termSource": "" }, - "description":"", - "uri":"", - "version":"", - "parameters":[ - - ], - "components":[ + "description": "", + "uri": "", + "version": "", + "parameters": [], + "components": [ { - "componentName":"", - "componentType":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" } } ] }, { - "@id":"#protocol/20_20", - "name":"library construction", - "protocolType":{ - "annotationValue":"library construction", - "termAccession":"", - "termSource":"" + "@id": "#protocol/20_20", + "name": "library construction", + "protocolType": { + "annotationValue": "library construction", + "termAccession": "", + "termSource": "" }, - "description":"", - "uri":"", - "version":"", - "parameters":[ + "description": "", + "uri": "", + "version": "", + "parameters": [ { - "@id":"#parameter/349", - "parameterName":{ - "annotationValue":"library_construction_protocol", - "termAccession":"", - "termSource":"" + "@id": "#parameter/349", + "parameterName": { + "annotationValue": "library_construction_protocol", + "termAccession": "", + "termSource": "" } }, { - "@id":"#parameter/351", - "parameterName":{ - "annotationValue":"design_description", - "termAccession":"", - "termSource":"" + "@id": "#parameter/351", + "parameterName": { + "annotationValue": "design_description", + "termAccession": "", + "termSource": "" } }, { - "@id":"#parameter/352", - "parameterName":{ - "annotationValue":"library source", - "termAccession":"", - "termSource":"" + "@id": "#parameter/352", + "parameterName": { + "annotationValue": "library source", + "termAccession": "", + "termSource": "" } }, { - "@id":"#parameter/353", - "parameterName":{ - "annotationValue":"library strategy", - "termAccession":"", - "termSource":"" + "@id": "#parameter/353", + "parameterName": { + "annotationValue": "library strategy", + "termAccession": "", + "termSource": "" } }, { - "@id":"#parameter/354", - "parameterName":{ - "annotationValue":"library selection", - "termAccession":"", - "termSource":"" + "@id": "#parameter/354", + "parameterName": { + "annotationValue": "library selection", + "termAccession": "", + "termSource": "" } }, { - "@id":"#parameter/355", - "parameterName":{ - "annotationValue":"library layout", - "termAccession":"", - "termSource":"" + "@id": "#parameter/355", + "parameterName": { + "annotationValue": "library layout", + "termAccession": "", + "termSource": "" } }, { - "@id":"#parameter/356", - "parameterName":{ - "annotationValue":"insert size", - "termAccession":"", - "termSource":"" + "@id": "#parameter/356", + "parameterName": { + "annotationValue": "insert size", + "termAccession": "", + "termSource": "" } } ], - "components":[ + "components": [ { - "componentName":"", - "componentType":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" } } ] }, { - "@id":"#protocol/21_21", - "name":"nucleic acid sequencing", - "protocolType":{ - "annotationValue":"nucleic acid sequencing", - "termAccession":"", - "termSource":"" + "@id": "#protocol/21_21", + "name": "nucleic acid sequencing", + "protocolType": { + "annotationValue": "nucleic acid sequencing", + "termAccession": "", + "termSource": "" }, - "description":"", - "uri":"", - "version":"", - "parameters":[ + "description": "", + "uri": "", + "version": "", + "parameters": [ { - "@id":"#parameter/363", - "parameterName":{ - "annotationValue":"sequencing instrument", - "termAccession":"", - "termSource":"" + "@id": "#parameter/363", + "parameterName": { + "annotationValue": "sequencing instrument", + "termAccession": "", + "termSource": "" } } ], - "components":[ + "components": [ { - "componentName":"", - "componentType":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" } } ] } ], - "processSequence":[ + "processSequence": [ { - "@id":"#process/sample_collection/331", - "name":"", - "executesProtocol":{ - "@id":"#protocol/18_10" - }, - "parameterValues":[ - - ], - "performer":"", - "date":"", - "previousProcess":{ - - }, - "nextProcess":{ - + "@id": "#process/sample_collection/331", + "name": "", + "executesProtocol": { + "@id": "#protocol/18_10" }, - "inputs":[ + "parameterValues": [], + "performer": "", + "date": "", + "previousProcess": {}, + "nextProcess": {}, + "inputs": [ { - "@id":"#source/330" + "@id": "#source/330" } ], - "outputs":[ + "outputs": [ { - "@id":"#sample/331" + "@id": "#sample/331" } ] } ], - "assays":[ + "assays": [ { - "@id":"#assay/18_20_21", - "filename":"a_assays.txt", - "measurementType":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "@id": "#assay/18_20_21", + "filename": "a_assays.txt", + "measurementType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" }, - "technologyType":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "technologyType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" }, - "technologyPlatform":"", - "characteristicCategories":[ + "technologyPlatform": "", + "characteristicCategories": [ { - "@id":"#characteristic_category/Title_350", - "characteristicType":{ - "annotationValue":"Title", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/Title_350", + "characteristicType": { + "annotationValue": "Title", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/submission_date_358", - "characteristicType":{ - "annotationValue":"submission date", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/submission_date_358", + "characteristicType": { + "annotationValue": "submission date", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/status_359", - "characteristicType":{ - "annotationValue":"status", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/status_359", + "characteristicType": { + "annotationValue": "status", + "termAccession": "", + "termSource": "" } }, { - "@id":"#characteristic_category/accession_360", - "characteristicType":{ - "annotationValue":"accession", - "termAccession":"", - "termSource":"" + "@id": "#characteristic_category/accession_360", + "characteristicType": { + "annotationValue": "accession", + "termAccession": "", + "termSource": "" } } ], - "materials":{ - "samples":[ + "materials": { + "samples": [ { - "@id":"#sample/331" + "@id": "#sample/331" } ], - "otherMaterials":[ + "otherMaterials": [ { - "@id":"#other_material/332", - "name":"extract 1", - "type":"Extract Name", - "characteristics":[ - - ], - "derivesFrom":[ + "@id": "#other_material/332", + "name": "extract 1", + "type": "Extract Name", + "characteristics": [], + "derivesFrom": [ { - "@id":"#sample/331" + "@id": "#sample/331" } ] }, { - "@id":"#other_material/333", - "name":"library 1", - "type":"library name", - "characteristics":[ + "@id": "#other_material/333", + "name": "library 1", + "type": "Extract Name", + "characteristics": [ { - "category":{ - "@id":"#characteristic_category/Title_350" + "category": { + "@id": "#characteristic_category/Title_350" }, - "value":{ - "annotationValue":"library 1", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "library 1", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/submission_date_358" + "category": { + "@id": "#characteristic_category/submission_date_358" }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/status_359" + "category": { + "@id": "#characteristic_category/status_359" }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#characteristic_category/accession_360" + "category": { + "@id": "#characteristic_category/accession_360" }, - "value":{ - "annotationValue":"", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } } ], - "derivesFrom":[ + "derivesFrom": [ { - "@id":"#other_material/332" + "@id": "#other_material/332" } ] } ] }, - "processSequence":[ + "processSequence": [ { - "@id":"#process/nucleic_acid_extraction/332", - "name":"", - "executesProtocol":{ - "@id":"#protocol/19_18" + "@id": "#process/nucleic_acid_extraction/332", + "name": "", + "executesProtocol": { + "@id": "#protocol/19_18" }, - "parameterValues":[ - - ], - "performer":"", - "date":"", - "previousProcess":{ - "@id":"#process/sample_collection/332" + "parameterValues": [], + "performer": "", + "date": "", + "previousProcess": { + "@id": "#process/sample_collection/332" }, - "nextProcess":{ - "@id":"#process/library_construction/332" + "nextProcess": { + "@id": "#process/library_construction/332" }, - "inputs":[ + "inputs": [ { - "@id":"#sample/331" + "@id": "#sample/331" } ], - "outputs":[ + "outputs": [ { - "@id":"#other_material/332" + "@id": "#other_material/332" } ] }, { - "@id":"#process/library_construction/333", - "name":"", - "executesProtocol":{ - "@id":"#protocol/20_20" + "@id": "#process/library_construction/333", + "name": "", + "executesProtocol": { + "@id": "#protocol/20_20" }, - "parameterValues":[ + "parameterValues": [ { - "category":{ - "@id":"#parameter/349" + "category": { + "@id": "#parameter/349" }, - "value":{ - "annotationValue":"lib prep", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "lib prep", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#parameter/351" + "category": { + "@id": "#parameter/351" }, - "value":{ - "annotationValue":"Test", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "Test", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#parameter/352" + "category": { + "@id": "#parameter/352" }, - "value":{ - "annotationValue":"OTHER", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "OTHER", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#parameter/353" + "category": { + "@id": "#parameter/353" }, - "value":{ - "annotationValue":"OTHER", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "OTHER", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#parameter/354" + "category": { + "@id": "#parameter/354" }, - "value":{ - "annotationValue":"RT-PCR", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "RT-PCR", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#parameter/355" + "category": { + "@id": "#parameter/355" }, - "value":{ - "annotationValue":"SINGLE", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "SINGLE", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } }, { - "category":{ - "@id":"#parameter/356" + "category": { + "@id": "#parameter/356" }, - "value":{ - "annotationValue":"100", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": "100", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } } ], - "performer":"", - "date":"", - "previousProcess":{ - "@id":"#process/nucleic_acid_extraction/333" + "performer": "", + "date": "", + "previousProcess": { + "@id": "#process/nucleic_acid_extraction/333" }, - "nextProcess":{ - "@id":"#process/nucleic_acid_sequencing/333" + "nextProcess": { + "@id": "#process/nucleic_acid_sequencing/333" }, - "inputs":[ + "inputs": [ { - "@id":"#other_material/332" + "@id": "#other_material/332" } ], - "outputs":[ + "outputs": [ { - "@id":"#other_material/333" + "@id": "#other_material/333" } ] }, { - "@id":"#process/nucleic_acid_sequencing/334", - "name":"", - "executesProtocol":{ - "@id":"#protocol/21_21" + "@id": "#process/nucleic_acid_sequencing/334", + "name": "", + "executesProtocol": { + "@id": "#protocol/21_21" }, - "parameterValues":[ + "parameterValues": [ { - "category":{ - "@id":"#parameter/363" + "category": { + "@id": "#parameter/363" }, - "value":{ - "annotationValue":" MinION", - "termSource":"", - "termAccession":"" + "value": { + "annotationValue": " MinION", + "termSource": "", + "termAccession": "" }, - "unit":{ - "termSource":"", - "termAccession":"", - "comments":[ - - ] + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] } } ], - "performer":"", - "date":"", - "previousProcess":{ - "@id":"#process/library_construction/334" - }, - "nextProcess":{ - + "performer": "", + "date": "", + "previousProcess": { + "@id": "#process/library_construction/334" }, - "inputs":[ + "nextProcess": {}, + "inputs": [ { - "@id":"#other_material/333" + "@id": "#other_material/333" } ], - "outputs":[ + "outputs": [ { - "@id":"#data_file/334" + "@id": "#data_file/334" } ] } ], - "dataFiles":[ + "dataFiles": [ { - "@id":"#data/334", - "name":"fake2.bam", - "type":"Raw Data File", - "comments":[ + "@id": "#data/334", + "name": "fake2.bam", + "type": "Raw Data File", + "comments": [ { - "name":"file type", - "value":"bam" + "name": "file type", + "value": "bam" }, { - "name":"file checksum", - "value":"9840f585055afc37de353706fd31a377" + "name": "file checksum", + "value": "9840f585055afc37de353706fd31a377" }, { - "name":"submission date", - "value":"" + "name": "submission date", + "value": "" }, { - "name":"status", - "value":"" + "name": "status", + "value": "" }, { - "name":"accession", - "value":"" + "name": "accession", + "value": "" } ] } ], - "unitCategories":[ - - ] + "unitCategories": [] } ], - "factors":[ - - ], - "unitCategories":[ - - ] + "factors": [], + "unitCategories": [] } ] } -} +} \ No newline at end of file diff --git a/test-data/biosamples-modified-isa.json b/test-data/biosamples-modified-isa.json index 80f9319..b71ad8a 100644 --- a/test-data/biosamples-modified-isa.json +++ b/test-data/biosamples-modified-isa.json @@ -1,877 +1,1022 @@ { - "investigation" : { - "identifier" : "", - "title" : "Bob's investigation", - "description" : "", - "submissionDate" : "", - "publicReleaseDate" : "", - "ontologySourceReferences" : [ ], - "filename" : "Bob's investigation.txt", - "comments" : [ { - "name" : "ISAjson export time", - "value" : "2022-11-07T08:09:59Z" - }, { - "name" : "SEEK Project name", - "value" : "Bob's PhD project" - }, { - "name" : "SEEK Project ID", - "value" : "http://localhost:3000/single_pages/2" - }, { - "name" : "SEEK Investigation ID", - "value" : "19" - } ], - "publications" : [ ], - "people" : [ { - "lastName" : "Bob", - "firstName" : "Bob", - "midInitials" : "", - "email" : "bob@testing.com", - "phone" : "", - "fax" : "", - "address" : "", - "affiliation" : "", - "roles" : [ { - "termAccession" : "", - "termSource" : "", - "annotationValue" : "" - } ], - "comments" : [ { - "name" : "", - "value" : "", - "@id" : "" - } ], - "@id" : "#people/5" - } ], - "studies" : [ { - "identifier" : "", - "title" : "Arabidopsis thaliana", - "description" : "Nucleic acid sequencing and metabolomics and proteomics of Arabidopsis thaliana in specific experimental conditions to test a specific hypothesis.\r\n", - "submissionDate" : "", - "publicReleaseDate" : "", - "filename" : "Arabidopsis thaliana.txt", - "comments" : [ { - "name" : "SEEK Study ID", - "value" : "10" - }, { - "name" : "SEEK creation date", - "value" : "2022-11-03T16:20:49Z" - } ], - "publications" : [ ], - "people" : [ { - "lastName" : "Bob", - "firstName" : "Bob", - "midInitials" : "", - "email" : "bob@testing.com", - "phone" : "", - "fax" : "", - "address" : "", - "affiliation" : "", - "roles" : [ { - "termAccession" : "", - "termSource" : "", - "annotationValue" : "" - } ], - "comments" : [ { - "name" : "", - "value" : "", - "@id" : "" - } ], - "@id" : "#people/5" - } ], - "studyDesignDescriptors" : [ ], - "characteristicCategories" : [ { - "characteristicType" : { - "annotationValue" : "Title", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/Title_317" - }, { - "characteristicType" : { - "annotationValue" : "Description", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/Description_318" - }, { - "characteristicType" : { - "annotationValue" : "tax_id", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/tax_id_319" - }, { - "characteristicType" : { - "annotationValue" : "organism", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/organism_320" - }, { - "characteristicType" : { - "annotationValue" : "cell_type", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/cell_type_321" - }, { - "characteristicType" : { - "annotationValue" : "dev_stage", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/dev_stage_322" - }, { - "characteristicType" : { - "annotationValue" : "collection_date", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/collection_date_323" - }, { - "characteristicType" : { - "annotationValue" : "isolation_source", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/isolation_source_324" - }, { - "characteristicType" : { - "annotationValue" : "collected_by", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/collected_by_325" - }, { - "characteristicType" : { - "annotationValue" : "geographic location (country and/or sea)", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/geographic_location_(country_and/or_sea)_326" - }, { - "characteristicType" : { - "annotationValue" : "submission date", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/submission_date_327" - }, { - "characteristicType" : { - "annotationValue" : "status", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/status_328" - }, { - "characteristicType" : { - "annotationValue" : "accession", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/accession_329" - } ], - "materials" : { - "sources" : [ { - "name" : "plant 1", - "characteristics" : [ { - "category" : { - "@id" : "#characteristic_category/Title_317" - }, - "value" : { - "annotationValue" : "plant 1", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/Description_318" - }, - "value" : { - "annotationValue" : "plant in the lab", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/tax_id_319" - }, - "value" : { - "annotationValue" : "NCBI:txid3702", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/organism_320" - }, - "value" : { - "annotationValue" : "Arabidopsis thaliana", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/cell_type_321" - }, - "value" : { - "annotationValue" : "na", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/dev_stage_322" - }, - "value" : { - "annotationValue" : "budding", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/collection_date_323" - }, - "value" : { - "annotationValue" : "01/01/2022", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/isolation_source_324" - }, - "value" : { - "annotationValue" : "seed", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/collected_by_325" + "investigation": { + "identifier": "", + "title": "Bob's investigation", + "description": "", + "submissionDate": "", + "publicReleaseDate": "", + "ontologySourceReferences": [], + "filename": "Bob's investigation.txt", + "comments": [ + { + "name": "ISAjson export time", + "value": "2022-11-07T08:09:59Z" + }, + { + "name": "SEEK Project name", + "value": "Bob's PhD project" + }, + { + "name": "SEEK Project ID", + "value": "http://localhost:3000/single_pages/2" + }, + { + "name": "SEEK Investigation ID", + "value": "19" + } + ], + "publications": [], + "people": [ + { + "lastName": "Bob", + "firstName": "Bob", + "midInitials": "", + "email": "bob@testing.com", + "phone": "", + "fax": "", + "address": "", + "affiliation": "", + "roles": [ + { + "termAccession": "", + "termSource": "", + "annotationValue": "" + } + ], + "comments": [ + { + "name": "", + "value": "", + "@id": "" + } + ], + "@id": "#people/5" + } + ], + "studies": [ + { + "identifier": "", + "title": "Arabidopsis thaliana", + "description": "Nucleic acid sequencing and metabolomics and proteomics of Arabidopsis thaliana in specific experimental conditions to test a specific hypothesis.\r\n", + "submissionDate": "", + "publicReleaseDate": "", + "filename": "Arabidopsis thaliana.txt", + "comments": [ + { + "name": "SEEK Study ID", + "value": "10" + }, + { + "name": "SEEK creation date", + "value": "2022-11-03T16:20:49Z" + } + ], + "publications": [], + "people": [ + { + "lastName": "Bob", + "firstName": "Bob", + "midInitials": "", + "email": "bob@testing.com", + "phone": "", + "fax": "", + "address": "", + "affiliation": "", + "roles": [ + { + "termAccession": "", + "termSource": "", + "annotationValue": "" + } + ], + "comments": [ + { + "name": "", + "value": "", + "@id": "" + } + ], + "@id": "#people/5" + } + ], + "studyDesignDescriptors": [], + "characteristicCategories": [ + { + "characteristicType": { + "annotationValue": "Title", + "termAccession": "", + "termSource": "" + }, + "@id": "#characteristic_category/Title_317" + }, + { + "characteristicType": { + "annotationValue": "Description", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "Bob", - "termSource" : "", - "termAccession" : "" + "@id": "#characteristic_category/Description_318" + }, + { + "characteristicType": { + "annotationValue": "tax_id", + "termAccession": "", + "termSource": "" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/geographic_location_(country_and/or_sea)_326" + "@id": "#characteristic_category/tax_id_319" + }, + { + "characteristicType": { + "annotationValue": "organism", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "Belgium", - "termSource" : "", - "termAccession" : "" + "@id": "#characteristic_category/organism_320" + }, + { + "characteristicType": { + "annotationValue": "cell_type", + "termAccession": "", + "termSource": "" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/submission_date_327" + "@id": "#characteristic_category/cell_type_321" + }, + { + "characteristicType": { + "annotationValue": "dev_stage", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" + "@id": "#characteristic_category/dev_stage_322" + }, + { + "characteristicType": { + "annotationValue": "collection_date", + "termAccession": "", + "termSource": "" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/status_328" + "@id": "#characteristic_category/collection_date_323" + }, + { + "characteristicType": { + "annotationValue": "isolation_source", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" + "@id": "#characteristic_category/isolation_source_324" + }, + { + "characteristicType": { + "annotationValue": "collected_by", + "termAccession": "", + "termSource": "" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/accession_329" + "@id": "#characteristic_category/collected_by_325" + }, + { + "characteristicType": { + "annotationValue": "geographic location (country and/or sea)", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" + "@id": "#characteristic_category/geographic_location_(country_and/or_sea)_326" + }, + { + "characteristicType": { + "annotationValue": "submission date", + "termAccession": "", + "termSource": "" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/accession" + "@id": "#characteristic_category/submission_date_327" + }, + { + "characteristicType": { + "annotationValue": "status", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "SAMEA130788488" - } - } ], - "@id" : "#source/330" - } ], - "samples" : [ { - "name" : "leaf 1", - "derivesFrom" : [ { - "@id" : "#source/330" - } ], - "characteristics" : [ { - "category" : { - "@id" : "#characteristic_category/accession" + "@id": "#characteristic_category/status_328" + }, + { + "characteristicType": { + "annotationValue": "accession", + "termAccession": "", + "termSource": "" }, - "value" : { - "annotationValue" : "SAMEA130788489" + "@id": "#characteristic_category/accession_329" + } + ], + "materials": { + "sources": [ + { + "name": "plant 1", + "characteristics": [ + { + "category": { + "@id": "#characteristic_category/Title_317" + }, + "value": { + "annotationValue": "plant 1", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/Description_318" + }, + "value": { + "annotationValue": "plant in the lab", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/tax_id_319" + }, + "value": { + "annotationValue": "NCBI:txid3702", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/organism_320" + }, + "value": { + "annotationValue": "Arabidopsis thaliana", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/cell_type_321" + }, + "value": { + "annotationValue": "na", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/dev_stage_322" + }, + "value": { + "annotationValue": "budding", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/collection_date_323" + }, + "value": { + "annotationValue": "01/01/2022", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/isolation_source_324" + }, + "value": { + "annotationValue": "seed", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/collected_by_325" + }, + "value": { + "annotationValue": "Bob", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/geographic_location_(country_and/or_sea)_326" + }, + "value": { + "annotationValue": "Belgium", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/submission_date_327" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/status_328" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/accession_329" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/accession" + }, + "value": { + "annotationValue": "SAMEA130788488" + } + } + ], + "@id": "#source/330" } - } ], - "factorValues" : [ { - "category" : { - "@id" : "" - }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] + ], + "samples": [ + { + "name": "leaf 1", + "derivesFrom": [ + { + "@id": "#source/330" + } + ], + "characteristics": [ + { + "category": { + "@id": "#characteristic_category/accession" + }, + "value": { + "annotationValue": "SAMEA130788489" + } + } + ], + "factorValues": [ + { + "category": { + "@id": "" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + } + ], + "@id": "#sample/331" } - } ], - "@id" : "#sample/331" - } ] - }, - "protocols" : [ { - "name" : "sample collection", - "protocolType" : { - "annotationValue" : "sample collection", - "termAccession" : "", - "termSource" : "" + ] }, - "description" : "", - "uri" : "", - "version" : "", - "parameters" : [ ], - "components" : [ { - "componentName" : "", - "componentType" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - } - } ], - "@id" : "#protocol/18_10" - }, { - "name" : "nucleic acid extraction", - "protocolType" : { - "annotationValue" : "nucleic acid extraction", - "termAccession" : "", - "termSource" : "" - }, - "description" : "", - "uri" : "", - "version" : "", - "parameters" : [ ], - "components" : [ { - "componentName" : "", - "componentType" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - } - } ], - "@id" : "#protocol/19_18" - }, { - "name" : "library construction", - "protocolType" : { - "annotationValue" : "library construction", - "termAccession" : "", - "termSource" : "" - }, - "description" : "", - "uri" : "", - "version" : "", - "parameters" : [ { - "parameterName" : { - "annotationValue" : "library_construction_protocol", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/349" - }, { - "parameterName" : { - "annotationValue" : "design_description", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/351" - }, { - "parameterName" : { - "annotationValue" : "library source", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/352" - }, { - "parameterName" : { - "annotationValue" : "library strategy", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/353" - }, { - "parameterName" : { - "annotationValue" : "library selection", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/354" - }, { - "parameterName" : { - "annotationValue" : "library layout", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/355" - }, { - "parameterName" : { - "annotationValue" : "insert size", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/356" - } ], - "components" : [ { - "componentName" : "", - "componentType" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - } - } ], - "@id" : "#protocol/20_20" - }, { - "name" : "nucleic acid sequencing", - "protocolType" : { - "annotationValue" : "nucleic acid sequencing", - "termAccession" : "", - "termSource" : "" - }, - "description" : "", - "uri" : "", - "version" : "", - "parameters" : [ { - "parameterName" : { - "annotationValue" : "sequencing instrument", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#parameter/363" - } ], - "components" : [ { - "componentName" : "", - "componentType" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - } - } ], - "@id" : "#protocol/21_21" - } ], - "processSequence" : [ { - "name" : "", - "executesProtocol" : { - "@id" : "#protocol/18_10" - }, - "parameterValues" : [ ], - "performer" : "", - "date" : "", - "previousProcess" : { }, - "nextProcess" : { }, - "inputs" : [ { - "@id" : "#source/330" - } ], - "outputs" : [ { - "@id" : "#sample/331" - } ], - "@id" : "#process/sample_collection/331" - } ], - "assays" : [ { - "filename" : "a_assays.txt", - "measurementType" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - }, - "technologyType" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" - }, - "technologyPlatform" : "", - "characteristicCategories" : [ { - "characteristicType" : { - "annotationValue" : "Title", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/Title_350" - }, { - "characteristicType" : { - "annotationValue" : "submission date", - "termAccession" : "", - "termSource" : "" - }, - "@id" : "#characteristic_category/submission_date_358" - }, { - "characteristicType" : { - "annotationValue" : "status", - "termAccession" : "", - "termSource" : "" + "protocols": [ + { + "name": "sample collection", + "protocolType": { + "annotationValue": "sample collection", + "termAccession": "", + "termSource": "" + }, + "description": "", + "uri": "", + "version": "", + "parameters": [], + "components": [ + { + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + } + } + ], + "@id": "#protocol/18_10" }, - "@id" : "#characteristic_category/status_359" - }, { - "characteristicType" : { - "annotationValue" : "accession", - "termAccession" : "", - "termSource" : "" + { + "name": "nucleic acid extraction", + "protocolType": { + "annotationValue": "nucleic acid extraction", + "termAccession": "", + "termSource": "" + }, + "description": "", + "uri": "", + "version": "", + "parameters": [], + "components": [ + { + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + } + } + ], + "@id": "#protocol/19_18" }, - "@id" : "#characteristic_category/accession_360" - } ], - "materials" : { - "samples" : [ { - "@id" : "#sample/331" - } ], - "otherMaterials" : [ { - "name" : "extract 1", - "type" : "Extract Name", - "characteristics" : [ ], - "derivesFrom" : [ { - "@id" : "#sample/331" - } ], - "@id" : "#other_material/332" - }, { - "name" : "library 1", - "type" : "library name", - "characteristics" : [ { - "category" : { - "@id" : "#characteristic_category/Title_350" + { + "name": "library construction", + "protocolType": { + "annotationValue": "library construction", + "termAccession": "", + "termSource": "" + }, + "description": "", + "uri": "", + "version": "", + "parameters": [ + { + "parameterName": { + "annotationValue": "library_construction_protocol", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/349" }, - "value" : { - "annotationValue" : "library 1", - "termSource" : "", - "termAccession" : "" + { + "parameterName": { + "annotationValue": "design_description", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/351" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#characteristic_category/submission_date_358" + { + "parameterName": { + "annotationValue": "library source", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/352" }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" + { + "parameterName": { + "annotationValue": "library strategy", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/353" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] + { + "parameterName": { + "annotationValue": "library selection", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/354" + }, + { + "parameterName": { + "annotationValue": "library layout", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/355" + }, + { + "parameterName": { + "annotationValue": "insert size", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/356" } - }, { - "category" : { - "@id" : "#characteristic_category/status_359" + ], + "components": [ + { + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + } + } + ], + "@id": "#protocol/20_20" + }, + { + "name": "nucleic acid sequencing", + "protocolType": { + "annotationValue": "nucleic acid sequencing", + "termAccession": "", + "termSource": "" + }, + "description": "", + "uri": "", + "version": "", + "parameters": [ + { + "parameterName": { + "annotationValue": "sequencing instrument", + "termAccession": "", + "termSource": "" + }, + "@id": "#parameter/363" + } + ], + "components": [ + { + "componentName": "", + "componentType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + } + } + ], + "@id": "#protocol/21_21" + } + ], + "processSequence": [ + { + "name": "", + "executesProtocol": { + "@id": "#protocol/18_10" + }, + "parameterValues": [], + "performer": "", + "date": "", + "previousProcess": {}, + "nextProcess": {}, + "inputs": [ + { + "@id": "#source/330" + } + ], + "outputs": [ + { + "@id": "#sample/331" + } + ], + "@id": "#process/sample_collection/331" + } + ], + "assays": [ + { + "filename": "a_assays.txt", + "measurementType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "technologyType": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "technologyPlatform": "", + "characteristicCategories": [ + { + "characteristicType": { + "annotationValue": "Title", + "termAccession": "", + "termSource": "" + }, + "@id": "#characteristic_category/Title_350" }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" + { + "characteristicType": { + "annotationValue": "submission date", + "termAccession": "", + "termSource": "" + }, + "@id": "#characteristic_category/submission_date_358" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] + { + "characteristicType": { + "annotationValue": "status", + "termAccession": "", + "termSource": "" + }, + "@id": "#characteristic_category/status_359" + }, + { + "characteristicType": { + "annotationValue": "accession", + "termAccession": "", + "termSource": "" + }, + "@id": "#characteristic_category/accession_360" } - }, { - "category" : { - "@id" : "#characteristic_category/accession_360" + ], + "materials": { + "samples": [ + { + "@id": "#sample/331" + } + ], + "otherMaterials": [ + { + "name": "extract 1", + "type": "Extract Name", + "characteristics": [], + "derivesFrom": [ + { + "@id": "#sample/331" + } + ], + "@id": "#other_material/332" + }, + { + "name": "library 1", + "type": "Extract Name", + "characteristics": [ + { + "category": { + "@id": "#characteristic_category/Title_350" + }, + "value": { + "annotationValue": "library 1", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/submission_date_358" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/status_359" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#characteristic_category/accession_360" + }, + "value": { + "annotationValue": "", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + } + ], + "derivesFrom": [ + { + "@id": "#other_material/332" + } + ], + "@id": "#other_material/333" + } + ] + }, + "processSequence": [ + { + "name": "", + "executesProtocol": { + "@id": "#protocol/19_18" + }, + "parameterValues": [], + "performer": "", + "date": "", + "previousProcess": { + "@id": "#process/sample_collection/332" + }, + "nextProcess": { + "@id": "#process/library_construction/332" + }, + "inputs": [ + { + "@id": "#sample/331" + } + ], + "outputs": [ + { + "@id": "#other_material/332" + } + ], + "@id": "#process/nucleic_acid_extraction/332" }, - "value" : { - "annotationValue" : "", - "termSource" : "", - "termAccession" : "" + { + "name": "", + "executesProtocol": { + "@id": "#protocol/20_20" + }, + "parameterValues": [ + { + "category": { + "@id": "#parameter/349" + }, + "value": { + "annotationValue": "lib prep", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#parameter/351" + }, + "value": { + "annotationValue": "Test", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#parameter/352" + }, + "value": { + "annotationValue": "OTHER", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#parameter/353" + }, + "value": { + "annotationValue": "OTHER", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#parameter/354" + }, + "value": { + "annotationValue": "RT-PCR", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#parameter/355" + }, + "value": { + "annotationValue": "SINGLE", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + }, + { + "category": { + "@id": "#parameter/356" + }, + "value": { + "annotationValue": "100", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + } + ], + "performer": "", + "date": "", + "previousProcess": { + "@id": "#process/nucleic_acid_extraction/333" + }, + "nextProcess": { + "@id": "#process/nucleic_acid_sequencing/333" + }, + "inputs": [ + { + "@id": "#other_material/332" + } + ], + "outputs": [ + { + "@id": "#other_material/333" + } + ], + "@id": "#process/library_construction/333" }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] + { + "name": "", + "executesProtocol": { + "@id": "#protocol/21_21" + }, + "parameterValues": [ + { + "category": { + "@id": "#parameter/363" + }, + "value": { + "annotationValue": " MinION", + "termSource": "", + "termAccession": "" + }, + "unit": { + "termSource": "", + "termAccession": "", + "comments": [] + } + } + ], + "performer": "", + "date": "", + "previousProcess": { + "@id": "#process/library_construction/334" + }, + "nextProcess": {}, + "inputs": [ + { + "@id": "#other_material/333" + } + ], + "outputs": [ + { + "@id": "#data_file/334" + } + ], + "@id": "#process/nucleic_acid_sequencing/334" } - } ], - "derivesFrom" : [ { - "@id" : "#other_material/332" - } ], - "@id" : "#other_material/333" - } ] - }, - "processSequence" : [ { - "name" : "", - "executesProtocol" : { - "@id" : "#protocol/19_18" - }, - "parameterValues" : [ ], - "performer" : "", - "date" : "", - "previousProcess" : { - "@id" : "#process/sample_collection/332" - }, - "nextProcess" : { - "@id" : "#process/library_construction/332" - }, - "inputs" : [ { - "@id" : "#sample/331" - } ], - "outputs" : [ { - "@id" : "#other_material/332" - } ], - "@id" : "#process/nucleic_acid_extraction/332" - }, { - "name" : "", - "executesProtocol" : { - "@id" : "#protocol/20_20" - }, - "parameterValues" : [ { - "category" : { - "@id" : "#parameter/349" - }, - "value" : { - "annotationValue" : "lib prep", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#parameter/351" - }, - "value" : { - "annotationValue" : "Test", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#parameter/352" - }, - "value" : { - "annotationValue" : "OTHER", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#parameter/353" - }, - "value" : { - "annotationValue" : "OTHER", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#parameter/354" - }, - "value" : { - "annotationValue" : "RT-PCR", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#parameter/355" - }, - "value" : { - "annotationValue" : "SINGLE", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - }, { - "category" : { - "@id" : "#parameter/356" - }, - "value" : { - "annotationValue" : "100", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - } ], - "performer" : "", - "date" : "", - "previousProcess" : { - "@id" : "#process/nucleic_acid_extraction/333" - }, - "nextProcess" : { - "@id" : "#process/nucleic_acid_sequencing/333" - }, - "inputs" : [ { - "@id" : "#other_material/332" - } ], - "outputs" : [ { - "@id" : "#other_material/333" - } ], - "@id" : "#process/library_construction/333" - }, { - "name" : "", - "executesProtocol" : { - "@id" : "#protocol/21_21" - }, - "parameterValues" : [ { - "category" : { - "@id" : "#parameter/363" - }, - "value" : { - "annotationValue" : " MinION", - "termSource" : "", - "termAccession" : "" - }, - "unit" : { - "termSource" : "", - "termAccession" : "", - "comments" : [ ] - } - } ], - "performer" : "", - "date" : "", - "previousProcess" : { - "@id" : "#process/library_construction/334" - }, - "nextProcess" : { }, - "inputs" : [ { - "@id" : "#other_material/333" - } ], - "outputs" : [ { - "@id" : "#data_file/334" - } ], - "@id" : "#process/nucleic_acid_sequencing/334" - } ], - "dataFiles" : [ { - "name" : "fake2.bam", - "type" : "Raw Data File", - "comments" : [ { - "name" : "file type", - "value" : "bam" - }, { - "name" : "file checksum", - "value" : "9840f585055afc37de353706fd31a377" - }, { - "name" : "submission date", - "value" : "" - }, { - "name" : "status", - "value" : "" - }, { - "name" : "accession", - "value" : "" - } ], - "@id" : "#data/334" - } ], - "unitCategories" : [ ], - "@id" : "#assay/18_20_21" - } ], - "factors" : [ ], - "unitCategories" : [ ] - } ] + ], + "dataFiles": [ + { + "name": "fake2.bam", + "type": "Raw Data File", + "comments": [ + { + "name": "file type", + "value": "bam" + }, + { + "name": "file checksum", + "value": "9840f585055afc37de353706fd31a377" + }, + { + "name": "submission date", + "value": "" + }, + { + "name": "status", + "value": "" + }, + { + "name": "accession", + "value": "" + } + ], + "@id": "#data/334" + } + ], + "unitCategories": [], + "@id": "#assay/18_20_21" + } + ], + "factors": [], + "unitCategories": [] + } + ] } -} +} \ No newline at end of file