diff --git a/api/adapters/base.py b/api/adapters/base.py index dbe6eab..6343451 100644 --- a/api/adapters/base.py +++ b/api/adapters/base.py @@ -1,7 +1,7 @@ import abc - +from typing import Type from api.config import Settings, get_settings -from api.models.catalog import DatasetMetadataDOC +from api.models.catalog import T from api.models.user import Submission @@ -9,7 +9,7 @@ class AbstractRepositoryRequestHandler(abc.ABC): settings: Settings = get_settings() @abc.abstractmethod - def get_metadata(self, record_id: str): + def get_metadata(self, record_id: str) -> dict: """Returns the metadata for the specified record from a repository""" ... @@ -19,13 +19,13 @@ class AbstractRepositoryMetadataAdapter(abc.ABC): @staticmethod @abc.abstractmethod - def to_catalog_record(metadata: dict) -> DatasetMetadataDOC: + def to_catalog_record(metadata: dict, meta_model_type: Type[T]) -> T: """Converts repository metadata to a catalog dataset record""" ... @staticmethod @abc.abstractmethod - def to_repository_record(catalog_record: DatasetMetadataDOC): + def to_repository_record(catalog_record: T): """Converts dataset catalog dataset record to repository metadata""" ... @@ -35,7 +35,7 @@ def update_submission(submission: Submission, repo_record_id: str) -> Submission """Sets additional repository specific metadata to submission record""" ... - async def get_metadata(self, record_id: str): + async def get_metadata(self, record_id: str) -> dict: """Returns the metadata for the specified record from a repository""" return self.repo_api_handler.get_metadata(record_id) diff --git a/api/adapters/hydroshare.py b/api/adapters/hydroshare.py index 67a0a6d..11d9b37 100644 --- a/api/adapters/hydroshare.py +++ b/api/adapters/hydroshare.py @@ -7,8 +7,8 @@ from api.adapters.utils import RepositoryType, register_adapter from api.exceptions import RepositoryException from api.models import schema -from api.models.catalog import DatasetMetadataDOC -from api.models.user import Submission, SubmissionType +from api.models.catalog import HSResourceMetadataDOC +from api.models.user import Submission class Creator(BaseModel): @@ -163,7 +163,8 @@ def to_dataset_license(self): class _HydroshareRequestHandler(AbstractRepositoryRequestHandler): - def get_metadata(self, record_id: str): + + def get_metadata(self, record_id: str) -> dict: hs_meta_url = self.settings.hydroshare_meta_read_url % record_id hs_file_url = self.settings.hydroshare_file_read_url % record_id @@ -194,13 +195,13 @@ class HydroshareMetadataAdapter(AbstractRepositoryMetadataAdapter): repo_api_handler = _HydroshareRequestHandler() @staticmethod - def to_catalog_record(metadata: dict) -> DatasetMetadataDOC: + def to_catalog_record(metadata: dict, meta_model_type: HSResourceMetadataDOC) -> HSResourceMetadataDOC: """Converts hydroshare resource metadata to a catalog dataset record""" hs_metadata_model = _HydroshareResourceMetadata(**metadata) return hs_metadata_model.to_catalog_dataset() @staticmethod - def to_repository_record(catalog_record: DatasetMetadataDOC): + def to_repository_record(catalog_record: HSResourceMetadataDOC): """Converts dataset catalog record to hydroshare resource metadata""" raise NotImplementedError @@ -293,7 +294,7 @@ def to_dataset_provider(): return provider def to_catalog_dataset(self): - dataset = DatasetMetadataDOC.construct() + dataset = HSResourceMetadataDOC.construct() dataset.provider = self.to_dataset_provider() dataset.name = self.title dataset.description = self.abstract diff --git a/api/adapters/s3.py b/api/adapters/s3.py index 5620904..acb55e7 100644 --- a/api/adapters/s3.py +++ b/api/adapters/s3.py @@ -1,28 +1,61 @@ -import boto3 import json -from botocore.client import Config +from http import HTTPStatus +from typing import Type + +import boto3 from botocore import UNSIGNED +from botocore.client import Config +from botocore.exceptions import ClientError as S3ClientError -from api.adapters.base import AbstractRepositoryMetadataAdapter, AbstractRepositoryRequestHandler +from api.adapters.base import ( + AbstractRepositoryMetadataAdapter, + AbstractRepositoryRequestHandler, +) from api.adapters.utils import RepositoryType, register_adapter -from api.models.catalog import DatasetMetadataDOC -from api.models.user import Submission, SubmissionType +from api.exceptions import RepositoryException +from api.models.catalog import T +from api.models.user import Submission class _S3RequestHandler(AbstractRepositoryRequestHandler): - def get_metadata(self, record_id: str): + def get_metadata(self, record_id: str) -> dict: endpoint_url = record_id.split("+")[0] bucket_name = record_id.split("+")[1] file_key = record_id.split("+")[2] - s3 = boto3.client('s3', config=Config(signature_version=UNSIGNED), endpoint_url=endpoint_url) - - response = s3.get_object(Bucket=bucket_name, Key=file_key) - json_content = response['Body'].read().decode('utf-8') + # TODO: Should we be expecting the path for the data file and then compute the metadata file path from that? + # Or should we be expecting the metadata file path directly? May be we should get path for both + # data file and metadata file. If have the path for the data file we can check that the data file + # exists and then retrieve the metadata file and catalog the metadata. + + # check if the endpoint URL is an AWS S3 URL + if endpoint_url.endswith("amazonaws.com"): + endpoint_url = None + s3 = boto3.client( + "s3", config=Config(signature_version=UNSIGNED), endpoint_url=endpoint_url + ) + try: + response = s3.get_object(Bucket=bucket_name, Key=file_key) + except S3ClientError as ex: + if ex.response["Error"]["Code"] == "NoSuchKey": + raise RepositoryException( + detail=f"Specified metadata file was not found in S3: {bucket_name}/{file_key}", + status_code=HTTPStatus.NOT_FOUND + ) + else: + err_msg = f"Error accessing S3 file({bucket_name}/{file_key}): {str(ex)}" + raise RepositoryException(detail=err_msg, status_code=HTTPStatus.BAD_REQUEST) - # Parse the JSON content - data = json.loads(json_content) + json_content = response["Body"].read().decode("utf-8") + # parse the JSON content + try: + data = json.loads(json_content) + except json.JSONDecodeError as ex: + err_msg = f"Invalid JSON content in S3 file ({file_key}). Error: {str(ex)}" + raise RepositoryException(detail=err_msg, status_code=HTTPStatus.BAD_REQUEST) + # remove additionalType field - this will be set by the schema model + data.pop("additionalType", None) return data @@ -30,12 +63,12 @@ class S3MetadataAdapter(AbstractRepositoryMetadataAdapter): repo_api_handler = _S3RequestHandler() @staticmethod - def to_catalog_record(metadata: dict) -> DatasetMetadataDOC: - return DatasetMetadataDOC(**metadata) + def to_catalog_record(metadata: dict, meta_model_type: Type[T]) -> T: + return meta_model_type(**metadata) @staticmethod - def to_repository_record(catalog_record: DatasetMetadataDOC): - """Converts dataset catalog record to hydroshare resource metadata""" + def to_repository_record(catalog_record: T): + """Converts dataset catalog record to repository resource/dataset metadata""" raise NotImplementedError @staticmethod diff --git a/api/adapters/utils.py b/api/adapters/utils.py index 22473df..bddd954 100644 --- a/api/adapters/utils.py +++ b/api/adapters/utils.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Type, Union +from typing import Type, Union, Dict from api.adapters.base import AbstractRepositoryMetadataAdapter @@ -9,7 +9,7 @@ class RepositoryType(str, Enum): S3 = 'S3' -_adapter_registry = {} +_adapter_registry: Dict[RepositoryType, Type[AbstractRepositoryMetadataAdapter]] = {} def register_adapter(repository_type: RepositoryType, adapter_class: Type[AbstractRepositoryMetadataAdapter]) -> None: @@ -21,3 +21,4 @@ def get_adapter_by_type(repository_type: RepositoryType) -> Union[AbstractReposi if adapter_cls: return adapter_cls() return None + diff --git a/api/main.py b/api/main.py index 1bbef28..1cac2ad 100644 --- a/api/main.py +++ b/api/main.py @@ -14,7 +14,13 @@ from starlette.responses import PlainTextResponse from api.config import get_settings -from api.models.catalog import DatasetMetadataDOC +from api.models.catalog import ( + CoreMetadataDOC, + HSResourceMetadataDOC, + GenericDatasetMetadataDOC, + NetCDFMetadataDOC, + RasterMetadataDOC, +) from api.models.user import Submission, User from api.routes.catalog import router as catalog_router from api.routes.discovery import router as discovery_router @@ -48,7 +54,11 @@ async def startup_db_client(): settings = get_settings() app.mongodb_client = AsyncIOMotorClient(settings.db_connection_string) app.mongodb = app.mongodb_client[settings.database_name] - await init_beanie(database=app.mongodb, document_models=[DatasetMetadataDOC, User, Submission]) + await init_beanie( + database=app.mongodb, + document_models=[CoreMetadataDOC, HSResourceMetadataDOC, GenericDatasetMetadataDOC, NetCDFMetadataDOC, + RasterMetadataDOC, User, Submission] + ) @app.on_event("shutdown") diff --git a/api/models/catalog.py b/api/models/catalog.py index 1253089..8d4a5f6 100644 --- a/api/models/catalog.py +++ b/api/models/catalog.py @@ -1,16 +1,23 @@ import datetime -from typing import Optional +from typing import TypeVar, Optional from beanie import Document from api.models.user import Submission, S3Path -from .schema import CoreMetadata, DatasetMetadata +from .schema import ( + CoreMetadata, + GenericDatasetMetadata, + HSResourceMetadata, + HSNetCDFMetadata, + HSRasterMetadata, +) class CoreMetadataDOC(Document, CoreMetadata): - # this field is not stored in the database, but is populated from the corresponding submission record - # using the type field in the submission record + # these fields are not stored in the database, but are populated from the corresponding submission record submission_type: str = None + repository_identifier: str = None + s3_path: Optional[S3Path] = None class Settings: # name is the collection name in database (iguide) where the Metadata Record documents will be stored @@ -36,6 +43,27 @@ def as_submission(self) -> Submission: ) -class DatasetMetadataDOC(CoreMetadataDOC, DatasetMetadata): - repository_identifier: str = None - s3_path: Optional[S3Path] = None +class HSResourceMetadataDOC(CoreMetadataDOC, HSResourceMetadata): + + def as_submission(self) -> Submission: + submission = super().as_submission() + submission.repository = "HydroShare" + submission.repository_identifier = self.repository_identifier + return submission + + +class GenericDatasetMetadataDOC(CoreMetadataDOC, GenericDatasetMetadata): + pass + + +class NetCDFMetadataDOC(CoreMetadataDOC, HSNetCDFMetadata): + pass + + +class RasterMetadataDOC(CoreMetadataDOC, HSRasterMetadata): + pass + +# T is a type variable that can be used for type hinting for any schema model that inherits from CoreMetadataDOC + + +T = TypeVar("T", bound=CoreMetadataDOC) diff --git a/api/models/management/generate_schema.py b/api/models/management/generate_schema.py index e6cdb91..bad9cd1 100644 --- a/api/models/management/generate_schema.py +++ b/api/models/management/generate_schema.py @@ -3,30 +3,65 @@ import typer -from api.models.schema import DatasetMetadata - - -def main(output_name: str = "api/models/schemas/schema.json"): - schema = DatasetMetadata.schema() - json_schema = DatasetMetadata.schema_json()#indent=2) - # Have to run it a few times for the definitions to get updated before inserted into another model - while "#/definitions/" in json_schema: - for definition in schema["definitions"]: - class_definition = schema["definitions"][definition] - # replace allOf with a single definition - json_schema = json_schema.replace( - f'"allOf": [{{"$ref": "#/definitions/{definition}"}}]', - json.dumps(class_definition)[1:-1] - ) - #replace definition directly - json_schema = json_schema.replace( - f'"$ref": "#/definitions/{definition}"', - json.dumps(class_definition)[1:-1] - ) - embedded_schema = json.loads(json_schema) - current_directory = absolute_directory(output_name) - with open(current_directory, "w") as f: - f.write(json.dumps(embedded_schema, indent=2)) +from api.models.schema import ( + GenericDatasetMetadata, + HSNetCDFMetadata, + HSRasterMetadata, + HSResourceMetadata, +) + + +def main(): + def generate_schema_json(schema_model, folder_name): + base_directory = "api/models/schemas" + schema_file_path = os.path.join(base_directory, folder_name, "schema.json") + schema = schema_model.schema() + json_schema = schema_model.schema_json() + + # Have to run it a few times for the definitions to get updated before inserted into another model + while "#/definitions/" in json_schema: + for definition in schema["definitions"]: + class_definition = schema["definitions"][definition] + # replace allOf with a single definition + json_schema = json_schema.replace( + f'"allOf": [{{"$ref": "#/definitions/{definition}"}}]', + json.dumps(class_definition)[1:-1] + ) + #replace definition directly + json_schema = json_schema.replace( + f'"$ref": "#/definitions/{definition}"', + json.dumps(class_definition)[1:-1] + ) + embedded_schema = json.loads(json_schema) + current_directory = absolute_directory(schema_file_path) + with open(current_directory, "w") as f: + f.write(json.dumps(embedded_schema, indent=2)) + + schemas = get_schemas() + for schema_item in schemas: + generate_schema_json(schema_model=schema_item["model"], folder_name=schema_item["folder_name"]) + + +def get_schemas(): + schemas = [ + { + "model": GenericDatasetMetadata, + "folder_name": "generic", + }, + { + "model": HSResourceMetadata, + "folder_name": "hs_resource", + }, + { + "model": HSNetCDFMetadata, + "folder_name": "netcdf", + }, + { + "model": HSRasterMetadata, + "folder_name": "raster", + }, + ] + return schemas def absolute_directory(output_name): diff --git a/api/models/schema.py b/api/models/schema.py index 0fdb84d..73fedcc 100644 --- a/api/models/schema.py +++ b/api/models/schema.py @@ -318,7 +318,7 @@ class PropertyValueBase(SchemaBaseModel): type: str = Field( alias="@type", default="PropertyValue", - const="PropertyValue", + const=True, description="A property-value pair.", ) propertyID: Optional[str] = Field( @@ -400,7 +400,7 @@ class MediaObject(SchemaBaseModel): ) name: str = Field(description="The name of the media object (file).") sha256: Optional[str] = Field(title="SHA-256", description="The SHA-256 hash of the media object.") - isPartOf: Optional[List[MediaObjectPartOf]] = Field( + isPartOf: Optional[MediaObjectPartOf] = Field( title="Is part of", description="Link to or citation for a related metadata document that this media object is a part of", ) @@ -455,9 +455,10 @@ class CoreMetadata(SchemaBaseModel): name: str = Field(title="Name or title", description="A text string with a descriptive name or title for the resource." ) - description: str = Field(title="Description or abstract", - description="A text string containing a description/abstract for the resource." - ) + description: Optional[str] = Field( + title="Description or abstract", + description="A text string containing a description/abstract for the resource.", + ) url: HttpUrl = Field( title="URL", description="A URL for the landing page that describes the resource and where the content " @@ -466,22 +467,26 @@ class CoreMetadata(SchemaBaseModel): ) identifier: Optional[List[IdentifierStr]] = Field( title="Identifiers", - description="Any kind of identifier for the resource. Identifiers may be DOIs or unique strings " + description="Any kind of identifier for the resource/dataset. Identifiers may be DOIs or unique strings " "assigned by a repository. Multiple identifiers can be entered. Where identifiers can be " "encoded as URLs, enter URLs here." ) - creator: List[Union[Creator, Organization]] = Field(description="Person or Organization that created the resource.") - dateCreated: datetime = Field(title="Date created", description="The date on which the resource was created.") - keywords: List[str] = Field( + creator: Optional[List[Union[Creator, Organization]]] = Field( + description="Person or Organization that created the resource." + ) + dateCreated: Optional[datetime] = Field( + title="Date created", description="The date on which the resource was created." + ) + keywords: Optional[List[str]] = Field( min_items=1, description="Keywords or tags used to describe the dataset, delimited by commas." ) - license: License = Field( + license: Optional[License] = Field( description="A license document that applies to the resource." ) - provider: Union[Organization, Provider] = Field( + provider: Optional[Union[Organization, Provider]] = Field( description="The repository, service provider, organization, person, or service performer that provides" - " access to the resource." + " access to the resource." ) publisher: Optional[PublisherOrganization] = Field( title="Publisher", @@ -517,17 +522,6 @@ class CoreMetadata(SchemaBaseModel): description="A Grant or monetary assistance that directly or indirectly provided funding or sponsorship " "for creation of the resource.", ) - temporalCoverage: Optional[TemporalCoverage] = Field( - title="Temporal coverage", - description="The time period that applies to all of the content within the resource.", - ) - spatialCoverage: Optional[Place] = Field( - description="The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. " - "It is a sub property of contentLocation intended primarily for more technical and " - "detailed materials. For example with a Dataset, it indicates areas that the dataset " - "describes: a dataset of New York weather would have spatialCoverage which was the " - "place: the state of New York.", - ) hasPart: Optional[List[HasPart]] = Field( title="Has part", description="Link to or citation for a related resource that is part of this resource." @@ -542,18 +536,157 @@ class CoreMetadata(SchemaBaseModel): description="A media object that encodes this CreativeWork. This property is a synonym for encoding.", ) citation: Optional[List[str]] = Field(title="Citation", description="A bibliographic citation for the resource.") + additionalProperty: Optional[List[PropertyValue]] = Field( + title="Additional properties", + default=[], + description="Additional properties of the dataset/resource." + ) -class DatasetMetadata(CoreMetadata): +class HSResourceMetadata(CoreMetadata): + # modeled after hydroshare resource metadata + # used in cataloging a HydroShare resource + description: str = Field( + title="Description or abstract", + description="A text string containing a description/abstract for the resource.", + ) + url: HttpUrl = Field( + title="URL", + description="A URL for the landing page that describes the resource and where the content " + "of the resource can be accessed. If there is no landing page," + " provide the URL of the content." + ) + identifier: List[IdentifierStr] = Field( + title="Identifiers", + description="Any kind of identifier for the resource. Identifiers may be DOIs or unique strings " + "assigned by a repository. Multiple identifiers can be entered. Where identifiers can be " + "encoded as URLs, enter URLs here." + ) + creator: List[Union[Creator, Organization]] = Field(description="Person or Organization that created the resource.") + dateCreated: datetime = Field( + title="Date created", description="The date on which the resource was created." + ) + keywords: List[str] = Field( + min_items=1, + description="Keywords or tags used to describe the dataset, delimited by commas.", + ) + license: License = Field( + description="A license document that applies to the resource." + ) + provider: Union[Organization, Provider] = Field( + description="The repository, service provider, organization, person, or service performer that provides" + " access to the resource." + ) + inLanguage: Union[LanguageEnum, InLanguageStr] = Field( + title="Language", + description="The language of the content of the resource." + ) + dateModified: datetime = Field( + title="Date modified", + description="The date on which the resource was most recently modified or updated." + ) + temporalCoverage: Optional[TemporalCoverage] = Field( + title="Temporal coverage", + description="The time period that applies to all of the content within the resource.", + ) + spatialCoverage: Optional[Place] = Field( + description="The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. " + "It is a sub property of contentLocation intended primarily for more technical and " + "detailed materials. For example with a Dataset, it indicates areas that the dataset " + "describes: a dataset of New York weather would have spatialCoverage which was the " + "place: the state of New York.", + ) + + +class GenericDatasetMetadata(CoreMetadata): + # A generic dataset schema - primarily used for manual metadata entry for cataloging a dataset + # It should not be used for fetched/extracted metadata from external sources - subtypes specifically defined + # should be used for that + type: str = Field( + alias="@type", + title="Submission type", + default="Dataset", + const=True, + description="Type of submission", + ) + additionalType: Optional[str] = Field( + title="Additional type of submission", + description="An additional type for the dataset." + ) + temporalCoverage: Optional[TemporalCoverage] = Field( + title="Temporal coverage", + description="The time period that applies to the dataset.", + ) + spatialCoverage: Optional[Place] = Field( + description="The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. " + "It is a sub property of contentLocation intended primarily for more technical and " + "detailed materials. For example with a Dataset, it indicates areas that the dataset " + "describes: a dataset of New York weather would have spatialCoverage which was the " + "place: the state of New York.", + ) variableMeasured: Optional[List[Union[str, PropertyValue]]] = Field( title="Variables measured", description="Measured variables." ) - additionalProperty: Optional[List[PropertyValue]] = Field( - title="Additional properties", - default=[], - description="Additional properties of the dataset." + associatedMedia: List[MediaObject] = Field( + title="Dataset content", + description="A media object that encodes this CreativeWork. This property is a synonym for encoding.", ) sourceOrganization: Optional[SourceOrganization] = Field( title="Source organization", description="The organization that provided the data for this dataset." ) + + +class HSNetCDFMetadata(GenericDatasetMetadata): + # modeled after hydroshare netcdf aggregation metadata + # used for registering a netcdf dataset in the catalog - netcdf metadata is saved as a catalog record + + additionalType: str = Field( + title="Specific additional type of submission", + default="NetCDF", + const=True, + description="Specific additional type of submission", + ) + temporalCoverage: TemporalCoverage = Field( + title="Temporal coverage", + description="The time period that applies to the dataset.", + readOnly=True, + ) + spatialCoverage: Place = Field( + description="The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. " + "It is a sub property of contentLocation intended primarily for more technical and " + "detailed materials. For example with a Dataset, it indicates areas that the dataset " + "describes: a dataset of New York weather would have spatialCoverage which was the " + "place: the state of New York.", + readOnly=True, + ) + variableMeasured: List[Union[str, PropertyValue]] = Field( + title="Variables measured", + description="Measured variables.", + readOnly=True, + ) + + +class HSRasterMetadata(GenericDatasetMetadata): + # modeled after hydroshare raster aggregation metadata + # used for registering a raster dataset in the catalog - raster metadata is saved as a catalog record + + additionalType: str = Field( + title="Specific additional type of submission", + default="Geo Raster", + const=True, + description="Specific additional type of submission", + ) + temporalCoverage: Optional[TemporalCoverage] = Field( + title="Temporal coverage", + description="The time period that applies to the dataset.", + readOnly=True, + ) + spatialCoverage: Place = Field( + description="The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. " + "It is a sub property of contentLocation intended primarily for more technical and " + "detailed materials. For example with a Dataset, it indicates areas that the dataset " + "describes: a dataset of New York weather would have spatialCoverage which was the " + "place: the state of New York.", + readOnly=True, + ) diff --git a/api/models/schemas/generic/__init__.py b/api/models/schemas/generic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/models/schemas/generic/schema.json b/api/models/schemas/generic/schema.json new file mode 100644 index 0000000..48d1b62 --- /dev/null +++ b/api/models/schemas/generic/schema.json @@ -0,0 +1,2818 @@ +{ + "title": "GenericDatasetMetadata", + "type": "object", + "properties": { + "@context": { + "title": "@Context", + "description": "Specifies the vocabulary employed for understanding the structured data markup.", + "default": "https://schema.org", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "@type": { + "title": "Submission type", + "description": "Type of submission", + "default": "Dataset", + "const": "Dataset", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string with a descriptive name or title for the resource.", + "type": "string" + }, + "description": { + "title": "Description or abstract", + "description": "A text string containing a description/abstract for the resource.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for the landing page that describes the resource and where the content of the resource can be accessed. If there is no landing page, provide the URL of the content.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "identifier": { + "title": "Identifiers", + "description": "Any kind of identifier for the resource/dataset. Identifiers may be DOIs or unique strings assigned by a repository. Multiple identifiers can be entered. Where identifiers can be encoded as URLs, enter URLs here.", + "type": "array", + "items": { + "type": "string", + "title": "Identifier" + } + }, + "creator": { + "title": "Creator", + "description": "Person or Organization that created the resource.", + "type": "array", + "items": { + "anyOf": [ + { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "dateCreated": { + "title": "Date created", + "description": "The date on which the resource was created.", + "type": "string", + "format": "date-time" + }, + "keywords": { + "title": "Keywords", + "description": "Keywords or tags used to describe the dataset, delimited by commas.", + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "title": "License", + "description": "A license document that applies to the resource.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "provider": { + "title": "Provider", + "description": "The repository, service provider, organization, person, or service performer that provides access to the resource.", + "anyOf": [ + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + ] + }, + "publisher": { + "title": "PublisherOrganization", + "description": "Where the resource is permanently published, indicated the repository, service provider, or organization that published the resource - e.g., CUAHSI HydroShare. This may be the same as Provider.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "datePublished": { + "title": "Date published", + "description": "Date of first publication for the resource.", + "type": "string", + "format": "date-time" + }, + "subjectOf": { + "title": "Subject of", + "description": "Link to or citation for a related resource that is about or describes this resource - e.g., a journal paper that describes this resource or a related metadata document describing the resource.", + "type": "array", + "items": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "version": { + "title": "Version", + "description": "A text string indicating the version of the resource.", + "type": "string" + }, + "inLanguage": { + "title": "Language", + "description": "The language of the content of the resource.", + "anyOf": [ + { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + { + "type": "string", + "title": "Other", + "description": "Please specify another language." + } + ] + }, + "creativeWorkStatus": { + "title": "Resource status", + "description": "The status of this resource in terms of its stage in a lifecycle. Example terms include Incomplete, Draft, Published, and Obsolete.", + "anyOf": [ + { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + } + ] + }, + "dateModified": { + "title": "Date modified", + "description": "The date on which the resource was most recently modified or updated.", + "type": "string", + "format": "date-time" + }, + "funding": { + "title": "Funding", + "description": "A Grant or monetary assistance that directly or indirectly provided funding or sponsorship for creation of the resource.", + "type": "array", + "items": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + }, + "hasPart": { + "title": "Has part", + "description": "Link to or citation for a related resource that is part of this resource.", + "type": "array", + "items": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "isPartOf": { + "title": "Is part of", + "description": "Link to or citation for a related resource that this resource is a part of - e.g., a related collection.", + "type": "array", + "items": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "associatedMedia": { + "title": "Dataset content", + "description": "A media object that encodes this CreativeWork. This property is a synonym for encoding.", + "type": "array", + "items": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + } + }, + "citation": { + "title": "Citation", + "description": "A bibliographic citation for the resource.", + "type": "array", + "items": { + "type": "string" + } + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the dataset/resource.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + }, + "additionalType": { + "title": "Additional type of submission", + "description": "An additional type for the dataset.", + "type": "string" + }, + "temporalCoverage": { + "title": "TemporalCoverage", + "description": "The time period that applies to the dataset.", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "spatialCoverage": { + "title": "Place", + "description": "The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. It is a sub property of contentLocation intended primarily for more technical and detailed materials. For example with a Dataset, it indicates areas that the dataset describes: a dataset of New York weather would have spatialCoverage which was the place: the state of New York.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "variableMeasured": { + "title": "Variables measured", + "description": "Measured variables.", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + ] + } + }, + "sourceOrganization": { + "title": "SourceOrganization", + "description": "The organization that provided the data for this dataset.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization that created the data.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name", + "url", + "associatedMedia" + ], + "definitions": { + "Affiliation": { + "title": "Affiliation", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Creator": { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "Organization": { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "License": { + "title": "License", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Provider": { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "PublisherOrganization": { + "title": "PublisherOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "SubjectOf": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "LanguageEnum": { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + "Draft": { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + "Incomplete": { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + "Obsolete": { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + "Published": { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + }, + "Grant": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "HasPart": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "IsPartOf": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObjectPartOf": { + "title": "MediaObjectPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObject": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + }, + "PropertyValueBase": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "PropertyValue": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "TemporalCoverage": { + "title": "TemporalCoverage", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "GeoCoordinates": { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + "GeoShape": { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + }, + "Place": { + "title": "Place", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "SourceOrganization": { + "title": "SourceOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization that created the data.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + } +} \ No newline at end of file diff --git a/api/models/schemas/hs_resource/__init__.py b/api/models/schemas/hs_resource/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/models/schemas/hs_resource/schema.json b/api/models/schemas/hs_resource/schema.json new file mode 100644 index 0000000..e1b146b --- /dev/null +++ b/api/models/schemas/hs_resource/schema.json @@ -0,0 +1,2562 @@ +{ + "title": "HSResourceMetadata", + "type": "object", + "properties": { + "@context": { + "title": "@Context", + "description": "Specifies the vocabulary employed for understanding the structured data markup.", + "default": "https://schema.org", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "@type": { + "title": "Submission type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "Dataset", + "enum": [ + "Dataset", + "Notebook", + "Software Source Code" + ], + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string with a descriptive name or title for the resource.", + "type": "string" + }, + "description": { + "title": "Description or abstract", + "description": "A text string containing a description/abstract for the resource.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for the landing page that describes the resource and where the content of the resource can be accessed. If there is no landing page, provide the URL of the content.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "identifier": { + "title": "Identifiers", + "description": "Any kind of identifier for the resource. Identifiers may be DOIs or unique strings assigned by a repository. Multiple identifiers can be entered. Where identifiers can be encoded as URLs, enter URLs here.", + "type": "array", + "items": { + "type": "string", + "title": "Identifier" + } + }, + "creator": { + "title": "Creator", + "description": "Person or Organization that created the resource.", + "type": "array", + "items": { + "anyOf": [ + { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "dateCreated": { + "title": "Date created", + "description": "The date on which the resource was created.", + "type": "string", + "format": "date-time" + }, + "keywords": { + "title": "Keywords", + "description": "Keywords or tags used to describe the dataset, delimited by commas.", + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "title": "License", + "description": "A license document that applies to the resource.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "provider": { + "title": "Provider", + "description": "The repository, service provider, organization, person, or service performer that provides access to the resource.", + "anyOf": [ + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + ] + }, + "publisher": { + "title": "PublisherOrganization", + "description": "Where the resource is permanently published, indicated the repository, service provider, or organization that published the resource - e.g., CUAHSI HydroShare. This may be the same as Provider.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "datePublished": { + "title": "Date published", + "description": "Date of first publication for the resource.", + "type": "string", + "format": "date-time" + }, + "subjectOf": { + "title": "Subject of", + "description": "Link to or citation for a related resource that is about or describes this resource - e.g., a journal paper that describes this resource or a related metadata document describing the resource.", + "type": "array", + "items": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "version": { + "title": "Version", + "description": "A text string indicating the version of the resource.", + "type": "string" + }, + "inLanguage": { + "title": "Language", + "description": "The language of the content of the resource.", + "anyOf": [ + { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + { + "type": "string", + "title": "Other", + "description": "Please specify another language." + } + ] + }, + "creativeWorkStatus": { + "title": "Resource status", + "description": "The status of this resource in terms of its stage in a lifecycle. Example terms include Incomplete, Draft, Published, and Obsolete.", + "anyOf": [ + { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + } + ] + }, + "dateModified": { + "title": "Date modified", + "description": "The date on which the resource was most recently modified or updated.", + "type": "string", + "format": "date-time" + }, + "funding": { + "title": "Funding", + "description": "A Grant or monetary assistance that directly or indirectly provided funding or sponsorship for creation of the resource.", + "type": "array", + "items": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + }, + "hasPart": { + "title": "Has part", + "description": "Link to or citation for a related resource that is part of this resource.", + "type": "array", + "items": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "isPartOf": { + "title": "Is part of", + "description": "Link to or citation for a related resource that this resource is a part of - e.g., a related collection.", + "type": "array", + "items": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "associatedMedia": { + "title": "Resource content", + "description": "A media object that encodes this CreativeWork. This property is a synonym for encoding.", + "type": "array", + "items": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + } + }, + "citation": { + "title": "Citation", + "description": "A bibliographic citation for the resource.", + "type": "array", + "items": { + "type": "string" + } + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the dataset/resource.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + }, + "temporalCoverage": { + "title": "TemporalCoverage", + "description": "The time period that applies to all of the content within the resource.", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "spatialCoverage": { + "title": "Place", + "description": "The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. It is a sub property of contentLocation intended primarily for more technical and detailed materials. For example with a Dataset, it indicates areas that the dataset describes: a dataset of New York weather would have spatialCoverage which was the place: the state of New York.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + } + }, + "required": [ + "name", + "description", + "url", + "identifier", + "creator", + "dateCreated", + "keywords", + "license", + "provider", + "inLanguage", + "dateModified" + ], + "definitions": { + "Affiliation": { + "title": "Affiliation", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Creator": { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "Organization": { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "License": { + "title": "License", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Provider": { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "PublisherOrganization": { + "title": "PublisherOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "SubjectOf": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "LanguageEnum": { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + "Draft": { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + "Incomplete": { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + "Obsolete": { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + "Published": { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + }, + "Grant": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "HasPart": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "IsPartOf": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObjectPartOf": { + "title": "MediaObjectPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObject": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + }, + "PropertyValueBase": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "PropertyValue": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "TemporalCoverage": { + "title": "TemporalCoverage", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "GeoCoordinates": { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + "GeoShape": { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + }, + "Place": { + "title": "Place", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/api/models/schemas/netcdf/__init__.py b/api/models/schemas/netcdf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/models/schemas/netcdf/schema.json b/api/models/schemas/netcdf/schema.json new file mode 100644 index 0000000..c7b9032 --- /dev/null +++ b/api/models/schemas/netcdf/schema.json @@ -0,0 +1,2826 @@ +{ + "title": "HSNetCDFMetadata", + "type": "object", + "properties": { + "@context": { + "title": "@Context", + "description": "Specifies the vocabulary employed for understanding the structured data markup.", + "default": "https://schema.org", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "@type": { + "title": "Submission type", + "description": "Type of submission", + "default": "Dataset", + "const": "Dataset", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string with a descriptive name or title for the resource.", + "type": "string" + }, + "description": { + "title": "Description or abstract", + "description": "A text string containing a description/abstract for the resource.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for the landing page that describes the resource and where the content of the resource can be accessed. If there is no landing page, provide the URL of the content.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "identifier": { + "title": "Identifiers", + "description": "Any kind of identifier for the resource/dataset. Identifiers may be DOIs or unique strings assigned by a repository. Multiple identifiers can be entered. Where identifiers can be encoded as URLs, enter URLs here.", + "type": "array", + "items": { + "type": "string", + "title": "Identifier" + } + }, + "creator": { + "title": "Creator", + "description": "Person or Organization that created the resource.", + "type": "array", + "items": { + "anyOf": [ + { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "dateCreated": { + "title": "Date created", + "description": "The date on which the resource was created.", + "type": "string", + "format": "date-time" + }, + "keywords": { + "title": "Keywords", + "description": "Keywords or tags used to describe the dataset, delimited by commas.", + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "title": "License", + "description": "A license document that applies to the resource.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "provider": { + "title": "Provider", + "description": "The repository, service provider, organization, person, or service performer that provides access to the resource.", + "anyOf": [ + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + ] + }, + "publisher": { + "title": "PublisherOrganization", + "description": "Where the resource is permanently published, indicated the repository, service provider, or organization that published the resource - e.g., CUAHSI HydroShare. This may be the same as Provider.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "datePublished": { + "title": "Date published", + "description": "Date of first publication for the resource.", + "type": "string", + "format": "date-time" + }, + "subjectOf": { + "title": "Subject of", + "description": "Link to or citation for a related resource that is about or describes this resource - e.g., a journal paper that describes this resource or a related metadata document describing the resource.", + "type": "array", + "items": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "version": { + "title": "Version", + "description": "A text string indicating the version of the resource.", + "type": "string" + }, + "inLanguage": { + "title": "Language", + "description": "The language of the content of the resource.", + "anyOf": [ + { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + { + "type": "string", + "title": "Other", + "description": "Please specify another language." + } + ] + }, + "creativeWorkStatus": { + "title": "Resource status", + "description": "The status of this resource in terms of its stage in a lifecycle. Example terms include Incomplete, Draft, Published, and Obsolete.", + "anyOf": [ + { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + } + ] + }, + "dateModified": { + "title": "Date modified", + "description": "The date on which the resource was most recently modified or updated.", + "type": "string", + "format": "date-time" + }, + "funding": { + "title": "Funding", + "description": "A Grant or monetary assistance that directly or indirectly provided funding or sponsorship for creation of the resource.", + "type": "array", + "items": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + }, + "hasPart": { + "title": "Has part", + "description": "Link to or citation for a related resource that is part of this resource.", + "type": "array", + "items": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "isPartOf": { + "title": "Is part of", + "description": "Link to or citation for a related resource that this resource is a part of - e.g., a related collection.", + "type": "array", + "items": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "associatedMedia": { + "title": "Dataset content", + "description": "A media object that encodes this CreativeWork. This property is a synonym for encoding.", + "type": "array", + "items": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + } + }, + "citation": { + "title": "Citation", + "description": "A bibliographic citation for the resource.", + "type": "array", + "items": { + "type": "string" + } + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the dataset/resource.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + }, + "additionalType": { + "title": "Specific additional type of submission", + "description": "Specific additional type of submission", + "default": "NetCDF", + "const": "NetCDF", + "type": "string" + }, + "temporalCoverage": { + "title": "TemporalCoverage", + "description": "The time period that applies to the dataset.", + "readOnly": true, + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "spatialCoverage": { + "title": "Place", + "description": "The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. It is a sub property of contentLocation intended primarily for more technical and detailed materials. For example with a Dataset, it indicates areas that the dataset describes: a dataset of New York weather would have spatialCoverage which was the place: the state of New York.", + "readOnly": true, + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "variableMeasured": { + "title": "Variables measured", + "description": "Measured variables.", + "readOnly": true, + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + ] + } + }, + "sourceOrganization": { + "title": "SourceOrganization", + "description": "The organization that provided the data for this dataset.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization that created the data.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name", + "url", + "associatedMedia", + "temporalCoverage", + "spatialCoverage", + "variableMeasured" + ], + "definitions": { + "Affiliation": { + "title": "Affiliation", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Creator": { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "Organization": { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "License": { + "title": "License", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Provider": { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "PublisherOrganization": { + "title": "PublisherOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "SubjectOf": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "LanguageEnum": { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + "Draft": { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + "Incomplete": { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + "Obsolete": { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + "Published": { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + }, + "Grant": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "HasPart": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "IsPartOf": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObjectPartOf": { + "title": "MediaObjectPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObject": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + }, + "PropertyValueBase": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "PropertyValue": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "TemporalCoverage": { + "title": "TemporalCoverage", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "GeoCoordinates": { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + "GeoShape": { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + }, + "Place": { + "title": "Place", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "SourceOrganization": { + "title": "SourceOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization that created the data.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + } +} \ No newline at end of file diff --git a/api/models/schemas/raster/__init__.py b/api/models/schemas/raster/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/models/schemas/raster/schema.json b/api/models/schemas/raster/schema.json new file mode 100644 index 0000000..78c7183 --- /dev/null +++ b/api/models/schemas/raster/schema.json @@ -0,0 +1,2823 @@ +{ + "title": "HSRasterMetadata", + "type": "object", + "properties": { + "@context": { + "title": "@Context", + "description": "Specifies the vocabulary employed for understanding the structured data markup.", + "default": "https://schema.org", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "@type": { + "title": "Submission type", + "description": "Type of submission", + "default": "Dataset", + "const": "Dataset", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string with a descriptive name or title for the resource.", + "type": "string" + }, + "description": { + "title": "Description or abstract", + "description": "A text string containing a description/abstract for the resource.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for the landing page that describes the resource and where the content of the resource can be accessed. If there is no landing page, provide the URL of the content.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "identifier": { + "title": "Identifiers", + "description": "Any kind of identifier for the resource/dataset. Identifiers may be DOIs or unique strings assigned by a repository. Multiple identifiers can be entered. Where identifiers can be encoded as URLs, enter URLs here.", + "type": "array", + "items": { + "type": "string", + "title": "Identifier" + } + }, + "creator": { + "title": "Creator", + "description": "Person or Organization that created the resource.", + "type": "array", + "items": { + "anyOf": [ + { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "dateCreated": { + "title": "Date created", + "description": "The date on which the resource was created.", + "type": "string", + "format": "date-time" + }, + "keywords": { + "title": "Keywords", + "description": "Keywords or tags used to describe the dataset, delimited by commas.", + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "title": "License", + "description": "A license document that applies to the resource.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "provider": { + "title": "Provider", + "description": "The repository, service provider, organization, person, or service performer that provides access to the resource.", + "anyOf": [ + { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + ] + }, + "publisher": { + "title": "PublisherOrganization", + "description": "Where the resource is permanently published, indicated the repository, service provider, or organization that published the resource - e.g., CUAHSI HydroShare. This may be the same as Provider.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "datePublished": { + "title": "Date published", + "description": "Date of first publication for the resource.", + "type": "string", + "format": "date-time" + }, + "subjectOf": { + "title": "Subject of", + "description": "Link to or citation for a related resource that is about or describes this resource - e.g., a journal paper that describes this resource or a related metadata document describing the resource.", + "type": "array", + "items": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "version": { + "title": "Version", + "description": "A text string indicating the version of the resource.", + "type": "string" + }, + "inLanguage": { + "title": "Language", + "description": "The language of the content of the resource.", + "anyOf": [ + { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + { + "type": "string", + "title": "Other", + "description": "Please specify another language." + } + ] + }, + "creativeWorkStatus": { + "title": "Resource status", + "description": "The status of this resource in terms of its stage in a lifecycle. Example terms include Incomplete, Draft, Published, and Obsolete.", + "anyOf": [ + { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + } + ] + }, + "dateModified": { + "title": "Date modified", + "description": "The date on which the resource was most recently modified or updated.", + "type": "string", + "format": "date-time" + }, + "funding": { + "title": "Funding", + "description": "A Grant or monetary assistance that directly or indirectly provided funding or sponsorship for creation of the resource.", + "type": "array", + "items": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + } + }, + "hasPart": { + "title": "Has part", + "description": "Link to or citation for a related resource that is part of this resource.", + "type": "array", + "items": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "isPartOf": { + "title": "Is part of", + "description": "Link to or citation for a related resource that this resource is a part of - e.g., a related collection.", + "type": "array", + "items": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "associatedMedia": { + "title": "Dataset content", + "description": "A media object that encodes this CreativeWork. This property is a synonym for encoding.", + "type": "array", + "items": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + } + }, + "citation": { + "title": "Citation", + "description": "A bibliographic citation for the resource.", + "type": "array", + "items": { + "type": "string" + } + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the dataset/resource.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + }, + "additionalType": { + "title": "Specific additional type of submission", + "description": "Specific additional type of submission", + "default": "Geo Raster", + "const": "Geo Raster", + "type": "string" + }, + "temporalCoverage": { + "title": "TemporalCoverage", + "description": "The time period that applies to the dataset.", + "readOnly": true, + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "spatialCoverage": { + "title": "Place", + "description": "The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. It is a sub property of contentLocation intended primarily for more technical and detailed materials. For example with a Dataset, it indicates areas that the dataset describes: a dataset of New York weather would have spatialCoverage which was the place: the state of New York.", + "readOnly": true, + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "variableMeasured": { + "title": "Variables measured", + "description": "Measured variables.", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + ] + } + }, + "sourceOrganization": { + "title": "SourceOrganization", + "description": "The organization that provided the data for this dataset.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization that created the data.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name", + "url", + "associatedMedia", + "spatialCoverage" + ], + "definitions": { + "Affiliation": { + "title": "Affiliation", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Creator": { + "title": "Creator", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the creator.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for creator.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "Organization": { + "title": "Organization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the provider organization or repository.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "License": { + "title": "License", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A text string indicating the name of the license under which the resource is shared.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL for a web page that describes the license.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "A text string describing the license or containing the text of the license itself.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "Provider": { + "title": "Provider", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A person.", + "default": "Person", + "const": "Person", + "type": "string" + }, + "name": { + "title": "Name", + "description": "A string containing the full name of the person. Personal name format: Family Name, Given Name.", + "type": "string" + }, + "email": { + "title": "Email", + "description": "A string containing an email address for the provider.", + "type": "string", + "format": "email" + }, + "identifier": { + "title": "Identifier", + "description": "ORCID identifier for the person.", + "pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\\b", + "options": { + "placeholder": "e.g. '0000-0001-2345-6789'" + }, + "errorMessage": { + "pattern": "must match the ORCID pattern. e.g. '0000-0001-2345-6789'" + }, + "type": "string" + }, + "affiliation": { + "title": "Affiliation", + "description": "The affiliation of the creator with the organization.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization the creator is affiliated with.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "PublisherOrganization": { + "title": "PublisherOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the publishing organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the publisher organization or repository.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "SubjectOf": { + "title": "SubjectOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address that serves as a reference to access additional details related to the record. It is important to note that this type of metadata solely pertains to the record itself and may not necessarily be an integral component of the record, unlike the HasPart metadata.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is about or describes this resource - e.g., a related metadata document describing the resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "LanguageEnum": { + "title": "Language", + "description": "", + "enum": [ + "eng", + "esp" + ], + "type": "string" + }, + "Draft": { + "title": "Draft", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Draft", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource is in draft state and should not be considered final. Content and metadata may change", + "readOnly": true, + "type": "string" + } + } + }, + "Incomplete": { + "title": "Incomplete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Incomplete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "Data collection is ongoing or the resource is not completed", + "readOnly": true, + "type": "string" + } + } + }, + "Obsolete": { + "title": "Obsolete", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Obsolete", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been replaced by a newer version, or the resource is no longer considered applicable", + "readOnly": true, + "type": "string" + } + } + }, + "Published": { + "title": "Published", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "DefinedTerm", + "type": "string" + }, + "name": { + "title": "Name", + "default": "Published", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the item being defined.", + "default": "The resource has been permanently published and should be considered final and complete", + "readOnly": true, + "type": "string" + } + } + }, + "Grant": { + "title": "Grant", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "This metadata represents details about a grant or financial assistance provided to an individual(s) or organization(s) for supporting the work related to the record.", + "default": "MonetaryGrant", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "A text string indicating the name or title of the grant or financial assistance.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A text string describing the grant or financial assistance.", + "type": "string" + }, + "identifier": { + "title": "Funding identifier", + "description": "Grant award number or other identifier.", + "type": "string" + }, + "funder": { + "title": "Funding Organization", + "description": "The organization that provided the funding or sponsorship.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "name" + ] + }, + "HasPart": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "IsPartOf": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObjectPartOf": { + "title": "MediaObjectPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "MediaObject": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + }, + "PropertyValueBase": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "PropertyValue": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "TemporalCoverage": { + "title": "TemporalCoverage", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "formatMaximum": { + "$data": "1/endDate" + }, + "errorMessage": { + "formatMaximum": "must be lesser than or equal to End date" + }, + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "formatMinimum": { + "$data": "1/startDate" + }, + "errorMessage": { + "formatMinimum": "must be greater than or equal to Start date" + }, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "GeoCoordinates": { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + "GeoShape": { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + }, + "Place": { + "title": "Place", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "SourceOrganization": { + "title": "SourceOrganization", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "default": "Organization", + "const": "Organization", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the organization that created the data.", + "type": "string" + }, + "url": { + "title": "URL", + "description": "A URL to the homepage for the organization.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "address": { + "title": "Address", + "description": "Full address for the organization - e.g., \u201c8200 Old Main Hill, Logan, UT 84322-8200\u201d.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + } +} \ No newline at end of file diff --git a/api/models/schemas/schema.json b/api/models/schemas/schema.json index 06550c8..40874f0 100644 --- a/api/models/schemas/schema.json +++ b/api/models/schemas/schema.json @@ -1,5 +1,5 @@ { - "title": "DatasetMetadata", + "title": "GenericDatasetMetadata", "type": "object", "properties": { "@context": { @@ -16,13 +16,9 @@ }, "@type": { "title": "Submission type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "description": "Type of submission", "default": "Dataset", - "enum": [ - "Dataset", - "Notebook", - "Software Source Code" - ], + "const": "Dataset", "type": "string" }, "name": { @@ -48,7 +44,7 @@ }, "identifier": { "title": "Identifiers", - "description": "Any kind of identifier for the resource. Identifiers may be DOIs or unique strings assigned by a repository. Multiple identifiers can be entered. Where identifiers can be encoded as URLs, enter URLs here.", + "description": "Any kind of identifier for the resource/dataset. Identifiers may be DOIs or unique strings assigned by a repository. Multiple identifiers can be entered. Where identifiers can be encoded as URLs, enter URLs here.", "type": "array", "items": { "type": "string", @@ -631,157 +627,514 @@ ] } }, - "temporalCoverage": { - "title": "TemporalCoverage", - "description": "The time period that applies to all of the content within the resource.", - "type": "object", - "properties": { - "startDate": { - "title": "Start date", - "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", - "formatMaximum": { - "$data": "1/endDate" + "hasPart": { + "title": "Has part", + "description": "Link to or citation for a related resource that is part of this resource.", + "type": "array", + "items": { + "title": "HasPart", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" }, - "errorMessage": { - "formatMaximum": "must be lesser than or equal to End date" + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" }, - "type": "string", - "format": "date-time" + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } }, - "endDate": { - "title": "End date", - "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", - "formatMinimum": { - "$data": "1/startDate" + "required": [ + "name" + ] + } + }, + "isPartOf": { + "title": "Is part of", + "description": "Link to or citation for a related resource that this resource is a part of - e.g., a related collection.", + "type": "array", + "items": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" }, - "errorMessage": { - "formatMinimum": "must be greater than or equal to Start date" + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" }, - "type": "string", - "format": "date-time" - } - }, - "required": [ - "startDate" - ] - }, - "spatialCoverage": { - "title": "Place", - "description": "The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. It is a sub property of contentLocation intended primarily for more technical and detailed materials. For example with a Dataset, it indicates areas that the dataset describes: a dataset of New York weather would have spatialCoverage which was the place: the state of New York.", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Represents the focus area of the record's content.", - "default": "Place", - "type": "string" - }, - "name": { - "title": "Name", - "description": "Name of the place.", - "type": "string" - }, - "geo": { - "title": "Geo", - "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", - "anyOf": [ - { - "title": "GeoCoordinates", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", - "default": "GeoCoordinates", - "type": "string" - }, - "latitude": { - "title": "Latitude", - "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", - "type": "number" - }, - "longitude": { - "title": "Longitude", - "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", - "type": "number" - } - }, - "required": [ - "latitude", - "longitude" - ] - }, - { - "title": "GeoShape", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "A structured representation that describes the coordinates of a geographic feature.", - "default": "GeoShape", - "type": "string" - }, - "box": { - "title": "Box", - "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", - "type": "string" - } - }, - "required": [ - "box" - ] + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" } - ] + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" + } }, - "additionalProperty": { - "title": "Additional properties", - "description": "Additional properties of the place.", - "default": [], - "type": "array", - "items": { - "title": "PropertyValue", + "required": [ + "name" + ] + } + }, + "associatedMedia": { + "title": "Dataset content", + "description": "A media object that encodes this CreativeWork. This property is a synonym for encoding.", + "type": "array", + "items": { + "title": "MediaObject", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "An item that encodes the record.", + "default": "MediaObject", + "type": "string" + }, + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", + "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", "type": "object", "properties": { "@type": { "title": "@Type", - "description": "A property-value pair.", - "default": "PropertyValue", - "const": "PropertyValue", - "type": "string" - }, - "propertyID": { - "title": "Property ID", - "description": "The ID of the property.", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", "type": "string" }, "name": { - "title": "Name", - "description": "The name of the property.", + "title": "Name or title", + "description": "Submission's name or title", "type": "string" }, - "value": { - "title": "Value", - "description": "The value of the property.", - "anyOf": [ - { - "type": "string" - }, - { - "title": "PropertyValue", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "A property-value pair.", - "default": "PropertyValue", - "const": "PropertyValue", - "type": "string" - }, - "propertyID": { - "title": "Property ID", - "description": "The ID of the property.", - "type": "string" - }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "contentUrl", + "contentSize", + "name" + ] + } + }, + "citation": { + "title": "Citation", + "description": "A bibliographic citation for the resource.", + "type": "array", + "items": { + "type": "string" + } + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the dataset/resource.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + { + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } + ] + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + }, + "additionalType": { + "title": "Additional type of submission", + "description": "An additional type for the dataset.", + "type": "string" + }, + "temporalCoverage": { + "title": "TemporalCoverage", + "description": "The time period that applies to the dataset.", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "spatialCoverage": { + "title": "Place", + "description": "The spatialCoverage of a CreativeWork indicates the place(s) which are the focus of the content. It is a sub property of contentLocation intended primarily for more technical and detailed materials. For example with a Dataset, it indicates areas that the dataset describes: a dataset of New York weather would have spatialCoverage which was the place: the state of New York.", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Represents the focus area of the record's content.", + "default": "Place", + "type": "string" + }, + "name": { + "title": "Name", + "description": "Name of the place.", + "type": "string" + }, + "geo": { + "title": "Geo", + "description": "Specifies the geographic coordinates of the place in the form of a point location, line, or area coverage extent.", + "anyOf": [ + { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", + "type": "string" + }, + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", + "type": "number" + }, + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", + "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" + }, + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "type": "string" + } + }, + "required": [ + "box" + ] + } + ] + }, + "additionalProperty": { + "title": "Additional properties", + "description": "Additional properties of the place.", + "default": [], + "type": "array", + "items": { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "anyOf": [ + { + "type": "string" + }, + { + "title": "PropertyValue", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A property-value pair.", + "default": "PropertyValue", + "const": "PropertyValue", + "type": "string" + }, + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, "name": { "title": "Name", "description": "The name of the property.", @@ -836,271 +1189,87 @@ "const": "PropertyValue", "type": "string" }, - "propertyID": { - "title": "Property ID", - "description": "The ID of the property.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the property.", - "type": "string" - }, - "value": { - "title": "Value", - "description": "The value of the property.", - "type": "string" - }, - "unitCode": { - "title": "Measurement unit", - "description": "The unit of measurement for the value.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the property.", - "type": "string" - }, - "minValue": { - "title": "Minimum value", - "description": "The minimum allowed value for the property.", - "type": "number" - }, - "maxValue": { - "title": "Maximum value", - "description": "The maximum allowed value for the property.", - "type": "number" - }, - "measurementTechnique": { - "title": "Measurement technique", - "description": "A technique or technology used in a measurement.", - "type": "string" - } - }, - "required": [ - "name", - "value" - ] - } - } - ] - }, - "unitCode": { - "title": "Measurement unit", - "description": "The unit of measurement for the value.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the property.", - "type": "string" - }, - "minValue": { - "title": "Minimum value", - "description": "The minimum allowed value for the property.", - "type": "number" - }, - "maxValue": { - "title": "Maximum value", - "description": "The maximum allowed value for the property.", - "type": "number" - }, - "measurementTechnique": { - "title": "Measurement technique", - "description": "A technique or technology used in a measurement.", - "type": "string" - } - }, - "required": [ - "name", - "value" - ] - } - } - } - }, - "hasPart": { - "title": "Has part", - "description": "Link to or citation for a related resource that is part of this resource.", - "type": "array", - "items": { - "title": "HasPart", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the data resource.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "description": { - "title": "Description", - "description": "Information about a related resource that is part of this resource.", - "type": "string" - } - }, - "required": [ - "name" - ] - } - }, - "isPartOf": { - "title": "Is part of", - "description": "Link to or citation for a related resource that this resource is a part of - e.g., a related collection.", - "type": "array", - "items": { - "title": "IsPartOf", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the data resource.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "description": { - "title": "Description", - "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", - "type": "string" - } - }, - "required": [ - "name" - ] - } - }, - "associatedMedia": { - "title": "Resource content", - "description": "A media object that encodes this CreativeWork. This property is a synonym for encoding.", - "type": "array", - "items": { - "title": "MediaObject", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "An item that encodes the record.", - "default": "MediaObject", - "type": "string" - }, - "contentUrl": { - "title": "Content URL", - "description": "The direct URL link to access or download the actual content of the media object.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "encodingFormat": { - "title": "Encoding format", - "description": "Represents the specific file format in which the media is encoded.", - "type": "string" - }, - "contentSize": { - "title": "Content size", - "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the media object (file).", - "type": "string" - }, - "sha256": { - "title": "SHA-256", - "description": "The SHA-256 hash of the media object.", - "type": "string" - }, - "isPartOf": { - "title": "Is part of", - "description": "Link to or citation for a related metadata document that this media object is a part of", - "type": "array", - "items": { - "title": "MediaObjectPartOf", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the related metadata document.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" + "propertyID": { + "title": "Property ID", + "description": "The ID of the property.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the property.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "The value of the property.", + "type": "string" + }, + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } } - }, - "description": { - "title": "Description", - "description": "Information about a related metadata document.", - "type": "string" - } + ] }, - "required": [ - "name" - ] - } + "unitCode": { + "title": "Measurement unit", + "description": "The unit of measurement for the value.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] } - }, - "required": [ - "contentUrl", - "contentSize", - "name" - ] - } - }, - "citation": { - "title": "Citation", - "description": "A bibliographic citation for the resource.", - "type": "array", - "items": { - "type": "string" + } } }, "variableMeasured": { @@ -1287,193 +1456,9 @@ }, "required": [ "name", - "value" - ] - } - ] - } - }, - "additionalProperty": { - "title": "Additional properties", - "description": "Additional properties of the dataset.", - "default": [], - "type": "array", - "items": { - "title": "PropertyValue", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "A property-value pair.", - "default": "PropertyValue", - "const": "PropertyValue", - "type": "string" - }, - "propertyID": { - "title": "Property ID", - "description": "The ID of the property.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the property.", - "type": "string" - }, - "value": { - "title": "Value", - "description": "The value of the property.", - "anyOf": [ - { - "type": "string" - }, - { - "title": "PropertyValue", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "A property-value pair.", - "default": "PropertyValue", - "const": "PropertyValue", - "type": "string" - }, - "propertyID": { - "title": "Property ID", - "description": "The ID of the property.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the property.", - "type": "string" - }, - "value": { - "title": "Value", - "description": "The value of the property.", - "type": "string" - }, - "unitCode": { - "title": "Measurement unit", - "description": "The unit of measurement for the value.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the property.", - "type": "string" - }, - "minValue": { - "title": "Minimum value", - "description": "The minimum allowed value for the property.", - "type": "number" - }, - "maxValue": { - "title": "Maximum value", - "description": "The maximum allowed value for the property.", - "type": "number" - }, - "measurementTechnique": { - "title": "Measurement technique", - "description": "A technique or technology used in a measurement.", - "type": "string" - } - }, - "required": [ - "name", - "value" - ] - }, - { - "type": "array", - "items": { - "title": "PropertyValue", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "A property-value pair.", - "default": "PropertyValue", - "const": "PropertyValue", - "type": "string" - }, - "propertyID": { - "title": "Property ID", - "description": "The ID of the property.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the property.", - "type": "string" - }, - "value": { - "title": "Value", - "description": "The value of the property.", - "type": "string" - }, - "unitCode": { - "title": "Measurement unit", - "description": "The unit of measurement for the value.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the property.", - "type": "string" - }, - "minValue": { - "title": "Minimum value", - "description": "The minimum allowed value for the property.", - "type": "number" - }, - "maxValue": { - "title": "Maximum value", - "description": "The maximum allowed value for the property.", - "type": "number" - }, - "measurementTechnique": { - "title": "Measurement technique", - "description": "A technique or technology used in a measurement.", - "type": "string" - } - }, - "required": [ - "name", - "value" - ] - } - } + "value" ] - }, - "unitCode": { - "title": "Measurement unit", - "description": "The unit of measurement for the value.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the property.", - "type": "string" - }, - "minValue": { - "title": "Minimum value", - "description": "The minimum allowed value for the property.", - "type": "number" - }, - "maxValue": { - "title": "Maximum value", - "description": "The maximum allowed value for the property.", - "type": "number" - }, - "measurementTechnique": { - "title": "Measurement technique", - "description": "A technique or technology used in a measurement.", - "type": "string" } - }, - "required": [ - "name", - "value" ] } }, @@ -1517,13 +1502,8 @@ }, "required": [ "name", - "description", "url", - "creator", - "dateCreated", - "keywords", - "license", - "provider" + "associatedMedia" ], "definitions": { "Affiliation": { @@ -2026,83 +2006,197 @@ "name" ] }, - "TemporalCoverage": { - "title": "TemporalCoverage", + "HasPart": { + "title": "HasPart", "type": "object", "properties": { - "startDate": { - "title": "Start date", - "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", - "formatMaximum": { - "$data": "1/endDate" - }, - "errorMessage": { - "formatMaximum": "must be lesser than or equal to End date" - }, - "type": "string", - "format": "date-time" + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" }, - "endDate": { - "title": "End date", - "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", - "formatMinimum": { - "$data": "1/startDate" - }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", "errorMessage": { - "formatMinimum": "must be greater than or equal to Start date" - }, + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that is part of this resource.", + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "IsPartOf": { + "title": "IsPartOf", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the data resource.", + "minLength": 1, + "maxLength": 2083, "type": "string", - "format": "date-time" + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", + "type": "string" } }, "required": [ - "startDate" + "name" ] }, - "GeoCoordinates": { - "title": "GeoCoordinates", + "MediaObjectPartOf": { + "title": "MediaObjectPartOf", "type": "object", "properties": { "@type": { "title": "@Type", - "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", - "default": "GeoCoordinates", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", "type": "string" }, - "latitude": { - "title": "Latitude", - "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", - "type": "number" + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" }, - "longitude": { - "title": "Longitude", - "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", - "type": "number" + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" } }, "required": [ - "latitude", - "longitude" + "name" ] }, - "GeoShape": { - "title": "GeoShape", + "MediaObject": { + "title": "MediaObject", "type": "object", "properties": { "@type": { "title": "@Type", - "description": "A structured representation that describes the coordinates of a geographic feature.", - "default": "GeoShape", + "description": "An item that encodes the record.", + "default": "MediaObject", "type": "string" }, - "box": { - "title": "Box", - "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", + "contentUrl": { + "title": "Content URL", + "description": "The direct URL link to access or download the actual content of the media object.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "encodingFormat": { + "title": "Encoding format", + "description": "Represents the specific file format in which the media is encoded.", + "type": "string" + }, + "contentSize": { + "title": "Content size", + "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the media object (file).", + "type": "string" + }, + "sha256": { + "title": "SHA-256", + "description": "The SHA-256 hash of the media object.", "type": "string" + }, + "isPartOf": { + "title": "MediaObjectPartOf", + "description": "Link to or citation for a related metadata document that this media object is a part of", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", + "default": "CreativeWork", + "type": "string" + }, + "name": { + "title": "Name or title", + "description": "Submission's name or title", + "type": "string" + }, + "url": { + "title": "URL", + "description": "The URL address to the related metadata document.", + "minLength": 1, + "maxLength": 2083, + "type": "string", + "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", + "errorMessage": { + "pattern": "must match format \"url\"" + } + }, + "description": { + "title": "Description", + "description": "Information about a related metadata document.", + "type": "string" + } + }, + "required": [ + "name" + ] } }, "required": [ - "box" + "contentUrl", + "contentSize", + "name" ] }, "PropertyValueBase": { @@ -2314,30 +2408,97 @@ "description": "The unit of measurement for the value.", "type": "string" }, - "description": { - "title": "Description", - "description": "A description of the property.", + "description": { + "title": "Description", + "description": "A description of the property.", + "type": "string" + }, + "minValue": { + "title": "Minimum value", + "description": "The minimum allowed value for the property.", + "type": "number" + }, + "maxValue": { + "title": "Maximum value", + "description": "The maximum allowed value for the property.", + "type": "number" + }, + "measurementTechnique": { + "title": "Measurement technique", + "description": "A technique or technology used in a measurement.", + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + }, + "TemporalCoverage": { + "title": "TemporalCoverage", + "type": "object", + "properties": { + "startDate": { + "title": "Start date", + "description": "A date/time object containing the instant corresponding to the commencement of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM).", + "type": "string", + "format": "date-time" + }, + "endDate": { + "title": "End date", + "description": "A date/time object containing the instant corresponding to the termination of the time interval (ISO8601 formatted date - YYYY-MM-DDTHH:MM). If the ending date is left off, that means the temporal coverage is ongoing.", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "startDate" + ] + }, + "GeoCoordinates": { + "title": "GeoCoordinates", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "Geographic coordinates that represent a specific location on the Earth's surface. GeoCoordinates typically consists of two components: latitude and longitude.", + "default": "GeoCoordinates", "type": "string" }, - "minValue": { - "title": "Minimum value", - "description": "The minimum allowed value for the property.", + "latitude": { + "title": "Latitude", + "description": "Represents the angular distance of a location north or south of the equator, measured in degrees and ranges from -90 to +90 degrees.", "type": "number" }, - "maxValue": { - "title": "Maximum value", - "description": "The maximum allowed value for the property.", + "longitude": { + "title": "Longitude", + "description": "Represents the angular distance of a location east or west of the Prime Meridian, measured in degrees and ranges from -180 to +180 degrees.", "type": "number" + } + }, + "required": [ + "latitude", + "longitude" + ] + }, + "GeoShape": { + "title": "GeoShape", + "type": "object", + "properties": { + "@type": { + "title": "@Type", + "description": "A structured representation that describes the coordinates of a geographic feature.", + "default": "GeoShape", + "type": "string" }, - "measurementTechnique": { - "title": "Measurement technique", - "description": "A technique or technology used in a measurement.", + "box": { + "title": "Box", + "description": "A box is a rectangular region defined by a pair of coordinates representing the southwest and northeast corners of the box.", "type": "string" } }, "required": [ - "name", - "value" + "box" ] }, "Place": { @@ -2593,203 +2754,6 @@ } } }, - "HasPart": { - "title": "HasPart", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the data resource.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "description": { - "title": "Description", - "description": "Information about a related resource that is part of this resource.", - "type": "string" - } - }, - "required": [ - "name" - ] - }, - "IsPartOf": { - "title": "IsPartOf", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the data resource.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "description": { - "title": "Description", - "description": "Information about a related resource that this resource is a part of - e.g., a related collection.", - "type": "string" - } - }, - "required": [ - "name" - ] - }, - "MediaObjectPartOf": { - "title": "MediaObjectPartOf", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the related metadata document.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "description": { - "title": "Description", - "description": "Information about a related metadata document.", - "type": "string" - } - }, - "required": [ - "name" - ] - }, - "MediaObject": { - "title": "MediaObject", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "An item that encodes the record.", - "default": "MediaObject", - "type": "string" - }, - "contentUrl": { - "title": "Content URL", - "description": "The direct URL link to access or download the actual content of the media object.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "encodingFormat": { - "title": "Encoding format", - "description": "Represents the specific file format in which the media is encoded.", - "type": "string" - }, - "contentSize": { - "title": "Content size", - "description": "Represents the file size, expressed in bytes, kilobytes, megabytes, or another unit of measurement.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the media object (file).", - "type": "string" - }, - "sha256": { - "title": "SHA-256", - "description": "The SHA-256 hash of the media object.", - "type": "string" - }, - "isPartOf": { - "title": "Is part of", - "description": "Link to or citation for a related metadata document that this media object is a part of", - "type": "array", - "items": { - "title": "MediaObjectPartOf", - "type": "object", - "properties": { - "@type": { - "title": "@Type", - "description": "Submission type can include various forms of content, such as datasets, software source code, digital documents, etc.", - "default": "CreativeWork", - "type": "string" - }, - "name": { - "title": "Name or title", - "description": "Submission's name or title", - "type": "string" - }, - "url": { - "title": "URL", - "description": "The URL address to the related metadata document.", - "minLength": 1, - "maxLength": 2083, - "type": "string", - "pattern": "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$", - "errorMessage": { - "pattern": "must match format \"url\"" - } - }, - "description": { - "title": "Description", - "description": "Information about a related metadata document.", - "type": "string" - } - }, - "required": [ - "name" - ] - } - } - }, - "required": [ - "contentUrl", - "contentSize", - "name" - ] - }, "SourceOrganization": { "title": "SourceOrganization", "type": "object", diff --git a/api/models/user.py b/api/models/user.py index 94b3f8b..93e0e5d 100644 --- a/api/models/user.py +++ b/api/models/user.py @@ -30,10 +30,15 @@ def identifier(self): identifier = f"{endpoint_url}/{self.bucket}/{self.path}" return identifier + @property + def access_url(self): + return f"{self.endpoint_url}+{self.bucket}+{self.path}" + class Submission(Document): title: str = None authors: List[str] = [] + # id of the metadata record that was submitted identifier: PydanticObjectId submitted: datetime = datetime.utcnow() url: HttpUrl = None diff --git a/api/routes/catalog.py b/api/routes/catalog.py index 43b73b0..4e8d507 100644 --- a/api/routes/catalog.py +++ b/api/routes/catalog.py @@ -1,23 +1,36 @@ -from typing import Annotated, List +from typing import Annotated, List, Type from beanie import PydanticObjectId, WriteRules from fastapi import APIRouter, Depends, HTTPException, status from api.adapters.utils import get_adapter_by_type, RepositoryType from api.authentication.user import get_current_user -from api.models.catalog import DatasetMetadataDOC -from api.models.user import Submission, SubmissionType, User, S3Path +from api.models.catalog import ( + T, + CoreMetadataDOC, + GenericDatasetMetadataDOC, + HSResourceMetadataDOC, + NetCDFMetadataDOC, +) +from api.models.user import Submission, User, SubmissionType, S3Path + router = APIRouter() +# TODO: create endpoints to register netcdf and raster datasets - these endpoints will take a path to the +# metadata json file and will create a new metadata record in the catalog. Use S3 (minIO) as the metadata file path + -def inject_repository_identifier(submission: Submission, document: DatasetMetadataDOC): +def inject_repository_identifier( + submission: Submission, + document: T, +) -> T: if submission.repository_identifier: document.repository_identifier = submission.repository_identifier return document -def inject_submission_type(submission: Submission, document: DatasetMetadataDOC): +def inject_submission_type(submission: Submission, document: T) -> T: if submission.repository is None: document.submission_type = SubmissionType.IGUIDE_FORM else: @@ -25,43 +38,89 @@ def inject_submission_type(submission: Submission, document: DatasetMetadataDOC) return document -def inject_submission_s3_path(submission: Submission, document: DatasetMetadataDOC): +def inject_submission_s3_path(submission: Submission, document: T) -> T: if submission.s3_path: document.s3_path = submission.s3_path + else: + document.s3_path = None return document -@router.post("/dataset/", response_model=DatasetMetadataDOC, status_code=status.HTTP_201_CREATED) -async def create_dataset(document: DatasetMetadataDOC, user: Annotated[User, Depends(get_current_user)]): +@router.post( + "/dataset/generic", + response_model=GenericDatasetMetadataDOC, + summary="Create a new generic dataset metadata record in catalog with user provided metadata", + description="Validates the user provided metadata and creates a new dataset metadata record in catalog", + status_code=status.HTTP_201_CREATED, +) +async def create_generic_dataset( + document: GenericDatasetMetadataDOC, + user: Annotated[User, Depends(get_current_user)], +): await document.insert() submission = document.as_submission() user.submissions.append(submission) await user.save(link_rule=WriteRules.WRITE) + document = inject_repository_identifier(submission, document) document = inject_submission_type(submission, document) + document = inject_submission_s3_path(submission, document) return document -@router.get("/dataset/{submission_id}", response_model=DatasetMetadataDOC, response_model_exclude_none=True) -async def get_dataset(submission_id: PydanticObjectId): - submission: Submission = await Submission.find_one(Submission.identifier == submission_id) - if submission is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") +@router.get( + "/dataset/generic/{submission_id}", + response_model=GenericDatasetMetadataDOC, + summary="Get a generic dataset metadata record", + description="Retrieves a generic dataset metadata record by submission identifier", + response_model_exclude_none=True, +) +async def get_generic_dataset(submission_id: PydanticObjectId): + document: GenericDatasetMetadataDOC = await _get_metadata_doc( + submission_id, GenericDatasetMetadataDOC + ) + return document - document: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) - if document is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") - document = inject_repository_identifier(submission, document) - document = inject_submission_type(submission, document) - document = inject_submission_s3_path(submission, document) +@router.get( + "/dataset/netcdf/{submission_id}", + response_model=NetCDFMetadataDOC, + summary="Get a netcdf dataset metadata record", + description="Retrieves a netcdf dataset metadata record by submission identifier", + response_model_exclude_none=True, +) +async def get_netcdf_dataset(submission_id: PydanticObjectId): + document: NetCDFMetadataDOC = await _get_metadata_doc( + submission_id, NetCDFMetadataDOC + ) return document -@router.get("/dataset/", response_model=List[DatasetMetadataDOC], response_model_exclude_none=True) +@router.get( + "/dataset/hs-resource/{submission_id}", + response_model=HSResourceMetadataDOC, + response_model_exclude_none=True, + summary="Get a HydroShare resource metadata record", + description="Retrieves a HydroShare resource metadata record by submission identifier", +) +async def get_hydroshare_dataset(submission_id: PydanticObjectId): + document: HSResourceMetadataDOC = await _get_metadata_doc( + submission_id, HSResourceMetadataDOC + ) + return document + + +@router.get( + "/dataset/", + response_model=List[CoreMetadataDOC], + response_model_exclude_none=True, + summary="Get all dataset metadata records for the authenticated user", + description="Retrieves all dataset metadata records for the authenticated user", +) async def get_datasets(user: Annotated[User, Depends(get_current_user)]): documents = [ inject_repository_identifier( - submission, await DatasetMetadataDOC.get(submission.identifier) + submission, await CoreMetadataDOC.find_one( + CoreMetadataDOC.id == submission.identifier, with_children=True).project(CoreMetadataDOC) ) for submission in user.submissions ] @@ -76,32 +135,61 @@ async def get_datasets(user: Annotated[User, Depends(get_current_user)]): return documents -@router.put("/dataset/{submission_id}", response_model=DatasetMetadataDOC) +@router.put( + "/dataset/generic/{submission_id}", + response_model=GenericDatasetMetadataDOC, + summary="Update an existing generic dataset metadata record in catalog with user provided metadata", + description="Validates the user provided metadata and updates an existing dataset metadata " + "record in catalog", +) async def update_dataset( submission_id: PydanticObjectId, - updated_document: DatasetMetadataDOC, + updated_document: GenericDatasetMetadataDOC, user: Annotated[User, Depends(get_current_user)], ): submission: Submission = user.submission(submission_id) if submission is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Dataset metadata record was not found", + ) - dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) + dataset: GenericDatasetMetadataDOC = await GenericDatasetMetadataDOC.get(submission.identifier) if dataset is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Dataset metadata record was not found", + ) + updated_document.id = dataset.id + await updated_document.replace() + dataset: GenericDatasetMetadataDOC = await GenericDatasetMetadataDOC.get(submission_id) dataset = await _update_dataset(updated_document=updated_document, original_document=dataset, submission=submission) return dataset -@router.delete("/dataset/{submission_id}", response_model=dict) -async def delete_dataset(submission_id: PydanticObjectId, user: Annotated[User, Depends(get_current_user)]): +@router.delete( + "/dataset/{submission_id}", + response_model=dict, + summary="Delete a metadata record from the catalog", + description="Deletes a metadata record in catalog along with the submission record", + status_code=status.HTTP_200_OK, +) +async def delete_dataset( + submission_id: PydanticObjectId, user: Annotated[User, Depends(get_current_user)] +): submission: Submission = user.submission(submission_id) if submission is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") - dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Dataset metadata record was not found", + ) + dataset = await CoreMetadataDOC.get(submission.identifier, with_children=True) if dataset is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Dataset metadata record was not found", + ) user.submissions.remove(submission) await user.save(link_rule=WriteRules.WRITE) await submission.delete() @@ -109,57 +197,121 @@ async def delete_dataset(submission_id: PydanticObjectId, user: Annotated[User, return {"deleted_dataset_id": submission_id} -@router.get("/submission/", response_model=List[Submission]) +@router.get("/submission/", + response_model=List[Submission], + summary="Get all submission records for the authenticated user", + description="Retrieves all submission records for the authenticated user", + status_code=status.HTTP_200_OK, + ) async def get_submissions(user: Annotated[User, Depends(get_current_user)]): return user.submissions -@router.get("/repository/hydroshare/{identifier}", response_model=DatasetMetadataDOC) -async def register_hydroshare_resource_metadata(identifier: str, user: Annotated[User, Depends(get_current_user)]): +@router.get("/repository/hydroshare/{identifier}", + response_model=HSResourceMetadataDOC, + summary="Register HydroShare resource metadata record in the catalog", + description="Retrieves the metadata for the resource from HydroShare repository and creates a new " + "metadata record in the catalog", + status_code=status.HTTP_201_CREATED, + ) +async def register_hydroshare_resource_metadata( + identifier: str, user: Annotated[User, Depends(get_current_user)] +): # check that the user has not already registered this resource - submission: Submission = user.submission_by_repository(repo_type=RepositoryType.HYDROSHARE, identifier=identifier) + submission: Submission = user.submission_by_repository( + repo_type=RepositoryType.HYDROSHARE, identifier=identifier + ) if submission is not None: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="This resource has already been submitted by this user", ) - dataset: DatasetMetadataDOC = await _save_to_db(repository_type=RepositoryType.HYDROSHARE, - identifier=identifier, user=user) + dataset = await _save_to_db( + repository_type=RepositoryType.HYDROSHARE, identifier=identifier, + meta_model_type=HSResourceMetadataDOC, user=user + ) return dataset -@router.put("/repository/hydroshare/{identifier}", response_model=DatasetMetadataDOC) -async def refresh_dataset_from_hydroshare(identifier: str, user: Annotated[User, Depends(get_current_user)]): - submission: Submission = user.submission_by_repository(repo_type=RepositoryType.HYDROSHARE, identifier=identifier) +@router.put("/repository/hydroshare/{identifier}", + response_model=HSResourceMetadataDOC, + summary="Refresh HydroShare resource metadata record in the catalog", + description="Retrieves the metadata for the resource from HydroShare repository and updates the existing " + "metadata record in the catalog", + status_code=status.HTTP_200_OK, + ) +async def refresh_dataset_from_hydroshare( + identifier: str, user: Annotated[User, Depends(get_current_user)] +): + submission: Submission = user.submission_by_repository( + repo_type=RepositoryType.HYDROSHARE, identifier=identifier + ) if submission is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Dataset metadata record was not found", + ) - dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) + dataset = await HSResourceMetadataDOC.get(submission.identifier) if dataset is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Dataset metadata record was not found", + ) + + dataset = await _save_to_db( + repository_type=RepositoryType.HYDROSHARE, + identifier=identifier, + user=user, + meta_model_type=HSResourceMetadataDOC, + submission=submission, + ) + return dataset + - dataset = await _save_to_db(repository_type=RepositoryType.HYDROSHARE, identifier=identifier, - user=user, submission=submission) +@router.post("/repository/s3/generic", + response_model=GenericDatasetMetadataDOC, + summary="Register a S3 generic dataset metadata record in the catalog", + description="Retrieves the metadata for the generic dataset from S3 repository and creates a new metadata " + "record in the catalog", + status_code=status.HTTP_201_CREATED + ) +async def register_s3_generic_dataset(s3_path: S3Path, user: Annotated[User, Depends(get_current_user)]): + identifier = s3_path.identifier + submission: Submission = user.submission_by_repository(repo_type=RepositoryType.S3, identifier=identifier) + identifier = s3_path.access_url + dataset = await _save_to_db(repository_type=RepositoryType.S3, identifier=identifier, user=user, + meta_model_type=GenericDatasetMetadataDOC, + submission=submission) return dataset -@router.put("/repository/s3", response_model=DatasetMetadataDOC) -async def register_s3_dataset(request_model: S3Path, user: Annotated[User, Depends(get_current_user)]): - """User provides the path to the S3 object. The metadata is fetched from the s3 object and saved to the catalog.""" - path = request_model.path - bucket = request_model.bucket - endpoint_url = request_model.endpoint_url - identifier = f"{endpoint_url}+{bucket}+{path}" +@router.post("/repository/s3/netcdf", + response_model=NetCDFMetadataDOC, + summary="Register a S3 NetCDF dataset metadata record in the catalog", + description="Retrieves the metadata for the NetCDF dataset from S3 repository and creates a new metadata " + "record in the catalog", + status_code=status.HTTP_201_CREATED + ) +async def register_s3_netcdf_dataset(s3_path: S3Path, user: Annotated[User, Depends(get_current_user)]): + identifier = s3_path.identifier submission: Submission = user.submission_by_repository(repo_type=RepositoryType.S3, identifier=identifier) + identifier = s3_path.access_url dataset = await _save_to_db(repository_type=RepositoryType.S3, identifier=identifier, user=user, + meta_model_type=NetCDFMetadataDOC, submission=submission) return dataset -@router.post("/dataset-s3/", response_model=DatasetMetadataDOC, status_code=status.HTTP_201_CREATED) +@router.post( + "/dataset-s3/", + response_model=GenericDatasetMetadataDOC, + summary="Create a new generic dataset metadata record in catalog with user provided metadata for a S3 data object", + status_code=status.HTTP_201_CREATED +) async def create_dataset_s3( s3_path: S3Path, - document: DatasetMetadataDOC, + document: GenericDatasetMetadataDOC, user: Annotated[User, Depends(get_current_user)] ): """User provides the metadata for the dataset and the path to the S3 object. The metadata is saved @@ -186,11 +338,18 @@ async def create_dataset_s3( return document -@router.put("/dataset-s3/{submission_id}", response_model=DatasetMetadataDOC, status_code=status.HTTP_200_OK) +@router.put( + "/dataset-s3/{submission_id}", + response_model=GenericDatasetMetadataDOC, + summary="Update an existing generic dataset metadata record in catalog with user provided" + " metadata for a S3 data object", + description="Validates the user provided metadata and updates an existing dataset metadata ", + status_code=status.HTTP_200_OK +) async def update_dataset_s3( s3_path: S3Path, submission_id: PydanticObjectId, - document: DatasetMetadataDOC, + document: GenericDatasetMetadataDOC, user: Annotated[User, Depends(get_current_user)] ): """User provides the updated metadata for the dataset and the path to the S3 object. The metadata is saved @@ -206,7 +365,7 @@ async def update_dataset_s3( detail="Dataset metadata record was not found", ) - dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) + dataset: GenericDatasetMetadataDOC = await GenericDatasetMetadataDOC.get(submission.identifier) if dataset is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dataset metadata record was not found") @@ -217,48 +376,93 @@ async def update_dataset_s3( return dataset -async def _save_to_db(repository_type: RepositoryType, identifier: str, user: User, submission: Submission = None): +async def _save_to_db( + repository_type: RepositoryType, + identifier: str, + user: User, + meta_model_type: Type[T], + submission: Submission = None, +) -> T: adapter = get_adapter_by_type(repository_type=repository_type) # fetch metadata from repository as catalog dataset - repo_dataset: DatasetMetadataDOC = await _get_repo_meta_as_catalog_record(adapter=adapter, identifier=identifier) + repo_dataset = await _get_repo_meta_as_catalog_record( + adapter=adapter, identifier=identifier, meta_model_type=meta_model_type + ) + s3_path = None + if repository_type == RepositoryType.S3: + s3_endpoint_url, bucket, path = identifier.split("+") + s3_path = S3Path(path=path, bucket=bucket, endpoint_url=s3_endpoint_url) + identifier = s3_path.identifier if submission is None: # new registration await repo_dataset.insert() submission = repo_dataset.as_submission() - submission = adapter.update_submission(submission=submission, repo_record_id=identifier) + submission = adapter.update_submission( + submission=submission, repo_record_id=identifier + ) + submission.s3_path = s3_path user.submissions.append(submission) await user.save(link_rule=WriteRules.WRITE) dataset = repo_dataset else: # update existing registration - dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) + dataset = await CoreMetadataDOC.get(submission.identifier, with_children=True) repo_dataset.id = dataset.id await repo_dataset.replace() - updated_dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(submission.identifier) + updated_dataset = await CoreMetadataDOC.get( + submission.identifier, with_children=True + ) updated_submission: Submission = updated_dataset.as_submission() - updated_submission = adapter.update_submission(submission=updated_submission, repo_record_id=identifier) + updated_submission = adapter.update_submission( + submission=updated_submission, repo_record_id=identifier + ) updated_submission.id = submission.id updated_submission.submitted = submission.submitted + updated_submission.s3_path = s3_path await updated_submission.replace() dataset = updated_dataset submission = updated_submission dataset = inject_repository_identifier(submission, dataset) dataset = inject_submission_type(submission, dataset) + dataset = inject_submission_s3_path(submission, dataset) return dataset -async def _get_repo_meta_as_catalog_record(adapter, identifier: str): +async def _get_repo_meta_as_catalog_record(adapter, identifier: str, meta_model_type: Type[T]) -> T: metadata = await adapter.get_metadata(identifier) - catalog_dataset: DatasetMetadataDOC = adapter.to_catalog_record(metadata) + catalog_dataset: T = adapter.to_catalog_record(metadata, meta_model_type=meta_model_type) return catalog_dataset -async def _update_dataset(updated_document: DatasetMetadataDOC, original_document: DatasetMetadataDOC, +async def _get_metadata_doc(submission_id: PydanticObjectId, meta_model_type: Type[T]) -> T: + submission: Submission = await Submission.find_one( + Submission.identifier == submission_id + ) + if submission is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Metadata record was not found", + ) + + document: meta_model_type = await meta_model_type.get(submission.identifier) + if document is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Metadata record was not found", + ) + + document = inject_repository_identifier(submission, document) + document = inject_submission_type(submission, document) + document = inject_submission_s3_path(submission, document) + return document + + +async def _update_dataset(updated_document: T, original_document: T, submission: Submission): updated_document.id = original_document.id await updated_document.replace() - dataset: DatasetMetadataDOC = await DatasetMetadataDOC.get(original_document.id) + dataset: T = await CoreMetadataDOC.get(original_document.id, with_children=True) updated_submission: Submission = dataset.as_submission() updated_submission.id = submission.id updated_submission.repository_identifier = submission.repository_identifier diff --git a/tests/conftest.py b/tests/conftest.py index ee1662c..5b183b0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,13 @@ from api.main import app from api.authentication.user import get_current_user from api.models.catalog import CoreMetadataDOC, Submission -from api.models.schema import CoreMetadata, DatasetMetadata +from api.models.schema import ( + CoreMetadata, + GenericDatasetMetadata, + HSResourceMetadata, + HSNetCDFMetadata, + HSRasterMetadata, +) from api.models.user import User from api.procedures.user import create_or_update_user @@ -34,7 +40,9 @@ async def client_test(): if not get_settings().testing: raise Exception("App is not in testing mode") async with LifespanManager(app): - async with AsyncClient(app=app, base_url="http://test", follow_redirects=True) as ac: + async with AsyncClient( + app=app, base_url="http://test", follow_redirects=True + ) as ac: ac.app = app yield ac @@ -66,31 +74,58 @@ async def test_user_access_token(): @pytest_asyncio.fixture -async def core_data(change_test_dir): +async def core_metadata(change_test_dir): with open("data/core_metadata.json", "r") as f: - return json.loads(f.read()) + yield json.loads(f.read()) @pytest_asyncio.fixture async def dataset_data(change_test_dir): with open("data/dataset_metadata.json", "r") as f: - return json.loads(f.read()) + yield json.loads(f.read()) + + +@pytest_asyncio.fixture +async def netcdf_metadata(change_test_dir): + with open("data/netcdf_metadata.json", "r") as f: + yield json.loads(f.read()) + + +@pytest_asyncio.fixture +async def raster_metadata(change_test_dir): + with open("data/raster_metadata.json", "r") as f: + yield json.loads(f.read()) + + +@pytest_asyncio.fixture +async def hs_resource_model(): + yield HSResourceMetadata @pytest_asyncio.fixture async def core_model(): - return CoreMetadata + yield CoreMetadata + + +@pytest_asyncio.fixture +async def generic_dataset_model(): + yield GenericDatasetMetadata + + +@pytest_asyncio.fixture +async def netcdf_metadata_model(): + yield HSNetCDFMetadata @pytest_asyncio.fixture -async def dataset_model(): - return DatasetMetadata +async def raster_metadata_model(): + yield HSRasterMetadata @pytest_asyncio.fixture async def hydroshare_resource_metadata(change_test_dir): with open("data/hydroshare_resource_meta.json", "r") as f: - return json.loads(f.read()) + yield json.loads(f.read()) @pytest_asyncio.fixture @@ -114,4 +149,4 @@ async def hydroshare_collection_metadata(hydroshare_resource_metadata): {"type": "This resource is described by", "value": "another resource"}, ] collection_meta["relations"] = relations - return collection_meta + yield collection_meta diff --git a/tests/data/core_metadata.json b/tests/data/core_metadata.json index 99fffd9..33f3d06 100644 --- a/tests/data/core_metadata.json +++ b/tests/data/core_metadata.json @@ -107,11 +107,6 @@ "contentSize": "0.17 MB", "name": "USGS gage locations within the Harvey-affected areas in Texas", "sha256": "830f4b50e78e8a8fb0f7eee7369171dacbcaa43cc2c4deb59cef8e4fd2f641c5", - "additionalProperty": [], - "variableMeasured": null, - "spatialCoverage": null, - "temporalCoverage": null, - "sourceOrganization": null, "isPartOf": null } ], diff --git a/tests/data/dataset_metadata.json b/tests/data/dataset_metadata.json index 2e0e4cd..c5dd395 100644 --- a/tests/data/dataset_metadata.json +++ b/tests/data/dataset_metadata.json @@ -5,6 +5,7 @@ "description": "This is a test dataset metadata json file to test catalog api", "url": "https://hydroshare.org/resource/e2fdf99cbb0c4275b32afd3c16ae6863", "identifier": ["https://www.hydroshare.org/resource/6625bdbde41c45c2b906f32be7ea70f0/"], + "additionalType": "Generic", "creator": [ { "@type": "Person", diff --git a/tests/data/netcdf_metadata.json b/tests/data/netcdf_metadata.json new file mode 100644 index 0000000..c2bb465 --- /dev/null +++ b/tests/data/netcdf_metadata.json @@ -0,0 +1,195 @@ +{ + "context": "https://schema.org", + "type": "Dataset", + "additionalType": "Multidimensional Dataset", + "name": "Snow water equivalent estimation at TWDEF site from Oct 2009 to June 2010", + "description": "This netCDF data is the simulation output from Utah Energy Balance (UEB) model.It includes the simulation result of snow water equivalent during the period Oct. 2009 to June 2010 for TWDEF site in Utah.", + "url": "https://www.hydroshare.org/resource/e2319a79641047bebaee534cfc250e2a/data/contents/netcdf_valid.nc", + "creator": [ + { + "type": "Person", + "email": "jamy@gmail.com", + "identifier": null, + "affiliation": null, + "name": "Jamy" + } + ], + "keywords": [ + "Snow water equivalent" + ], + "associatedMedia": [ + { + "type": "MediaObject", + "contentUrl": "https://www.hydroshare.org/resource/e2319a79641047bebaee534cfc250e2a/data/contents/netcdf_valid.nc", + "encodingFormat": "application/x-netcdf", + "contentSize": "4869.898 KB", + "name": "netcdf_valid.nc", + "sha256": "a3229e683732e4188077d33480f0321c7d97be2248f50ac2a6c1c3928b8c6f21" + } + ], + "spatialCoverage": { + "type": "Place", + "name": null, + "geo": { + "type": "GeoShape", + "box": "41.867126409000086 -111.5059403684569 41.86390807452128 -111.51138807956225" + }, + "additionalProperty": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "projection", + "value": "WGS 84 EPSG:4326" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "units", + "value": "Decimal degrees" + } + ] + }, + "temporalCoverage": { + "endDate": "2010-05-30T23:00:00", + "startDate": "2009-10-01T00:00:00" + }, + "additionalProperty": [], + "variableMeasured": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "m", + "description": "Snow water equivalent", + "minValue": null, + "maxValue": null, + "measurementTechnique": "model simulation of UEB model", + "name": "SWE", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Float", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "shape", + "value": "y,x,time" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "missingValue", + "value": "-9999" + } + ] + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Unknown", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "transverse_mercator", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Unknown", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "shape", + "value": "Not defined" + } + ] + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "hours since 2009-10-1 0:0:00 UTC", + "description": "time", + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "time", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Float", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "shape", + "value": "time" + } + ] + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Meter", + "description": "y coordinate of projection", + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "y", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Float", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "shape", + "value": "y" + } + ] + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Meter", + "description": "x coordinate of projection", + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "x", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "Float", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "shape", + "value": "x" + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/raster_metadata.json b/tests/data/raster_metadata.json new file mode 100644 index 0000000..28ae0b7 --- /dev/null +++ b/tests/data/raster_metadata.json @@ -0,0 +1,294 @@ +{ + "context": "https://schema.org", + "type": "Dataset", + "additionalType": "Geo Raster", + "name": "Logan", + "description": null, + "creator": null, + "keywords": null, + "url": "https://www.hydroshare.org/resource/e2319a79641047bebaee534cfc250e2a/data/contents/logan.vrt", + "associatedMedia": [ + { + "type": "MediaObject", + "contentUrl": "https://www.hydroshare.org/resource/e2319a79641047bebaee534cfc250e2a/data/contents/logan1.tif", + "encodingFormat": "image/tiff", + "contentSize": "100.749 KB", + "name": "logan1.tif", + "sha256": "830f4b50e78e8a8fb0f7eee7369171dacbcaa43cc2c4deb59cef8e4fd2f641c5" + }, + { + "type": "MediaObject", + "contentUrl": "https://www.hydroshare.org/resource/e2319a79641047bebaee534cfc250e2a/data/contents/logan2.tif", + "encodingFormat": "image/tiff", + "contentSize": "98.943 KB", + "name": "logan2.tif", + "sha256": "b5ad48ae6428c23c63ceaa93838eb2b3a688a3ecfbf10f01fcbc25f0d2d16f46" + }, + { + "type": "MediaObject", + "contentUrl": "https://www.hydroshare.org/resource/e2319a79641047bebaee534cfc250e2a/data/contents/logan.vrt", + "encodingFormat": null, + "contentSize": "2.175 KB", + "name": "logan.vrt", + "sha256": "94a844d49defcafef815258d1d2bc558b464d9454de7e178b25d5377eca789b4" + } + ], + "spatialCoverage": { + "type": "Place", + "name": null, + "geo": { + "type": "GeoShape", + "box": "42.0500287857716 -111.57737502643897 41.98745777903126 -111.65768822411246" + }, + "additionalProperty": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "projection", + "value": "WGS 84 EPSG:4326" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "units", + "value": "Decimal degrees" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": "meter", + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "spatialReference", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "datum", + "value": "North_American_Datum_1983" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "projection_string", + "value": "PROJCS[\"NAD83 / UTM zone 12N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4269\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-111],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"26912\"]]" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "projection", + "value": "NAD83 / UTM zone 12N" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "eastlimit", + "value": 452174.01909127034 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "northlimit", + "value": 4655492.446916306 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "westlimit", + "value": 445574.01909127034 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "southlimit", + "value": 4648592.446916306 + } + ] + } + ] + }, + "temporalCoverage": null, + "additionalProperty": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "cellInformation", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "name", + "value": "logan.vrt" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "cellDataType", + "value": "Float32" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "cellSizeXValue", + "value": 30.0 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "cellSizeYValue", + "value": 30.0 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "columns", + "value": 220 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "rows", + "value": 230 + } + ] + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": 2274.958984375, + "maxValue": 2880.007080078125, + "measurementTechnique": null, + "name": "bandInformation", + "value": [ + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "name", + "value": "Band_1" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "noDataValue", + "value": -3.4028234663852886e+38 + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "variableName", + "value": "" + }, + { + "type": "PropertyValue", + "propertyID": null, + "unitCode": null, + "description": null, + "minValue": null, + "maxValue": null, + "measurementTechnique": null, + "name": "variableUnit", + "value": "" + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/test_core_schema.py b/tests/test_core_schema.py index 5be8669..2b5ded2 100644 --- a/tests/test_core_schema.py +++ b/tests/test_core_schema.py @@ -6,34 +6,31 @@ @pytest.mark.asyncio -async def test_core_schema(core_data, core_model): +async def test_core_schema(core_metadata, generic_dataset_model): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + generic_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) - assert core_model_instance.name == "Test Dataset" + assert generic_model_instance.name == "Test Dataset" @pytest.mark.parametrize('multiple_creators', [True, False]) @pytest.mark.parametrize('creator_type', ["person", "organization"]) @pytest.mark.asyncio -async def test_core_schema_creator_cardinality(core_data, core_model, multiple_creators, creator_type): +async def test_core_schema_creator_cardinality(core_metadata, generic_dataset_model, multiple_creators, creator_type): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we can have one or more creators. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("creator") + + core_metadata.pop("creator") if multiple_creators: if creator_type == "person": - core_data["creator"] = [ + core_metadata["creator"] = [ {"@type": "Person", "name": "John Doe", "email": "john.doe@gmail.com"}, { "@type": "Person", @@ -48,7 +45,7 @@ async def test_core_schema_creator_cardinality(core_data, core_model, multiple_c } ] else: - core_data["creator"] = [ + core_metadata["creator"] = [ { "@type": "Organization", "name": "National Centers for Environmental Information", @@ -63,11 +60,11 @@ async def test_core_schema_creator_cardinality(core_data, core_model, multiple_c ] else: if creator_type == "person": - core_data["creator"] = [ + core_metadata["creator"] = [ {"@type": "Person", "name": "John Doe", "email": "john.doe@gmail.com"} ] else: - core_data["creator"] = [ + core_metadata["creator"] = [ { "@type": "Organization", "name": "National Centers for Environmental Information", @@ -76,7 +73,7 @@ async def test_core_schema_creator_cardinality(core_data, core_model, multiple_c ] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if multiple_creators: if creator_type == "person": @@ -135,18 +132,17 @@ async def test_core_schema_creator_cardinality(core_data, core_model, multiple_c ] ) @pytest.mark.asyncio -async def test_core_schema_creator_person_optional_attributes(core_data, core_model, data_format): +async def test_core_schema_creator_person_optional_attributes(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing email and identifier attributes are optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("creator") - core_data["creator"] = [data_format] + + core_metadata.pop("creator") + core_metadata["creator"] = [data_format] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.creator[0].type == "Person" assert core_model_instance.creator[0].name == "John Doe" @@ -198,18 +194,17 @@ async def test_core_schema_creator_person_optional_attributes(core_data, core_mo ] ) @pytest.mark.asyncio -async def test_core_schema_creator_affiliation_optional_attributes(core_data, core_model, data_format): +async def test_core_schema_creator_affiliation_optional_attributes(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing creator affiliation optional attributes. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("creator") - core_data["creator"] = [data_format] + + core_metadata.pop("creator") + core_metadata["creator"] = [data_format] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.creator[0].type == "Person" assert core_model_instance.creator[0].name == "John Doe" @@ -248,18 +243,17 @@ async def test_core_schema_creator_affiliation_optional_attributes(core_data, co ] ) @pytest.mark.asyncio -async def test_core_schema_creator_organization_optional_attributes(core_data, core_model, data_format): +async def test_core_schema_creator_organization_optional_attributes(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing optional attributes of the organization object. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("creator") - core_data["creator"] = [data_format] + + core_metadata.pop("creator") + core_metadata["creator"] = [data_format] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.creator[0].type == "Organization" assert core_model_instance.creator[0].name == "National Centers for Environmental Information" if "url" in data_format: @@ -268,18 +262,15 @@ async def test_core_schema_creator_organization_optional_attributes(core_data, c assert core_model_instance.creator[0].address == "1167 Massachusetts Ave Suites 418 & 419, Arlington, MA 02476" -@pytest.mark.parametrize('multiple_media', [True, False, None]) +@pytest.mark.parametrize('multiple_media', [True, False]) @pytest.mark.asyncio -async def test_core_schema_associated_media_cardinality(core_data, core_model, multiple_media): +async def test_core_schema_associated_media_cardinality(core_metadata, generic_dataset_model, multiple_media): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing one or more associated media objects can be created. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - if multiple_media is None: - core_data.pop("associatedMedia", None) - if multiple_media and multiple_media is not None: + + if multiple_media: associated_media = [ { "@type": "MediaObject", @@ -299,9 +290,9 @@ async def test_core_schema_associated_media_cardinality(core_data, core_model, m }, ] - core_data["associatedMedia"] = associated_media + core_metadata["associatedMedia"] = associated_media - elif multiple_media is not None: + else: associated_media = [ { "@type": "MediaObject", @@ -312,14 +303,12 @@ async def test_core_schema_associated_media_cardinality(core_data, core_model, m "name": "USGS gage locations within the Harvey-affected areas in Texas", } ] - core_data["associatedMedia"] = associated_media + core_metadata["associatedMedia"] = associated_media # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) - if multiple_media is None: - assert core_model_instance.associatedMedia is None - if multiple_media and multiple_media is not None: + if multiple_media: assert len(core_model_instance.associatedMedia) == 2 assert core_model_instance.associatedMedia[0].type == associated_media[0]["@type"] assert core_model_instance.associatedMedia[1].type == associated_media[1]["@type"] @@ -333,7 +322,7 @@ async def test_core_schema_associated_media_cardinality(core_data, core_model, m assert core_model_instance.associatedMedia[1].contentUrl == associated_media[1]["contentUrl"] assert core_model_instance.associatedMedia[0].sha256 == associated_media[0]["sha256"] assert core_model_instance.associatedMedia[1].sha256 == associated_media[1]["sha256"] - elif multiple_media is not None: + else: assert core_model_instance.associatedMedia[0].type == associated_media[0]["@type"] assert core_model_instance.associatedMedia[0].name == associated_media[0]["name"] assert core_model_instance.associatedMedia[0].contentSize == associated_media[0]["contentSize"] @@ -361,7 +350,7 @@ async def test_core_schema_associated_media_cardinality(core_data, core_model, m ) @pytest.mark.asyncio async def test_core_schema_associated_media_content_size( - core_data, core_model, content_size_format + core_metadata, generic_dataset_model, content_size_format ): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing @@ -369,10 +358,7 @@ async def test_core_schema_associated_media_content_size( Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - - core_data["associatedMedia"] = [ + core_metadata["associatedMedia"] = [ { "@type": "MediaObject", "contentUrl": "https://www.hydroshare.org/resource/51d1539bf6e94b15ac33f7631228118c/data/contents/USGS_Harvey_gages_TxLaMsAr.csv", @@ -382,34 +368,70 @@ async def test_core_schema_associated_media_content_size( "name": "USGS gage locations within the Harvey-affected areas in Texas", } ] - # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.associatedMedia[0].contentSize == content_size_format +@pytest.mark.parametrize('include_is_part_of', [True, False]) +@pytest.mark.asyncio +async def test_core_schema_associated_media_is_part_of_optional( + core_metadata, generic_dataset_model, include_is_part_of +): + """Test that a core metadata pydantic model can be created from core metadata json. + Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing + that the isPartOf attribute of the associatedMedia is optional. + Note: This test does nat add a record to the database. + """ + + core_metadata["associatedMedia"] = [ + { + "@type": "MediaObject", + "contentUrl": "https://www.hydroshare.org/resource/51d1539bf6e94b15ac33f7631228118c/data/contents/USGS_Harvey_gages_TxLaMsAr.csv", + "encodingFormat": "text/csv", + "contentSize": "10.0 GB", + "sha256": "2fba6f2ebac562dac6a57acf0fdc5fdfabc9654b3c910aa6ef69cf4385997e19", + "name": "USGS gage locations within the Harvey-affected areas in Texas", + } + ] + if include_is_part_of: + core_metadata["associatedMedia"][0]["isPartOf"] = { + "@type": "CreativeWork", + "name": "USGS_Harvey_gages_TxLaMsAr.csv.json", + "url": "https://www.hydroshare.org/resource/51d1539bf6e94b15ac33f7631228118c/data/contents/USGS_Harvey_gages_TxLaMsAr.csv.json", + } + # validate the data model + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) + # assert core_model_instance.associatedMedia[0].contentSize == content_size_format + if include_is_part_of: + assert core_model_instance.associatedMedia[0].isPartOf.type == "CreativeWork" + assert core_model_instance.associatedMedia[0].isPartOf.name == "USGS_Harvey_gages_TxLaMsAr.csv.json" + assert core_model_instance.associatedMedia[0].isPartOf.url == "https://www.hydroshare.org/resource/51d1539bf6e94b15ac33f7631228118c/data/contents/USGS_Harvey_gages_TxLaMsAr.csv.json" + else: + assert core_model_instance.associatedMedia[0].isPartOf is None + + @pytest.mark.parametrize("include_coverage", [True, False]) @pytest.mark.asyncio -async def test_core_schema_temporal_coverage_optional(core_data, core_model, include_coverage): +async def test_core_schema_temporal_coverage_optional(core_metadata, generic_dataset_model, include_coverage): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing temporal coverage can be optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model + coverage_value = { "startDate": "2007-03-01T13:00:00", "endDate": "2008-05-11T15:30:00", } - core_data.pop("temporalCoverage", None) + core_metadata.pop("temporalCoverage", None) if not include_coverage: - core_data.pop("temporalCoverage", None) + core_metadata.pop("temporalCoverage", None) else: - core_data["temporalCoverage"] = coverage_value + core_metadata["temporalCoverage"] = coverage_value # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if not include_coverage: assert core_model_instance.temporalCoverage is None else: @@ -425,18 +447,17 @@ async def test_core_schema_temporal_coverage_optional(core_data, core_model, inc ], ) @pytest.mark.asyncio -async def test_core_schema_temporal_coverage_format(core_data, core_model, data_format): +async def test_core_schema_temporal_coverage_format(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that endDate is optional for temporal coverage. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data["temporalCoverage"] = data_format + + core_metadata["temporalCoverage"] = data_format # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.temporalCoverage.startDate == datetime.datetime(2007, 3, 1, 13, 0, 0) if "endDate" in data_format: assert core_model_instance.temporalCoverage.endDate == datetime.datetime(2008, 5, 11, 15, 30, 0) @@ -446,14 +467,13 @@ async def test_core_schema_temporal_coverage_format(core_data, core_model, data_ @pytest.mark.parametrize('include_coverage', [True, False]) @pytest.mark.asyncio -async def test_core_schema_spatial_coverage_optional(core_data, core_model, include_coverage): +async def test_core_schema_spatial_coverage_optional(core_metadata, generic_dataset_model, include_coverage): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing spatial coverage can be optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model + coverage_value = { "@type": "Place", "name": "CUAHSI Office", @@ -465,12 +485,12 @@ async def test_core_schema_spatial_coverage_optional(core_data, core_model, incl } if not include_coverage: - core_data.pop("spatialCoverage", None) + core_metadata.pop("spatialCoverage", None) else: - core_data["spatialCoverage"] = coverage_value + core_metadata["spatialCoverage"] = coverage_value # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if not include_coverage: assert core_model_instance.spatialCoverage is None else: @@ -551,17 +571,16 @@ async def test_core_schema_spatial_coverage_optional(core_data, core_model, incl ], ) @pytest.mark.asyncio -async def test_core_schema_spatial_coverage_value_type(core_data, core_model, data_format): +async def test_core_schema_spatial_coverage_value_type(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing valid values for spatial coverage with optional additionalProperty attribute. Note: This test does not add a record to the database. """ - core_data = core_data - core_model = core_model - core_data["spatialCoverage"] = data_format + + core_metadata["spatialCoverage"] = data_format # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.spatialCoverage.type == "Place" if "name" in data_format: @@ -620,26 +639,25 @@ async def test_core_schema_spatial_coverage_value_type(core_data, core_model, da @pytest.mark.parametrize('include_creative_works', [True, False]) @pytest.mark.asyncio -async def test_create_dataset_creative_works_status_optional(core_data, core_model, include_creative_works): +async def test_create_dataset_creative_works_status_optional(core_metadata, generic_dataset_model, + include_creative_works): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing creativeWorkStatus can be optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model if not include_creative_works: - core_data.pop("creativeWorkStatus", None) + core_metadata.pop("creativeWorkStatus", None) else: - core_data["creativeWorkStatus"] = { + core_metadata["creativeWorkStatus"] = { "@type": "DefinedTerm", "name": "Draft", "description": "This is a draft dataset" } # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if not include_creative_works: assert core_model_instance.creativeWorkStatus is None else: @@ -650,23 +668,21 @@ async def test_create_dataset_creative_works_status_optional(core_data, core_mod @pytest.mark.parametrize('include_multiple', [True, False]) @pytest.mark.asyncio -async def test_core_schema_keywords_cardinality(core_data, core_model, include_multiple): +async def test_core_schema_keywords_cardinality(core_metadata, generic_dataset_model, include_multiple): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that one or more keywords can be added. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("keywords", None) + core_metadata.pop("keywords", None) if include_multiple: - core_data["keywords"] = ["Leaf wetness", "Core"] + core_metadata["keywords"] = ["Leaf wetness", "Core"] else: - core_data["keywords"] = ["Leaf wetness"] + core_metadata["keywords"] = ["Leaf wetness"] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if include_multiple: assert len(core_model_instance.keywords) == 2 assert core_model_instance.keywords[0] == "Leaf wetness" @@ -676,6 +692,29 @@ async def test_core_schema_keywords_cardinality(core_data, core_model, include_m assert core_model_instance.keywords[0] == "Leaf wetness" +@pytest.mark.parametrize('include_keywords', [True, False]) +@pytest.mark.asyncio +async def test_core_schema_keywords_optional(core_metadata, generic_dataset_model, include_keywords): + """Test that a core metadata pydantic model can be created from core metadata json. + Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing + that the value for the keywords attribute is optional. + Note: This test does nat add a record to the database. + """ + + core_metadata.pop("keywords", None) + if include_keywords: + core_metadata["keywords"] = ["Leaf wetness", "Core"] + + # validate the data model + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) + if include_keywords: + assert len(core_model_instance.keywords) == 2 + assert core_model_instance.keywords[0] == "Leaf wetness" + assert core_model_instance.keywords[1] == "Core" + else: + assert core_model_instance.keywords is None + + @pytest.mark.skip(reason="Not sure if we need to support URL format for license.") @pytest.mark.parametrize( "data_format", @@ -690,18 +729,16 @@ async def test_core_schema_keywords_cardinality(core_data, core_model, include_m ] ) @pytest.mark.asyncio -async def test_core_schema_license_value_type(core_data, core_model, data_format): +async def test_core_schema_license_value_type(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing valid value types for license property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data["license"] = data_format + core_metadata["license"] = data_format # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if isinstance(data_format, str): assert core_model_instance.license == data_format else: @@ -737,18 +774,16 @@ async def test_core_schema_license_value_type(core_data, core_model, data_format ] ) @pytest.mark.asyncio -async def test_core_schema_license_optional_attributes(core_data, core_model, data_format): +async def test_core_schema_license_optional_attributes(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing license of type CreativeWork optional attributes. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data["license"] = data_format + core_metadata["license"] = data_format # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.license.type == data_format["@type"] assert core_model_instance.license.name == data_format["name"] @@ -760,16 +795,14 @@ async def test_core_schema_license_optional_attributes(core_data, core_model, da @pytest.mark.parametrize('is_multiple', [True, False, None]) @pytest.mark.asyncio -async def test_core_schema_has_part_of_cardinality(core_data, core_model, is_multiple): +async def test_core_schema_has_part_of_cardinality(core_metadata, generic_dataset_model, is_multiple): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that the hasPartOf property is optional and one or more values can be added for this property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("hasPart", None) + core_metadata.pop("hasPart", None) has_parts = [] if is_multiple and is_multiple is not None: has_parts = [ @@ -786,7 +819,7 @@ async def test_core_schema_has_part_of_cardinality(core_data, core_model, is_mul "url": "https://www.hydroshare.org/resource/582060f00f6b443bb26e896426d9f62b/" } ] - core_data["hasPart"] = has_parts + core_metadata["hasPart"] = has_parts elif is_multiple is not None: has_parts = [ { @@ -796,10 +829,10 @@ async def test_core_schema_has_part_of_cardinality(core_data, core_model, is_mul "url": "https://www.hydroshare.org/resource/582060f00f6b443bb26e896426d9f62a/" } ] - core_data["hasPart"] = has_parts + core_metadata["hasPart"] = has_parts # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if is_multiple and is_multiple is not None: assert len(core_model_instance.hasPart) == 2 assert core_model_instance.hasPart[0].type == has_parts[0]["@type"] @@ -846,19 +879,18 @@ async def test_core_schema_has_part_of_cardinality(core_data, core_model, is_mul ] ) @pytest.mark.asyncio -async def test_core_schema_has_part_optional_attributes(core_data, core_model, data_format): +async def test_core_schema_has_part_optional_attributes(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing the optional attributes of hasPart property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("hasPart", None) - core_data["hasPart"] = [data_format] + + core_metadata.pop("hasPart", None) + core_metadata["hasPart"] = [data_format] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.hasPart[0].type == data_format["@type"] assert core_model_instance.hasPart[0].name == data_format["name"] if "description" in data_format: @@ -869,15 +901,14 @@ async def test_core_schema_has_part_optional_attributes(core_data, core_model, d @pytest.mark.parametrize('is_multiple', [True, False, None]) @pytest.mark.asyncio -async def test_core_schema_is_part_of_cardinality(core_data, core_model, is_multiple): +async def test_core_schema_is_part_of_cardinality(core_metadata, generic_dataset_model, is_multiple): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that the isPartOf property is optional and one or more values can be added for this property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("isPartOf", None) + + core_metadata.pop("isPartOf", None) is_part_of = [] if is_multiple and is_multiple is not None: is_part_of = [ @@ -894,7 +925,7 @@ async def test_core_schema_is_part_of_cardinality(core_data, core_model, is_mult "url": "https://www.hydroshare.org/resource/582060f00f6b443bb26e896426d9f62b/" } ] - core_data["isPartOf"] = is_part_of + core_metadata["isPartOf"] = is_part_of elif is_multiple is not None: is_part_of = [ { @@ -904,10 +935,10 @@ async def test_core_schema_is_part_of_cardinality(core_data, core_model, is_mult "url": "https://www.hydroshare.org/resource/582060f00f6b443bb26e896426d9f62a/" } ] - core_data["isPartOf"] = is_part_of + core_metadata["isPartOf"] = is_part_of # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if is_multiple and is_multiple is not None: assert len(core_model_instance.isPartOf) == 2 assert core_model_instance.isPartOf[0].type == is_part_of[0]["@type"] @@ -954,19 +985,18 @@ async def test_core_schema_is_part_of_cardinality(core_data, core_model, is_mult ] ) @pytest.mark.asyncio -async def test_core_schema_is_part_of_optional_attributes(core_data, core_model, data_format): +async def test_core_schema_is_part_of_optional_attributes(core_metadata, generic_dataset_model, data_format): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing the optional attributes of the isPartOf property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("isPartOf", None) - core_data["isPartOf"] = [data_format] + + core_metadata.pop("isPartOf", None) + core_metadata["isPartOf"] = [data_format] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) assert core_model_instance.isPartOf[0].type == data_format["@type"] assert core_model_instance.isPartOf[0].name == data_format["name"] if "description" in data_format: @@ -977,31 +1007,30 @@ async def test_core_schema_is_part_of_optional_attributes(core_data, core_model, @pytest.mark.parametrize('dt_type', ["datetime", None]) @pytest.mark.asyncio -async def test_core_schema_date_value_type(core_data, core_model, dt_type): +async def test_core_schema_date_value_type(core_metadata, generic_dataset_model, dt_type): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing allowed value types for the date type attributes (dateCreated, dateModified, and datePublished). Also testing that dateModified and datePublished are optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model + # TODO: test 'date' type after knowing whether we need to support both date and datetime if dt_type == "date": - core_data["dateCreated"] = "2020-01-01" - core_data["dateModified"] = "2020-02-01" - core_data["datePublished"] = "2020-05-01" + core_metadata["dateCreated"] = "2020-01-01" + core_metadata["dateModified"] = "2020-02-01" + core_metadata["datePublished"] = "2020-05-01" elif dt_type == "datetime": - core_data["dateCreated"] = "2020-01-01T10:00:05" - core_data["dateModified"] = "2020-02-01T11:20:30" - core_data["datePublished"] = "2020-05-01T08:00:45" + core_metadata["dateCreated"] = "2020-01-01T10:00:05" + core_metadata["dateModified"] = "2020-02-01T11:20:30" + core_metadata["datePublished"] = "2020-05-01T08:00:45" else: - core_data["dateCreated"] = "2020-01-01T10:00:05" - core_data.pop("dateModified", None) - core_data.pop("datePublished", None) + core_metadata["dateCreated"] = "2020-01-01T10:00:05" + core_metadata.pop("dateModified", None) + core_metadata.pop("datePublished", None) # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if dt_type == "date": assert core_model_instance.dateCreated == datetime.date(2020, 1, 1) assert core_model_instance.dateModified == datetime.date(2020, 2, 1) @@ -1018,30 +1047,29 @@ async def test_core_schema_date_value_type(core_data, core_model, dt_type): @pytest.mark.parametrize('provider_type', ["person", "organization"]) @pytest.mark.asyncio -async def test_core_schema_provider_value_type(core_data, core_model, provider_type): +async def test_core_schema_provider_value_type(core_metadata, generic_dataset_model, provider_type): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing allowed value types for the provider. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("provider", None) + + core_metadata.pop("provider", None) if provider_type == "person": - core_data["provider"] = { + core_metadata["provider"] = { "@type": "Person", "name": "John Doe", "email": "jdoe@gmail.com" } else: - core_data["provider"] = { + core_metadata["provider"] = { "@type": "Organization", "name": "HydroShare", "url": "https://hydroshare.org" } # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) provider = core_model_instance.provider if provider_type == "person": assert provider.type == "Person" @@ -1055,18 +1083,17 @@ async def test_core_schema_provider_value_type(core_data, core_model, provider_t @pytest.mark.parametrize('multiple_values', [True, False, None]) @pytest.mark.asyncio -async def test_core_schema_subject_of_cardinality(core_data, core_model, multiple_values): +async def test_core_schema_subject_of_cardinality(core_metadata, generic_dataset_model, multiple_values): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that the subjectOf property is optional and one or more values can be added for this property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model + if multiple_values is None: - core_data.pop("subjectOf", None) + core_metadata.pop("subjectOf", None) elif multiple_values: - core_data["subjectOf"] = [ + core_metadata["subjectOf"] = [ { "@type": "CreativeWork", "name": "Test subject of - 1", @@ -1080,7 +1107,7 @@ async def test_core_schema_subject_of_cardinality(core_data, core_model, multipl }, ] else: - core_data["subjectOf"] = [ + core_metadata["subjectOf"] = [ { "@type": "CreativeWork", "name": "Test subject of" @@ -1088,7 +1115,7 @@ async def test_core_schema_subject_of_cardinality(core_data, core_model, multipl ] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if multiple_values and multiple_values is not None: assert len(core_model_instance.subjectOf) == 2 assert core_model_instance.subjectOf[0].type == "CreativeWork" @@ -1112,21 +1139,20 @@ async def test_core_schema_subject_of_cardinality(core_data, core_model, multipl @pytest.mark.parametrize('include_version', [True, False]) @pytest.mark.asyncio -async def test_core_schema_version_cardinality(core_data, core_model, include_version): +async def test_core_schema_version_cardinality(core_metadata, generic_dataset_model, include_version): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that the version property is optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model + if include_version: - core_data["version"] = "v1.0" + core_metadata["version"] = "v1.0" else: - core_data.pop("version", None) + core_metadata.pop("version", None) # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if include_version: assert core_model_instance.version == "v1.0" else: @@ -1135,21 +1161,20 @@ async def test_core_schema_version_cardinality(core_data, core_model, include_ve @pytest.mark.parametrize('include_language', [True, False]) @pytest.mark.asyncio -async def test_core_schema_language_cardinality(core_data, core_model, include_language): +async def test_core_schema_language_cardinality(core_metadata, generic_dataset_model, include_language): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that the inLanguage property is optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model + if include_language: - core_data["inLanguage"] = "en-US" + core_metadata["inLanguage"] = "en-US" else: - core_data.pop("inLanguage", None) + core_metadata.pop("inLanguage", None) # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if include_language: assert core_model_instance.inLanguage == "en-US" else: @@ -1158,17 +1183,15 @@ async def test_core_schema_language_cardinality(core_data, core_model, include_l @pytest.mark.parametrize("multiple_funding", [True, False, None]) @pytest.mark.asyncio -async def test_core_schema_funding_cardinality(core_data, core_model, multiple_funding): +async def test_core_schema_funding_cardinality(core_metadata, generic_dataset_model, multiple_funding): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing that the funding property is optional and one or more values can be added to this property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model if multiple_funding and multiple_funding is not None: - core_data["funding"] = [ + core_metadata["funding"] = [ { "@type": "MonetaryGrant", "name": "HDR Institute: Geospatial Understanding through an Integrative Discovery Environment - 1", @@ -1181,7 +1204,7 @@ async def test_core_schema_funding_cardinality(core_data, core_model, multiple_f } ] elif multiple_funding is not None: - core_data["funding"] = [ + core_metadata["funding"] = [ { "@type": "MonetaryGrant", "name": "HDR Institute: Geospatial Understanding through an Integrative Discovery Environment", @@ -1190,10 +1213,10 @@ async def test_core_schema_funding_cardinality(core_data, core_model, multiple_f } ] else: - core_data.pop("funding", None) + core_metadata.pop("funding", None) # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if multiple_funding and multiple_funding is not None: assert core_model_instance.funding[0].type == "MonetaryGrant" assert ( @@ -1201,19 +1224,14 @@ async def test_core_schema_funding_cardinality(core_data, core_model, multiple_f == "HDR Institute: Geospatial Understanding through an Integrative Discovery Environment - 1" ) assert core_model_instance.funding[0].identifier == "https://nsf.gov/awardsearch/showAward?AWD_ID=2118329" - # assert core_model_instance.funding[0].funder.type == "Organization" - # assert core_model_instance.funding[0].funder.name == "National Science Foundation" - # assert core_model_instance.funding[0].funder.url[0] == "https://ror.org/021nxhr62" - # assert core_model_instance.funding[0].funder.identifier[1] == "https://doi.org/10.13039/100000001" + assert core_model_instance.funding[1].type == "MonetaryGrant" assert ( core_model_instance.funding[1].name == "HDR Institute: Geospatial Understanding through an Integrative Discovery Environment - 2" ) assert core_model_instance.funding[1].description == "Test grant description" - # assert core_model_instance.funding[1].funder.type == "Person" - # assert core_model_instance.funding[1].funder.name == "John Doe" - # assert core_model_instance.funding[1].funder.email == "johnd@gmail.com" + elif multiple_funding is not None: assert core_model_instance.funding[0].type == "MonetaryGrant" assert ( @@ -1222,26 +1240,22 @@ async def test_core_schema_funding_cardinality(core_data, core_model, multiple_f ) assert core_model_instance.funding[0].identifier == "https://nsf.gov/awardsearch/showAward?AWD_ID=2118329" assert core_model_instance.funding[0].description == "Test grant description" - # assert core_model_instance.funding[0].funder.type == "Person" - # assert core_model_instance.funding[0].funder.name == "John Doe" - # assert core_model_instance.funding[0].funder.email == "johnd@gmail.com" else: assert core_model_instance.funding is None @pytest.mark.parametrize('include_funder', [True, False]) @pytest.mark.asyncio -async def test_core_schema_funding_funder_optional(core_data, core_model, include_funder): +async def test_core_schema_funding_funder_optional(core_metadata, generic_dataset_model, include_funder): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing value for the funder attribute of the funding property is optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("funding", None) + + core_metadata.pop("funding", None) if include_funder: - core_data["funding"] = [ + core_metadata["funding"] = [ { "@type": "MonetaryGrant", "name": "HDR Institute: Geospatial Understanding through an Integrative Discovery Environment", @@ -1276,7 +1290,7 @@ async def test_core_schema_funding_funder_optional(core_data, core_model, includ } ] else: - core_data["funding"] = [ + core_metadata["funding"] = [ { "@type": "MonetaryGrant", "name": "HDR Institute: Geospatial Understanding through an Integrative Discovery Environment", @@ -1286,7 +1300,7 @@ async def test_core_schema_funding_funder_optional(core_data, core_model, includ ] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if include_funder: assert core_model_instance.funding[0].funder.type == "Organization" @@ -1303,20 +1317,19 @@ async def test_core_schema_funding_funder_optional(core_data, core_model, includ @pytest.mark.parametrize('include_citation', [True, False]) @pytest.mark.asyncio -async def test_core_schema_citation_optional(core_data, core_model, include_citation): +async def test_core_schema_citation_optional(core_metadata, generic_dataset_model, include_citation): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing citation is optional for the funding property. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("citation", None) + + core_metadata.pop("citation", None) if include_citation: - core_data["citation"] = ["Test citation"] + core_metadata["citation"] = ["Test citation"] # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if include_citation: assert core_model_instance.citation == ["Test citation"] else: @@ -1325,17 +1338,16 @@ async def test_core_schema_citation_optional(core_data, core_model, include_cita @pytest.mark.parametrize('include_publisher', [True, False]) @pytest.mark.asyncio -async def test_core_schema_publisher_optional(core_data, core_model, include_publisher): +async def test_core_schema_publisher_optional(core_metadata, generic_dataset_model, include_publisher): """Test that a core metadata pydantic model can be created from core metadata json. Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing - publisher is optional for the funding property. + publisher is optional. Note: This test does nat add a record to the database. """ - core_data = core_data - core_model = core_model - core_data.pop("publisher", None) + + core_metadata.pop("publisher", None) if include_publisher: - core_data["publisher"] = { + core_metadata["publisher"] = { "@type": "Organization", "name": "HydroShare", "url": "https://hydroshare.org", @@ -1343,7 +1355,7 @@ async def test_core_schema_publisher_optional(core_data, core_model, include_pub } # validate the data model - core_model_instance = await utils.validate_data_model(core_data, core_model) + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) if include_publisher: assert core_model_instance.publisher.type == "Organization" assert core_model_instance.publisher.name == "HydroShare" @@ -1351,3 +1363,24 @@ async def test_core_schema_publisher_optional(core_data, core_model, include_pub assert core_model_instance.publisher.address == "1167 Massachusetts Ave Suites 418 & 419, Arlington, MA 02476" else: assert core_model_instance.publisher is None + + +@pytest.mark.parametrize('include_additional_type', [True, False]) +@pytest.mark.asyncio +async def test_core_schema_additional_type_optional(core_metadata, generic_dataset_model, include_additional_type): + """Test that a core metadata pydantic model can be created from core metadata json. + Purpose of the test is to validate core metadata schema as defined by the pydantic model where we are testing + additionalType is optional. + Note: This test does nat add a record to the database. + """ + + core_metadata.pop("additionalType", None) + if include_additional_type: + core_metadata["additionalType"] = "NetCDF" + + # validate the data model + core_model_instance = await utils.validate_data_model(core_metadata, generic_dataset_model) + if include_additional_type: + assert core_model_instance.additionalType == "NetCDF" + else: + assert core_model_instance.additionalType is None diff --git a/tests/test_dataset_routes.py b/tests/test_dataset_routes.py index ef29b89..7759f64 100644 --- a/tests/test_dataset_routes.py +++ b/tests/test_dataset_routes.py @@ -9,13 +9,13 @@ @pytest.mark.asyncio async def test_create_dataset(client_test, dataset_data, test_user_access_token): - """Testing the dataset routes for post and get""" + """Testing the dataset routes (POST: api/catalog/dataset/generic, GET: api/catalog/dataset/generic/{record_id})""" # add a dataset record to the db - response = await client_test.post("api/catalog/dataset", json=dataset_data) + response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert response.status_code == 201 response_data = response.json() - record_id = response_data.pop('_id') + record_id = response_data.pop("_id") # adjust the temporal coverage dates for comparison if dataset_data["temporalCoverage"]["startDate"].endswith("Z"): @@ -53,14 +53,14 @@ async def test_create_dataset(client_test, dataset_data, test_user_access_token) assert user.submission(submission_id).repository is None assert user.submission(submission_id).repository_identifier is None # retrieve the record from the db - response = await client_test.get(f"api/catalog/dataset/{record_id}") + response = await client_test.get(f"api/catalog/dataset/generic/{record_id}") assert response.status_code == 200 @pytest.mark.parametrize('object_store_type', ['s3', 'minio']) @pytest.mark.asyncio async def test_create_dataset_s3(client_test, dataset_data, test_user_access_token, object_store_type): - """Testing the s3 dataset routes for post and get""" + """Testing the s3 dataset route (POST: api/catalog/dataset-s3/) for creating s3 generic metadata record""" if object_store_type == "minio": # set the path to the generic metadata file on minIO s3 @@ -74,7 +74,7 @@ async def test_create_dataset_s3(client_test, dataset_data, test_user_access_tok s3_path = { "path": "data/.hs/dataset_metadata.json", "bucket": "iguide-catalog", - "endpoint_url": "https://iguide-catalog.s3.us-west-2.amazonaws.com/", + "endpoint_url": "https://s3.us-west-2.amazonaws.com/", } payload = { @@ -96,7 +96,7 @@ async def test_create_dataset_s3(client_test, dataset_data, test_user_access_tok # retrieve the record from the db record_id = ds_metadata.pop('_id') - response = await client_test.get(f"api/catalog/dataset/{record_id}") + response = await client_test.get(f"api/catalog/dataset/generic/{record_id}") assert response.status_code == 200 # retrieve all submissions for the current user from the db submission_response = await client_test.get("api/catalog/submission") @@ -110,7 +110,7 @@ async def test_create_dataset_s3(client_test, dataset_data, test_user_access_tok @pytest.mark.parametrize('object_store_type', ['s3', 'minio']) @pytest.mark.asyncio async def test_update_dataset_s3(client_test, dataset_data, test_user_access_token, object_store_type): - """Testing the s3 dataset route (api/catalog/dataset-s3/) for put for updating s3 metadata record""" + """Testing the s3 dataset route (PUT: api/catalog/dataset-s3/) for updating s3 generic metadata record""" if object_store_type == "minio": # set the path to the generic metadata file on minIO s3 @@ -124,7 +124,7 @@ async def test_update_dataset_s3(client_test, dataset_data, test_user_access_tok s3_path = { "path": "data/.hs/dataset_metadata.json", "bucket": "iguide-catalog", - "endpoint_url": "https://iguide-catalog.s3.us-west-2.amazonaws.com/", + "endpoint_url": "https://s3.us-west-2.amazonaws.com/", } payload = { @@ -143,7 +143,7 @@ async def test_update_dataset_s3(client_test, dataset_data, test_user_access_tok assert ds_metadata["submission_type"] == SubmissionType.S3 # retrieve the record from the db record_id = ds_metadata.pop('_id') - response = await client_test.get(f"api/catalog/dataset/{record_id}") + response = await client_test.get(f"api/catalog/dataset/generic/{record_id}") assert response.status_code == 200 # update the dataset record @@ -160,7 +160,7 @@ async def test_update_dataset_s3(client_test, dataset_data, test_user_access_tok s3_path = { "path": "data/.hs/dataset_metadata-updated.json", "bucket": "iguide-catalog-updated", - "endpoint_url": "https://iguide-catalog-updated.s3.us-west-2.amazonaws.com/", + "endpoint_url": "https://s3.us-west-2.amazonaws.com/", } payload = { @@ -181,7 +181,7 @@ async def test_update_dataset_s3(client_test, dataset_data, test_user_access_tok assert ds_metadata["name"] == dataset_data['name'] # retrieve the record from the db record_id = ds_metadata.pop('_id') - response = await client_test.get(f"api/catalog/dataset/{record_id}") + response = await client_test.get(f"api/catalog/dataset/generic/{record_id}") assert response.status_code == 200 ds_metadata = response.json() assert ds_metadata["s3_path"] == s3_path @@ -197,20 +197,21 @@ async def test_update_dataset_s3(client_test, dataset_data, test_user_access_tok @pytest.mark.asyncio async def test_create_refresh_dataset_from_hydroshare(client_test, test_user_access_token): - """Testing catalog registration/refresh of hydroshare metadata record""" + """Testing catalog registration/refresh routes (GET: api/catalog/repository/hydroshare/{hs_res_id}, + PUT: api/catalog/repository/hydroshare/{hs_res_id}) of hydroshare metadata record""" # create hydroshare resource metadata as a catalog dataset record hs_published_res_id = "b5f58460941c49578e311adb9823657a" response = await client_test.get(f"api/catalog/repository/hydroshare/{hs_published_res_id}") - assert response.status_code == 200 + assert response.status_code == 201 hs_dataset = response.json() assert hs_dataset['repository_identifier'] == hs_published_res_id assert hs_dataset['submission_type'] == SubmissionType.HYDROSHARE await _check_hs_submission(hs_dataset, test_user_access_token, hs_published_res_id) # retrieve the record from the db - record_id = hs_dataset.get('_id') - response = await client_test.get(f"api/catalog/dataset/{record_id}") + record_id = hs_dataset.get("_id") + response = await client_test.get(f"api/catalog/dataset/hs-resource/{record_id}") assert response.status_code == 200 # refresh the hydroshare metadata record @@ -224,23 +225,30 @@ async def test_create_refresh_dataset_from_hydroshare(client_test, test_user_acc @pytest.mark.asyncio async def test_update_dataset(client_test, dataset_data): - """Testing the dataset put route for updating dataset record""" + """Testing the generic dataset route (PUT: api/catalog/dataset/generic) for updating dataset record""" # add a dataset record to the db - response = await client_test.post("api/catalog/dataset", json=dataset_data) + response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert response.status_code == 201 response_data = response.json() - record_id = response_data.get('_id') + record_id = response_data.get("_id") # update the dataset name - dataset_data['name'] = 'Updated title' + dataset_data["name"] = "Updated title" # remove citation - dataset_data['citation'] = [] + dataset_data["citation"] = [] # remove publisher - dataset_data['publisher'] = None + dataset_data["publisher"] = None # update the dataset temporal coverage - dataset_data["temporalCoverage"] = {"startDate": "2020-01-01T10:00:20", "endDate": "2020-11-29T00:30:00"} - response = await client_test.put(f"api/catalog/dataset/{record_id}", json=dataset_data) + dataset_data["temporalCoverage"] = { + "startDate": "2020-01-01T10:00:20", + "endDate": "2020-11-29T00:30:00", + } + + # this is the endpoint for updating the generic dataset record that we are testing + response = await client_test.put( + f"api/catalog/dataset/generic/{record_id}", json=dataset_data + ) assert response.status_code == 200 response_data = response.json() response_data.pop('_id') @@ -262,14 +270,15 @@ async def test_update_dataset(client_test, dataset_data): @pytest.mark.asyncio async def test_delete_dataset(client_test, dataset_data, test_user_access_token): - """Testing the dataset delete route for deleting a dataset record""" + """Testing the dataset delete route (DELETE: api/catalog/dataset/{record_id} for deleting a dataset record""" - # add a dataset record to the db - response = await client_test.post("api/catalog/dataset", json=dataset_data) + # add a generic dataset record to the db + response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert response.status_code == 201 response_data = response.json() - record_id = response_data.get('_id') - # delete the dataset record + record_id = response_data.get("_id") + + # delete the dataset record - this is the endpoint we are testing response = await client_test.delete(f"api/catalog/dataset/{record_id}") assert response.status_code == 200 # there should not be any submission records in the db @@ -285,16 +294,17 @@ async def test_delete_dataset(client_test, dataset_data, test_user_access_token) @pytest.mark.parametrize("multiple", [True, False]) @pytest.mark.asyncio async def test_get_datasets(client_test, dataset_data, multiple): - """Testing the get all datasets for a given user""" + """Testing the get all datasets route (GET: api/catalog/dataset) for a given user""" # add a dataset record to the db - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 if multiple: # add another dataset record to the db - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 + # this is the endpoint we are testing dataset_response = await client_test.get("api/catalog/dataset") assert dataset_response.status_code == 200 dataset_response_data = dataset_response.json() @@ -307,50 +317,19 @@ async def test_get_datasets(client_test, dataset_data, multiple): assert ds["submission_type"] == SubmissionType.IGUIDE_FORM -@pytest.mark.asyncio -async def test_get_datasets_2(client_test, dataset_data): - """Testing the get all datasets for a given user with different submission types""" - - # add a dataset record to the db - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) - assert dataset_response.status_code == 201 - # set the path to the generic metadata file on minIO s3 - s3_path = { - "path": "data/.hs/dataset_metadata.json", - "bucket": "catalog-api-test", - "endpoint_url": "https://api.minio.cuahsi.io/", - } - payload = { - "s3_path": s3_path, - "document": dataset_data - } - - response = await client_test.post("api/catalog/dataset-s3/", json=payload) - assert response.status_code == 201 - - # this is the endpoint we are testing - dataset_response = await client_test.get("api/catalog/dataset") - assert dataset_response.status_code == 200 - dataset_response_data = dataset_response.json() - assert len(dataset_response_data) == 2 - assert dataset_response_data[0]["submission_type"] == SubmissionType.IGUIDE_FORM - assert dataset_response_data[1]["submission_type"] == SubmissionType.S3 - assert dataset_response_data[1]["s3_path"] == s3_path - - @pytest.mark.asyncio async def test_get_datasets_different_submission_types(client_test, dataset_data): - """Testing the get all datasets for a given user""" + """Testing the get all datasets routes (GET: api/catalog/dataset) of different types for a given user""" # add a dataset record to the db simulation iGuide form submission - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 # add a dataset record to the db simulation S3 submission s3_path = { "path": "data/.hs/dataset_metadata.json", "bucket": "iguide-catalog", - "endpoint_url": "https://iguide-catalog.s3.us-west-2.amazonaws.com/", + "endpoint_url": "https://s3.us-west-2.amazonaws.com/", } payload = { @@ -364,7 +343,7 @@ async def test_get_datasets_different_submission_types(client_test, dataset_data # add a dataset record to the db simulation HydroShare submission hs_published_res_id = "b5f58460941c49578e311adb9823657a" response = await client_test.get(f"api/catalog/repository/hydroshare/{hs_published_res_id}") - assert response.status_code == 200 + assert response.status_code == 201 hs_dataset = response.json() assert hs_dataset['repository_identifier'] == hs_published_res_id assert hs_dataset['submission_type'] == SubmissionType.HYDROSHARE @@ -386,14 +365,16 @@ async def test_get_datasets_exclude_none(client_test, dataset_data): dataset_data["version"] = None # add a dataset record to the db - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 + response_data = dataset_response.json() + record_id = response_data.get("_id") - dataset_response = await client_test.get("api/catalog/dataset") + dataset_response = await client_test.get(f"api/catalog/dataset/generic/{record_id}") assert dataset_response.status_code == 200 dataset_response_data = dataset_response.json() - assert "version" not in dataset_response_data[0] - for a_property in dataset_response_data[0]["additionalProperty"]: + assert "version" not in dataset_response_data + for a_property in dataset_response_data["additionalProperty"]: assert "description" not in a_property assert "minValue" not in a_property assert "maxValue" not in a_property @@ -402,18 +383,100 @@ async def test_get_datasets_exclude_none(client_test, dataset_data): assert "measurementTechnique" not in a_property +@pytest.mark.asyncio +async def test_register_minio_s3_generic_dataset(client_test): + """Testing registering metadata route (POST: api/catalog/repository/s3/generic) for a generic dataset + stored on minIO s3""" + + # set the path to the generic metadata file on minIO s3 + s3_path = { + "path": "data/.hs/dataset_metadata.json", + "bucket": "catalog-api-test", + "endpoint_url": "https://api.minio.cuahsi.io/", + } + + dataset_response = await client_test.post( + "api/catalog/repository/s3/generic", json=s3_path + ) + assert dataset_response.status_code == 201 + ds_metadata = dataset_response.json() + expected_repository_identifier = f"{s3_path['endpoint_url']}{s3_path['bucket']}/{s3_path['path']}" + assert ds_metadata["repository_identifier"] == expected_repository_identifier + assert ds_metadata["submission_type"] == SubmissionType.S3 + assert ds_metadata["s3_path"] == s3_path + + # retrieve the record from the db + record_id = ds_metadata.get('_id') + response = await client_test.get(f"api/catalog/dataset/generic/{record_id}") + assert response.status_code == 200 + + +@pytest.mark.asyncio +async def test_register_minio_s3_netcdf_dataset(client_test): + """Testing registering metadata route (POST: api/catalog/repository/s3/netcdf) for a netcdf dataset + stored on minIO""" + + # set the path to the netcdf file on s3 (minIO) + s3_path = { + "path": "data/.hs/netcdf/netcdf_valid.nc.json", + "bucket": "catalog-api-test", + "endpoint_url": "https://api.minio.cuahsi.io/", + } + + dataset_response = await client_test.post("api/catalog/repository/s3/netcdf", json=s3_path) + assert dataset_response.status_code == 201 + ds_metadata = dataset_response.json() + assert ds_metadata["additionalType"] == "NetCDF" + expected_repository_identifier = f"{s3_path['endpoint_url']}{s3_path['bucket']}/{s3_path['path']}" + assert ds_metadata["repository_identifier"] == expected_repository_identifier + assert ds_metadata["submission_type"] == SubmissionType.S3 + assert ds_metadata["s3_path"] == s3_path + + # retrieve the record from the db + record_id = ds_metadata.get('_id') + response = await client_test.get(f"api/catalog/dataset/netcdf/{record_id}") + assert response.status_code == 200 + + +@pytest.mark.asyncio +async def test_register_aws_s3_netcdf_dataset(client_test): + """Testing registering metadata route (POST: api/catalog/repository/s3/netcdf) for a netcdf dataset on AWS s3""" + + # set the path to the netcdf file on amazon s3 + s3_path = { + "path": "data/.hs/netcdf/netcdf_valid.nc.json", + "bucket": "iguide-catalog", + "endpoint_url": "https://s3.us-west-2.amazonaws.com/", + } + + dataset_response = await client_test.post( + "api/catalog/repository/s3/netcdf", json=s3_path + ) + assert dataset_response.status_code == 201 + ds_metadata = dataset_response.json() + assert ds_metadata["additionalType"] == "NetCDF" + assert ds_metadata["repository_identifier"] == f"{s3_path['endpoint_url']}{s3_path['path']}" + assert ds_metadata["submission_type"] == SubmissionType.S3 + assert ds_metadata["s3_path"] == s3_path + + # retrieve the record from the db + record_id = ds_metadata.get('_id') + response = await client_test.get(f"api/catalog/dataset/netcdf/{record_id}") + assert response.status_code == 200 + + @pytest.mark.parametrize("multiple", [True, False]) @pytest.mark.asyncio async def test_get_submissions_1(client_test, dataset_data, multiple): - """Testing the get submissions route with one submission type only""" + """Testing the get submissions route (GET: api/catalog/dataset/generic) with one submission type only""" # add a dataset record to the db - this will also add a submission record - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 dataset_response_data = dataset_response.json() if multiple: # add another dataset record to the db - this will also add a submission record - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 dataset_response_data = [dataset_response_data, dataset_response.json()] @@ -432,26 +495,31 @@ async def test_get_submissions_1(client_test, dataset_data, multiple): assert submission_response_data[1]['url'] == dataset_response_data[1]['url'] assert submission_response_data[0]['repository'] is None assert submission_response_data[1]['repository'] is None + assert submission_response_data[0]['s3_path'] is None + assert submission_response_data[1]['s3_path'] is None else: assert len(submission_response_data) == 1 assert submission_response_data[0]['title'] == dataset_response_data['name'] assert submission_response_data[0]['identifier'] == dataset_response_data['_id'] assert submission_response_data[0]['url'] == dataset_response_data['url'] assert submission_response_data[0]['repository'] is None + assert submission_response_data[0]['s3_path'] is None @pytest.mark.asyncio async def test_get_submissions_2(client_test, dataset_data): - """Testing the get submissions route with different submission types""" + """Testing the get submissions route (GET: api/catalog/dataset/generic) with different submission types""" # add a dataset record to the db - this will also add a submission record - dataset_response = await client_test.post("api/catalog/dataset", json=dataset_data) + dataset_response = await client_test.post("api/catalog/dataset/generic", json=dataset_data) assert dataset_response.status_code == 201 dataset_response_data = dataset_response.json() + + # add a dataset record to the db simulation S3 submission s3_path = { "path": "data/.hs/dataset_metadata.json", "bucket": "iguide-catalog", - "endpoint_url": "https://iguide-catalog.s3.us-west-2.amazonaws.com/", + "endpoint_url": "https://s3.us-west-2.amazonaws.com/", } payload = { @@ -466,7 +534,7 @@ async def test_get_submissions_2(client_test, dataset_data): # add a dataset record to the db simulation HydroShare submission hs_published_res_id = "b5f58460941c49578e311adb9823657a" dataset_response = await client_test.get(f"api/catalog/repository/hydroshare/{hs_published_res_id}") - assert dataset_response.status_code == 200 + assert dataset_response.status_code == 201 dataset_response_data = dataset_response_data + [dataset_response.json()] # retrieve all submissions for the current user from the db submission_response = await client_test.get("api/catalog/submission") diff --git a/tests/test_dataset_schema.py b/tests/test_dataset_schema.py index 015d0b9..7062c0f 100644 --- a/tests/test_dataset_schema.py +++ b/tests/test_dataset_schema.py @@ -6,7 +6,7 @@ @pytest.mark.parametrize("set_additional_property", [True, False]) @pytest.mark.asyncio async def test_dataset_schema_additional_property( - dataset_data, dataset_model, set_additional_property + dataset_data, generic_dataset_model, set_additional_property ): """Test that a dataset metadata pydantic model can be created from dataset metadata json. Purpose of the test is to validate dataset metadata schema as defined by the pydantic model where we are testing @@ -14,8 +14,6 @@ async def test_dataset_schema_additional_property( Note: This test does not add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model additional_property = [ { "@type": "PropertyValue", @@ -39,7 +37,7 @@ async def test_dataset_schema_additional_property( dataset_data.pop("additionalProperty", None) # validate the data model - dataset_model_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_model_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) if set_additional_property: assert len(dataset_model_instance.additionalProperty) == 2 assert dataset_model_instance.additionalProperty[0].name == additional_property[0]["name"] @@ -61,7 +59,7 @@ async def test_dataset_schema_additional_property( @pytest.mark.parametrize("set_source_organization", [True, False]) @pytest.mark.asyncio async def test_dataset_schema_source_organization( - dataset_data, dataset_model, set_source_organization + dataset_data, generic_dataset_model, set_source_organization ): """Test that a dataset metadata pydantic model can be created from dataset metadata json. Purpose of the test is to validate dataset metadata schema as defined by the pydantic model where we are testing @@ -69,8 +67,6 @@ async def test_dataset_schema_source_organization( Note: This test does not add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model source_organization = { "@type": "Organization", "name": "National Hydrography Dataset", @@ -83,7 +79,7 @@ async def test_dataset_schema_source_organization( dataset_data.pop("sourceOrganization", None) # validate the data model - dataset_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) if set_source_organization: assert dataset_instance.sourceOrganization.type == source_organization["@type"] assert dataset_instance.sourceOrganization.name == source_organization["name"] @@ -95,15 +91,13 @@ async def test_dataset_schema_source_organization( @pytest.mark.parametrize("multiple_variable", [True, False, None]) @pytest.mark.asyncio async def test_dataset_schema_variable_cardinality( - dataset_data, dataset_model, multiple_variable + dataset_data, generic_dataset_model, multiple_variable ): """Test that a dataset pydantic model can be created from dataset json data. Purpose of the test is to validate dataset pydantic model where the variableMeasured property can have 0 or more values. Note: This test does nat add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model if multiple_variable and multiple_variable is not None: dataset_data["variableMeasured"] = [ @@ -133,7 +127,7 @@ async def test_dataset_schema_variable_cardinality( dataset_data["variableMeasured"] = [] # validate the dataset model - dataset_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) # checking dataset specific metadata if multiple_variable and multiple_variable is not None: assert len(dataset_instance.variableMeasured) == 2 @@ -172,18 +166,17 @@ async def test_dataset_schema_variable_cardinality( ) @pytest.mark.asyncio async def test_dataset_schema_variable_value_type( - dataset_data, dataset_model, data_format + dataset_data, generic_dataset_model, data_format ): """Test that a dataset pydantic model can be created from dataset json data. Purpose of the test is to validate dataset pydantic model where we are testing allowed value types for the variableMeasured property. Note: This test does nat add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model + dataset_data["variableMeasured"] = data_format # validate the data model - dataset_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) # checking dataset specific metadata if isinstance(data_format[0], dict): assert dataset_instance.variableMeasured[0].type == "PropertyValue" @@ -203,15 +196,14 @@ async def test_dataset_schema_variable_value_type( @pytest.mark.parametrize("multiple_distribution", [True, False]) @pytest.mark.asyncio async def test_dataset_schema_distribution_cardinality( - dataset_data, dataset_model, multiple_distribution + dataset_data, generic_dataset_model, multiple_distribution ): """Test that a dataset pydantic model can be created from dataset json data. Purpose of the test is to validate dataset pydantic model where we are testing the distribution property can have one or more values. Note: This test does nat add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model + if multiple_distribution: dataset_data["distribution"] = [ { @@ -238,7 +230,7 @@ async def test_dataset_schema_distribution_cardinality( "encodingFormat": "text/csv", } - dataset_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) # checking dataset specific metadata if multiple_distribution: assert len(dataset_instance.distribution) == 2 @@ -310,18 +302,17 @@ async def test_dataset_schema_distribution_cardinality( ) @pytest.mark.asyncio async def test_dataset_schema_distribution_value_type( - dataset_data, dataset_model, data_format + dataset_data, generic_dataset_model, data_format ): """Test that a dataset pydantic model can be created from dataset json data. Purpose of the test is to validate dataset pydantic model where we are testing allowed value types for distribution property. Note: This test does nat add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model + dataset_data["distribution"] = data_format # validate the data model - dataset_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) # checking dataset specific metadata assert dataset_instance.distribution.type == "DataDownload" assert dataset_instance.distribution.name == "Fiber_opticdist.zip" @@ -349,15 +340,14 @@ async def test_dataset_schema_distribution_value_type( @pytest.mark.parametrize("multiple_data_catalog", [True, False]) @pytest.mark.asyncio async def test_dataset_schema_data_catalog_cardinality( - dataset_data, dataset_model, multiple_data_catalog + dataset_data, generic_dataset_model, multiple_data_catalog ): """Test that a dataset pydantic model can be created from dataset json data. Purpose of the test is to validate dataset pydantic model where the includedInDataCatalog property can have one or more values. Note: This test does nat add a record to the database. """ - dataset_data = dataset_data - dataset_model = dataset_model + if multiple_data_catalog: dataset_data["includedInDataCatalog"] = [ { @@ -401,7 +391,7 @@ async def test_dataset_schema_data_catalog_cardinality( } ] # validate the dataset model - dataset_instance = await utils.validate_data_model(dataset_data, dataset_model) + dataset_instance = await utils.validate_data_model(dataset_data, generic_dataset_model) # checking dataset specific metadata if multiple_data_catalog: assert len(dataset_instance.includedInDataCatalog) == 2 diff --git a/tests/test_hydroshare_meta_adapter.py b/tests/test_hydroshare_meta_adapter.py index 6865198..8568658 100644 --- a/tests/test_hydroshare_meta_adapter.py +++ b/tests/test_hydroshare_meta_adapter.py @@ -3,12 +3,12 @@ from pydantic import ValidationError from api.adapters.utils import RepositoryType, get_adapter_by_type -from api.models.catalog import DatasetMetadataDOC +from api.models.catalog import HSResourceMetadataDOC @pytest.mark.parametrize('coverage_type', ["box", "point"]) @pytest.mark.asyncio -async def test_hydroshare_resource_meta_adapter(hydroshare_resource_metadata, coverage_type, dataset_model): +async def test_hydroshare_resource_meta_adapter(hydroshare_resource_metadata, coverage_type, hs_resource_model): """Test the HydroshareMetaAdapter for Composite Resource""" adapter = get_adapter_by_type(RepositoryType.HYDROSHARE) @@ -18,13 +18,13 @@ async def test_hydroshare_resource_meta_adapter(hydroshare_resource_metadata, co "units": "Decimal degrees", "projection": "Unknown"} - dataset = adapter.to_catalog_record(hydroshare_resource_metadata) + dataset = adapter.to_catalog_record(hydroshare_resource_metadata, type(hs_resource_model)) try: - dataset_model(**dataset.dict()) + hs_resource_model(**dataset.dict()) except ValidationError as err: pytest.fail(f"Catalog dataset schema model validation failed: {str(err)}") - assert isinstance(dataset, DatasetMetadataDOC) + assert isinstance(dataset, HSResourceMetadataDOC) assert dataset.name == "Testing IGUIDE Metadata Adapter for Hydroshare Resource" assert dataset.description == "This is a test resource - abstract" assert dataset.url == "http://www.hydroshare.org/resource/1ee81318135c40f587d9a3e5d689daf5" @@ -101,17 +101,17 @@ async def test_hydroshare_resource_meta_adapter(hydroshare_resource_metadata, co @pytest.mark.asyncio -async def test_hydroshare_collection_meta_adapter(hydroshare_collection_metadata, dataset_model): +async def test_hydroshare_collection_meta_adapter(hydroshare_collection_metadata, hs_resource_model): """Test the HydroshareMetaAdapter for Collection Resource""" adapter = get_adapter_by_type(RepositoryType.HYDROSHARE) - dataset = adapter.to_catalog_record(hydroshare_collection_metadata) + dataset = adapter.to_catalog_record(hydroshare_collection_metadata, type(hs_resource_model)) try: - dataset_model(**dataset.dict()) + hs_resource_model(**dataset.dict()) except ValidationError as err: pytest.fail(f"Catalog dataset schema model validation failed: {str(err)}") - assert isinstance(dataset, DatasetMetadataDOC) + assert isinstance(dataset, HSResourceMetadataDOC) assert dataset.isPartOf == [] assert len(dataset.hasPart) == 3 assert dataset.hasPart[0].description == "Tarboton, D. (2019). Created from iRODS by copy from create resource page, HydroShare" diff --git a/tests/test_netcdf_schema.py b/tests/test_netcdf_schema.py new file mode 100644 index 0000000..08ffdc6 --- /dev/null +++ b/tests/test_netcdf_schema.py @@ -0,0 +1,23 @@ +import pytest + +from tests import utils + + +@pytest.mark.asyncio +async def test_netcdf_schema(netcdf_metadata, netcdf_metadata_model): + """Test that a netcdf metadata pydantic model can be created from netcdf metadata json. + Purpose of the test is to validate netcdf metadata schema as defined by the pydantic model. Note: This test does nat + add a record to the database. + """ + + # remove additionalType field + netcdf_metadata.pop("additionalType") + # validate the data model + name = "Snow water equivalent estimation at TWDEF site from Oct 2009 to June 2010" + description = ("This netCDF data is the simulation output from Utah Energy Balance (UEB) model.It includes " + "the simulation result of snow water equivalent during the period Oct. 2009 to June 2010 " + "for TWDEF site in Utah.") + netcdf_model_instance = await utils.validate_data_model(netcdf_metadata, netcdf_metadata_model, name, description) + + assert netcdf_model_instance.name == name + assert netcdf_model_instance.additionalType == "NetCDF" diff --git a/tests/test_raster_schema.py b/tests/test_raster_schema.py new file mode 100644 index 0000000..7c05035 --- /dev/null +++ b/tests/test_raster_schema.py @@ -0,0 +1,21 @@ +import pytest + +from tests import utils + + +@pytest.mark.asyncio +async def test_raster_schema(raster_metadata, raster_metadata_model): + """Test that a raster metadata pydantic model can be created from netcdf metadata json. + Purpose of the test is to validate raster metadata schema as defined by the pydantic model. Note: This test does nat + add a record to the database. + """ + + # remove additionalType field + raster_metadata.pop("additionalType") + # validate the data model + name = "Logan" + description = None + raster_model_instance = await utils.validate_data_model(raster_metadata, raster_metadata_model, name, description) + + assert raster_model_instance.name == name + assert raster_model_instance.additionalType == "Geo Raster" diff --git a/tests/utils.py b/tests/utils.py index 569c3d5..18286b7 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,14 +2,21 @@ from pydantic import ValidationError -async def validate_data_model(data, model_class): +async def validate_data_model( + data, + model_class, + name="Test Dataset", + description="This is a test dataset metadata json file to test catalog api" +): """Helper function to validate pydantic schema data models.""" try: model_instance = model_class(**data) except ValidationError as e: pytest.fail(f"Schema model validation failed: {str(e)}") + # checking some core metadata - assert model_instance.name == "Test Dataset" - assert model_instance.description == "This is a test dataset metadata json file to test catalog api" + assert model_instance.name == name + assert model_instance.description == description + return model_instance diff --git a/triggers/scheduler.py b/triggers/scheduler.py index 876da7e..e577519 100644 --- a/triggers/scheduler.py +++ b/triggers/scheduler.py @@ -8,7 +8,7 @@ from api.adapters.utils import RepositoryType, get_adapter_by_type from api.config import get_settings -from api.models.catalog import DatasetMetadataDOC +from api.models.catalog import CoreMetadataDOC from api.models.user import Submission app = Rocketry(config={"task_execution": "async"}) @@ -42,14 +42,14 @@ async def retrieve_repository_record(submission: Submission): async def do_daily(): settings = get_settings() db = AsyncIOMotorClient(settings.db_connection_string)[settings.database_name] - await init_beanie(database=db, document_models=[Submission, DatasetMetadataDOC]) + await init_beanie(database=db, document_models=[Submission, CoreMetadataDOC]) async for submission in Submission.find(Submission.repository != None): if submission.repository == RepositoryType.S3: # skip S3 submissions as they are not yet supported for scheduled refresh continue try: - dataset = await DatasetMetadataDOC.get(submission.identifier) + dataset = await CoreMetadataDOC.get(submission.identifier, with_children=True) if dataset is None: logger.warning(f"No catalog record was found for submission: {submission.identifier}") continue @@ -61,7 +61,7 @@ async def do_daily(): await updated_dataset.replace() # update submission record - dataset = await DatasetMetadataDOC.get(submission.identifier) + dataset = await CoreMetadataDOC.get(submission.identifier, with_children=True) updated_submission = dataset.as_submission() updated_submission.id = submission.id updated_submission.submitted = submission.submitted