Skip to content

Commit

Permalink
Merge pull request #42 from kikkomep/enhancement/programmatic-usage
Browse files Browse the repository at this point in the history
enhancement: simplify API
  • Loading branch information
kikkomep authored Nov 25, 2024
2 parents dc581d4 + f252270 commit a34d453
Show file tree
Hide file tree
Showing 14 changed files with 405 additions and 262 deletions.
84 changes: 61 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@

A Python package to validate [RO-Crate](https://researchobject.github.io/ro-crate/)s.

* Supports CLI-based validation as well as programmatic validation (so it can
easily be used by Python code).
* Implements an extensible validation framework to which new RO-Crate profiles
can be added. Validation is based on SHACL shapes and Python code.
* Currently, validations for the following profiles are implemented: RO-Crate
(base profile), [Workflow
RO-Crate](https://www.researchobject.org/ro-crate/specification/1.1/workflows.html),
[Process Run
Crate](https://www.researchobject.org/workflow-run-crate/profiles/0.1/process_run_crate.html).
More are being implemented.
- Supports [CLI-based validation](#cli-based-validation) as well as [programmatic validation](#programmatic-validation) (so it can easily be used by Python code).
- Implements an extensible validation framework to which new RO-Crate profiles
can be added. Validation is based on SHACL shapes and Python code.
- Currently, validation for the following profiles is implemented: RO-Crate
(base profile), [Workflow
RO-Crate](https://w3id.org/workflowhub/workflow-ro-crate),
[Process Run
Crate](https://w3id.org/ro/wfrun/process).
[Workflow Run Crate](https://w3id.org/ro/wfrun/workflow),
[Provenance Run Crate](https://w3id.org/ro/wfrun/provenance),
[Workflow Testing RO-Crate](https://w3id.org/ro/wftest).

**Note**: this software is still work in progress. Feel free to try it out,
report positive and negative feedback. Do send a note (e.g., by opening an
Issue) before starting to develop patches you would like to contribute. The
report positive and negative feedback. Do send a note (e.g., by opening an
Issue) before starting to develop patches you would like to contribute. The
implementation of validation code for additional RO-Crate profiles would be
particularly welcome.


## Installation

You can install the package using `pip` or `poetry`. The following instructions assume you have Python 3.8 or later installed.
Expand All @@ -36,23 +36,23 @@ It’s recommended to create a virtual environment before installing the package

```bash
python3 -m venv .venv
````
```

Then, activate the virtual environment:

* On **Unix** or **macOS**:
- On **Unix** or **macOS**:

```bash
source .venv/bin/activate
```

* On **Windows** (Command Prompt):
- On **Windows** (Command Prompt):

```bash
.venv\Scripts\activate
```

* On **Windows** (PowerShell):
- On **Windows** (PowerShell):

```powershell
.venv\Scripts\Activate.ps1
Expand Down Expand Up @@ -86,8 +86,7 @@ Ensure you have Poetry installed. If not, follow the instructions [here](https:/
poetry install
```


## Usage
## CLI-based Validation

After installation, use the `rocrate-validator` command to validate RO-Crates. You can run this in a virtual activated environment (if created in the [optional step](#optional-step-create-a-virtual-environment) above) or without a virtual environment if none was created.

Expand All @@ -98,24 +97,63 @@ Run the validator using the following command:
```bash
rocrate-validator validate <path_to_rocrate>
```

where `<path_to_rocrate>` is the path to the RO-Crate you want to validate.

Type `rocrate-validator --help` for more information.



### 2. Using `poetry`

Run the validator using the following command:

```bash
poetry run rocrate-validator validate <path_to_rocrate>
```

where `<path_to_rocrate>` is the path to the RO-Crate you want to validate.

Type `rocrate-validator --help` for more information.

## Programmatic Validation

You can also integrate the package programmatically in your Python code. Here's an example:

```python

# Import the `services` and `models` module from the rocrate_validator package
from rocrate_validator import services, models

# Create an instance of `ValidationSettings` class to configure the validation
settings = services.ValidationSettings(
# Set the path to the RO-Crate root directory
rocrate_uri='/path/to/ro-crate',
# Set the identifier of the RO-Crate profile to use for validation.
# If not set, the system will attempt to automatically determine the appropriate validation profile.
profile_identifier='ro-crate-1.1',
# Set the requirement level for the validation
requirement_severity=models.Severity.REQUIRED,
)

# Call the validation service with the settings
result = services.validate(settings)

# Check if the validation was successful
if not result.has_issues():
print("RO-Crate is valid!")
else:
print("RO-Crate is invalid!")
# Explore the issues
for issue in result.get_issues():
# Every issue object has a reference to the check that failed, the severity of the issue, and a message describing the issue.
print(f"Detected issue of severity {issue.severity.name} with check \"{issue.check.identifier}\": {issue.message}")
```

... that leads to the following output:

```bash
RO-Crate is invalid!
Detected issue of severity REQUIRED with check "ro-crate-1.1:root_entity_exists: The RO-Crate must contain a root entity.
```
## Running the tests
Expand All @@ -138,8 +176,8 @@ This project is licensed under the terms of the Apache License 2.0. See the
This work has been partially funded by the following sources:
* the [BY-COVID](https://by-covid.org/) project (HORIZON Europe grant agreement number 101046203);
* the [LIFEMap](https://www.thelifemap.it/) project, funded by the Italian Ministry of Health (Piano Operative Salute, Trajectory 3).
- the [BY-COVID](https://by-covid.org/) project (HORIZON Europe grant agreement number 101046203);
- the [LIFEMap](https://www.thelifemap.it/) project, funded by the Italian Ministry of Health (Piano Operative Salute, Trajectory 3).
<img alt="Co-funded by the EU"
src="https://raw.githubusercontent.com/crs4/rocrate-validator/develop/docs/img/eu-logo/EN_Co-fundedbytheEU_RGB_POS.png"
Expand Down
2 changes: 1 addition & 1 deletion rocrate_validator/cli/commands/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def describe_profile(ctx,

try:
# Get the profile
profile = services.get_profile(profiles_path=profiles_path, profile_identifier=profile_identifier)
profile = services.get_profile(profile_identifier, profiles_path=profiles_path)

# Set the subheader title
subheader_title = f"[bold][cyan]Profile:[/cyan] [magenta italic]{profile.identifier}[/magenta italic][/bold]"
Expand Down
30 changes: 13 additions & 17 deletions rocrate_validator/cli/commands/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ def validate(ctx,
requirement_severity_only: bool = False,
rocrate_uri: Path = ".",
no_fail_fast: bool = False,
ontologies_path: Optional[Path] = None,
no_paging: bool = False,
verbose: bool = False,
output_format: str = "text",
Expand Down Expand Up @@ -268,8 +267,6 @@ def validate(ctx,
logger.debug("no_fail_fast: %s", no_fail_fast)
logger.debug("fail fast: %s", not no_fail_fast)

if ontologies_path:
logger.debug("ontologies_path: %s", os.path.abspath(ontologies_path))
if rocrate_uri:
logger.debug("rocrate_path: %s", os.path.abspath(rocrate_uri))

Expand All @@ -280,10 +277,9 @@ def validate(ctx,
"profile_identifier": profile_identifier,
"requirement_severity": requirement_severity,
"requirement_severity_only": requirement_severity_only,
"inherit_profiles": not disable_profile_inheritance,
"enable_profile_inheritance": not disable_profile_inheritance,
"verbose": verbose,
"data_path": rocrate_uri,
"ontology_path": Path(ontologies_path).absolute() if ontologies_path else None,
"rocrate_uri": rocrate_uri,
"abort_on_first": not no_fail_fast
}

Expand Down Expand Up @@ -567,7 +563,7 @@ def __init_layout__(self):
severity_color = get_severity_color(Severity.get(settings["requirement_severity"]))
base_info_layout = Layout(
Align(
f"\n[bold cyan]RO-Crate:[/bold cyan] [bold]{URI(settings['data_path']).uri}[/bold]"
f"\n[bold cyan]RO-Crate:[/bold cyan] [bold]{URI(settings['rocrate_uri']).uri}[/bold]"
"\n[bold cyan]Target Profile:[/bold cyan][bold magenta] "
f"{settings['profile_identifier']}[/bold magenta] "
f"{'[italic](autodetected)[/italic]' if settings['profile_autodetected'] else ''}"
Expand Down Expand Up @@ -756,14 +752,14 @@ def show_validation_details(self, pager: Pager, enable_pager: bool = True):
for issue in sorted(result.get_issues_by_check(check),
key=lambda x: (-x.severity.value, x)):
path = ""
if issue.resultPath and issue.value:
path = f" of [yellow]{issue.resultPath}[/yellow]"
if issue.value:
if issue.resultPath:
if issue.violatingProperty and issue.violatingPropertyValue:
path = f" of [yellow]{issue.violatingProperty}[/yellow]"
if issue.violatingPropertyValue:
if issue.violatingProperty:
path += "="
path += f"\"[green]{issue.value}[/green]\" " # keep the ending space
if issue.focusNode:
path = f"{path} on [cyan]<{issue.focusNode}>[/cyan]"
path += f"\"[green]{issue.violatingPropertyValue}[/green]\" " # keep the ending space
if issue.violatingEntity:
path = f"{path} on [cyan]<{issue.violatingEntity}>[/cyan]"
console.print(
Padding(f"- [[red]Violation[/red]{path}]: "
f"{Markdown(issue.message).markup}", (0, 9)), style="white")
Expand All @@ -777,14 +773,14 @@ def __compute_profile_stats__(validation_settings: dict):
# extract the validation settings
severity_validation = Severity.get(validation_settings.get("requirement_severity"))
profiles = services.get_profiles(validation_settings.get("profiles_path"), severity=severity_validation)
profile = services.get_profile(validation_settings.get("profiles_path"),
validation_settings.get("profile_identifier"),
profile = services.get_profile(validation_settings.get("profile_identifier"),
validation_settings.get("profiles_path"),
severity=severity_validation)
# initialize the profiles list
profiles = [profile]

# add inherited profiles if enabled
if validation_settings.get("inherit_profiles"):
if validation_settings.get("enable_profile_inheritance"):
profiles.extend(profile.inherited_profiles)
logger.debug("Inherited profiles: %r", profile.inherited_profiles)

Expand Down
3 changes: 3 additions & 0 deletions rocrate_validator/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,6 @@

# Current JSON output format
JSON_OUTPUT_FORMAT_VERSION = "0.1"

# Default value for the HTTP cache timeout
DEFAULT_HTTP_CACHE_TIMEOUT = 60
Loading

0 comments on commit a34d453

Please sign in to comment.