Skip to content

Commit

Permalink
Merge pull request #29 from NII-DG/develop
Browse files Browse the repository at this point in the history
Revised code and docs based on previous example and demo
  • Loading branch information
suecharo committed May 15, 2023
2 parents f00ede8 + 6fccdeb commit 42b08c7
Show file tree
Hide file tree
Showing 25 changed files with 256 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ FROM python:3.8.15-slim-buster
LABEL org.opencontainers.image.authors="National Institute of Informatics, Japan"
LABEL org.opencontainers.image.url="https://github.com/NII-DG/nii-dg"
LABEL org.opencontainers.image.source="https://raw.githubusercontent.com/NII-DG/nii-dg/main/Dockerfile"
LABEL org.opencontainers.image.version="0.1.1"
LABEL org.opencontainers.image.version="0.1.2"
LABEL org.opencontainers.image.licenses="Apache2.0"

RUN apt update && \
Expand Down
2 changes: 1 addition & 1 deletion compose.api.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3.5"
services:
app:
image: ghcr.io/NII-DG/nii-dg:0.1.1
image: ghcr.io/NII-DG/nii-dg:0.1.2
container_name: nii-dg
volumes:
- ${PWD}:/app
Expand Down
2 changes: 1 addition & 1 deletion compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3.5"
services:
app:
image: ghcr.io/NII-DG/nii-dg:0.1.1
image: ghcr.io/NII-DG/nii-dg:0.1.2
container_name: nii-dg
volumes:
- ${PWD}:/app
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
author = 'NII'

# The short X.Y version
version = '0.1.1'
version = '0.1.2'
# The full version, including alpha/beta/rc tags
release = '0.1.1'
release = '0.1.2'


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion nii_dg/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def get_results(request_id: str) -> Response:
results = job.result()
elif isinstance(job.exception(), CrateValidationError):
status = "FAILED"
results = result_wrapper(job.exception().entity_errors) # type:ignore
results = result_wrapper(job.exception().errors) # type:ignore
else:
status = "EXECUTOR_ERROR"
results = [{"err_msg": str(job.exception())}]
Expand Down
2 changes: 1 addition & 1 deletion nii_dg/check_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def is_url_accessible(url: str) -> bool:
bool: True if the URL is accessible, False otherwise.
"""
try:
req = Request(url, method='HEAD')
req = Request(url, method="HEAD")
res = urlopen(req)
return res.status < 400 # type: ignore
except Exception:
Expand Down
2 changes: 1 addition & 1 deletion nii_dg/module_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
GH_REPO: str = "NII-DG/nii-dg"
"""str: The GitHub repository name for the nii_dg package."""

GH_REF: str = "0.1.1"
GH_REF: str = "0.1.2"
"""str: The GitHub reference (tag or branch) for the nii_dg package."""


Expand Down
12 changes: 6 additions & 6 deletions nii_dg/ro_crate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import json
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Type, Union

from nii_dg.const import DOWNLOADED_SCHEMA_DIR_NAME, RO_CRATE_CONTEXT
from nii_dg.entity import (ContextualEntity, DataEntity, DefaultEntity, Entity,
Expand Down Expand Up @@ -140,7 +140,7 @@ def get_by_id(self, id_: str) -> List[Entity]:
"""
return [entity for entity in self.all_entities if entity.id == id_]

def get_by_type(self, type_: str) -> List[Entity]:
def get_by_type(self, type_: Type[Entity]) -> List[Entity]:
"""\
Get entities by type.
Expand All @@ -150,9 +150,9 @@ def get_by_type(self, type_: str) -> List[Entity]:
Returns:
A list of entities with the specified type.
"""
return [entity for entity in self.all_entities if entity.type == type_]
return [entity for entity in self.all_entities if type(entity) == type_]

def get_by_id_and_type(self, id_: str, type_: str) -> List[Entity]:
def get_by_id_and_type(self, id_: str, type_: Type[Entity]) -> List[Entity]:
"""\
Get entities by ID and type.
Expand All @@ -163,7 +163,7 @@ def get_by_id_and_type(self, id_: str, type_: str) -> List[Entity]:
Returns:
A list of entities with the specified ID and type.
"""
return [entity for entity in self.all_entities if entity.id == id_ and entity.type == type_]
return [entity for entity in self.all_entities if entity.id == id_ and type(entity) == type_]

def from_jsonld(self, jsonld: Dict[str, Any]) -> None:
"""\
Expand Down Expand Up @@ -250,7 +250,7 @@ def as_jsonld(self) -> Dict[str, Any]:
"@graph": [entity.as_jsonld() for entity in self.all_entities]
}

def dump(self, path: str) -> None:
def dump(self, path: Union[str, Path]) -> None:
"""\
Dump the RO-Crate to a file.
Expand Down
6 changes: 3 additions & 3 deletions nii_dg/schema/amed.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def validate(self, crate: "ROCrate") -> None:
error.add("hostingInstitution", "The hostingInstitution property is required when the hasPart property is not empty.")
if "dataManager" not in self:
error.add("dataManager", "The dataManager property is required when the hasPart property is not empty.")
if len(self["hasPart"]) > len(crate.get_by_type("DMP")):
if len(self["hasPart"]) > len(crate.get_by_type(DMP)):
error.add("hasPart", "The number of the hasPart property MUST be equal to the number of DMP entities.")

if error.has_error():
Expand Down Expand Up @@ -89,7 +89,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

dmp_metadata_ents = crate.get_by_type("DMPMetadata")
dmp_metadata_ents = crate.get_by_type(DMPMetadata)
if len(dmp_metadata_ents) == 0:
error.add("AnotherEntity", "Entity `DMPMetadata` MUST be required with DMP entity.")
else:
Expand All @@ -113,7 +113,7 @@ def validate(self, crate: "ROCrate") -> None:

if "contentSize" in self:
target_files = []
for ent in crate.get_by_type("File"):
for ent in crate.get_by_type(File):
if ent["dmpDataNumber"] == self:
target_files.append(ent)

Expand Down
8 changes: 4 additions & 4 deletions nii_dg/schema/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def validate(self, crate: "ROCrate") -> None:
if self["name"] not in name_list:
error.add("name", f"The name MUST be one of {name_list} registered in ROR.")
else:
if is_url_accessible(self.id):
if not is_url_accessible(self.id):
error.add("@id", "Failed to access the URL.")

if error.has_error():
Expand Down Expand Up @@ -155,7 +155,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

if is_url_accessible(self.id):
if not is_url_accessible(self.id):
error.add("@id", "Failed to access the URL.")

if error.has_error():
Expand Down Expand Up @@ -183,7 +183,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

if is_url_accessible(self.id):
if not is_url_accessible(self.id):
error.add("@id", "Failed to access the URL.")

if error.has_error():
Expand Down Expand Up @@ -230,7 +230,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

if is_url_accessible(self.id):
if not is_url_accessible(self.id):
error.add("@id", "Failed to access the URL.")

if error.has_error():
Expand Down
8 changes: 4 additions & 4 deletions nii_dg/schema/cao.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def validate(self, crate: "ROCrate") -> None:

if self["about"] != crate.root and self["about"] != {"@id": "./"}:
error.add("about", "The value of the about property MUST be the RootDataEntity of this crate.")
if len(self["hasPart"]) != len(crate.get_by_type("DMP")):
if len(self["hasPart"]) != len(crate.get_by_type(DMP)):
error.add("hasPart", "The number of the hasPart property MUST be equal to the number of DMP entities.")

if error.has_error():
Expand Down Expand Up @@ -83,7 +83,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

dmp_metadata_ents = crate.get_by_type("DMPMetadata")
dmp_metadata_ents = crate.get_by_type(DMPMetadata)
if len(dmp_metadata_ents) == 0:
error.add("AnotherEntity", "Entity `DMPMetadata` MUST be required with DMP entity.")
else:
Expand All @@ -108,7 +108,7 @@ def validate(self, crate: "ROCrate") -> None:

if "contentSize" in self:
target_files = []
for ent in crate.get_by_type("File"):
for ent in crate.get_by_type(File):
if ent["dmpDataNumber"] == self:
target_files.append(ent)

Expand Down Expand Up @@ -148,7 +148,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

if is_url_accessible(self.id) is False:
if not is_url_accessible(self.id):
error.add("@id", "The value MUST be a valid URL.")

if error.has_error():
Expand Down
6 changes: 3 additions & 3 deletions nii_dg/schema/ginfork.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from nii_dg.check_functions import is_absolute_path, is_url
from nii_dg.entity import ContextualEntity, EntityDef
from nii_dg.error import EntityError
from nii_dg.schema.base import Dataset
from nii_dg.schema.base import File as BaseFile
from nii_dg.utils import load_schema_file, sum_file_size

Expand Down Expand Up @@ -47,14 +48,13 @@ def validate(self, crate: "ROCrate") -> None:
error.add(
"about", "The value of this property MUST be the RootDataEntity of this crate.")

targets = [ent for ent in crate.get_by_type(
"File") if ent["experimentPackageFlag"] is True]
targets = [ent for ent in crate.get_by_type(File) if ent["experimentPackageFlag"] is True]
sum_size = sum_file_size(self["contentSize"][-2:], targets)
if sum_size > int(self["contentSize"][:-2]):
error.add(
"contentSize", "The total file size of ginfork.File labeled as an experimental package is larger than the defined size.")

dir_paths = [Path(dir_.id) for dir_ in crate.get_by_type("Dataset")]
dir_paths = [Path(dir_.id) for dir_ in crate.get_by_type(Dataset)]
required_dirs = [Path(experiment_dir).joinpath(required_dir_name)
for experiment_dir in self["experimentPackageList"]
for required_dir_name in self.REQUIRED_DIRECTORIES[self["datasetStructure"]]]
Expand Down
8 changes: 4 additions & 4 deletions nii_dg/schema/meti.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def validate(self, crate: "ROCrate") -> None:

if self["about"] != crate.root and self["about"] != {"@id": "./"}:
error.add("about", "The value of this property MUST be the RootDataEntity of this crate.")
if len(self["hasPart"]) != len(crate.get_by_type("DMP")):
if len(self["hasPart"]) != len(crate.get_by_type(DMP)):
diff = []
for dmp in crate.get_by_type("DMP"):
for dmp in crate.get_by_type(DMP):
if dmp not in self["hasPart"]:
diff.append(dmp)
error.add("hasPart", f"There is an omission of DMP entity in the list: {diff}.")
Expand Down Expand Up @@ -85,7 +85,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

dmp_metadata_ents = crate.get_by_type("DMPMetadata")
dmp_metadata_ents = crate.get_by_type(DMPMetadata)
if len(dmp_metadata_ents) == 0:
error.add("AnotherEntity", "Entity 'DMPMetadata' MUST be required with DMP entity.")
else:
Expand Down Expand Up @@ -121,7 +121,7 @@ def validate(self, crate: "ROCrate") -> None:

if "contentSize" in self:
target_files = []
for ent in crate.get_by_type("File"):
for ent in crate.get_by_type(File):
if ent["dmpDataNumber"] == self:
target_files.append(ent)

Expand Down
21 changes: 10 additions & 11 deletions nii_dg/schema/sapporo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import tempfile
import time
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, List, Optional
from typing import TYPE_CHECKING, Any, Dict, List
from urllib.parse import urlencode
from urllib.request import Request, urlopen

Expand Down Expand Up @@ -57,7 +57,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

sapporo_run_ents = crate.get_by_type("SapporoRun")
sapporo_run_ents = crate.get_by_type(SapporoRun)
if len(sapporo_run_ents) == 0:
error.add("AnotherEntity", "Entity `SapporoRun` MUST be required with sapporo.File entity.")

Expand All @@ -78,7 +78,7 @@ def check_props(self) -> None:

if not self.id.endswith("/"):
error.add("@id", "The value MUST end with '/'.")
if is_relative_path(self.id):
if not is_relative_path(self.id):
error.add("@id", "The value MUST be relative path to the directory, neither absolute path nor URL.")

if error.has_error():
Expand All @@ -89,7 +89,7 @@ def validate(self, crate: "ROCrate") -> None:

error = EntityError(self)

sapporo_run_ents = crate.get_by_type("SapporoRun")
sapporo_run_ents = crate.get_by_type(SapporoRun)
if len(sapporo_run_ents) == 0:
error.add("AnotherEntity", "Entity `SapporoRun` MUST be required with sapporo.File entity.")

Expand Down Expand Up @@ -119,22 +119,21 @@ def check_props(self) -> None:
raise error

@classmethod
def generate_run_request_json(cls, sapporo_run: "SapporoRun") -> Dict[str, Optional[str]]:
def generate_run_request_json(cls, sapporo_run: "SapporoRun") -> Dict[str, str]:
request_keys = ["workflow_params", "workflow_type", "workflow_type_version", "tags", "workflow_engine_name",
"workflow_engine_parameters", "workflow_url", "workflow_name", "workflow_attachment"]
run_request: Dict[str, Optional[str]] = {key: None for key in request_keys}
run_request: Dict[str, str] = {}
for key in request_keys:
if key in sapporo_run:
run_request[key] = sapporo_run[key]

return run_request

@classmethod
def execute_wf(cls, run_request: Dict[str, Optional[str]], endpoint: str) -> str:
def execute_wf(cls, run_request: Dict[str, str], endpoint: str) -> str:
data = urlencode(run_request).encode("utf-8") # type: ignore
headers = {"Content-Type": "application/x-www-form-urlencoded"}
# remove trailing slash from endpoint
request = Request(f"{endpoint.rstrip('/')}/runs", data=data, headers=headers) # type: ignore
request = Request(f"{endpoint.rstrip('/')}/runs", data=data) # type: ignore

with urlopen(request) as response:
result = json.load(response)
Expand Down Expand Up @@ -210,7 +209,7 @@ def validate(self, crate: "ROCrate") -> None:
raise error

if isinstance(self["outputs"], dict):
outputs = crate.get_by_id_and_type(self["outputs"]["@id"], "Dataset")
outputs = crate.get_by_id_and_type(self["outputs"]["@id"], Dataset)
if len(outputs) > 0:
self["outputs"] = outputs[0]
else:
Expand All @@ -219,7 +218,7 @@ def validate(self, crate: "ROCrate") -> None:
outputs_entities: List[Entity] = []
for ent in self["outputs"]["hasPart"]:
if isinstance(ent, dict):
file = crate.get_by_id_and_type(ent["@id"], "File")
file = crate.get_by_id_and_type(ent["@id"], File)
if len(file) > 0:
outputs_entities.append(file[0])
else:
Expand Down
3 changes: 2 additions & 1 deletion sapporo_example/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ services:
nii-dg:
build:
context: ..
dockerfile: Dockerfile-api
dockerfile: Dockerfile
image: nii-dg
container_name: nii-dg
ports:
- 0.0.0.0:5000:5000
restart: on-failure
command: [ "python", "/app/nii_dg/api.py" ]
networks:
- nii-dg-network
sapporo-service:
Expand Down
4 changes: 2 additions & 2 deletions sapporo_example/sapporo_use_case.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ $ curl localhost:5000/32012249-d8f7-4f64-b20d-853ea5be67b5
"results": [
{
"entityId": "#sapporo-run",
"props": "sapporo.SapporoRun:outputs_ERR034597_1.small_fastqc.html",
"reason": "The file ERR034597_1.small_fastqc.html is included in the result of re-execution, so File entity with @id ERR034597_1.small_fastqc.html is required in this crate."
"props": "sapporo.SapporoRun:outputs",
"reason": "The file ERR034597_1.small_fastqc.html is included in the result of re-execution, but this crate does not have File entity with @id ERR034597_1.small_fastqc.html."
}
],
"status": "FAILED"
Expand Down
Loading

0 comments on commit 42b08c7

Please sign in to comment.