semantic-benchmark 0.4.2__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {semantic_benchmark-0.4.2/src/semantic_benchmark.egg-info → semantic_benchmark-0.4.4}/PKG-INFO +13 -6
  2. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/README.md +10 -3
  3. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/pyproject.toml +3 -3
  4. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rocrate/create.py +105 -162
  5. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rohub/provenance.py +5 -5
  6. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/runner.py +6 -6
  7. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4/src/semantic_benchmark.egg-info}/PKG-INFO +13 -6
  8. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark.egg-info/requires.txt +2 -2
  9. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/LICENSE +0 -0
  10. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/setup.cfg +0 -0
  11. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/__init__.py +0 -0
  12. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rocrate/__init__.py +0 -0
  13. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rocrate/validation.py +0 -0
  14. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rohub/__init__.py +0 -0
  15. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rohub/download.py +0 -0
  16. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rohub/rohub_config.json +0 -0
  17. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/rohub/upload.py +0 -0
  18. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantic.py +0 -0
  19. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/__init__.py +0 -0
  20. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/loader.py +0 -0
  21. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/models.py +0 -0
  22. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/benchmark.ttl +0 -0
  23. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/data-source.ttl +0 -0
  24. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/field.ttl +0 -0
  25. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/file-object.ttl +0 -0
  26. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/mathematical-model.ttl +0 -0
  27. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/numerical-variable.ttl +0 -0
  28. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/parameter-set.ttl +0 -0
  29. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/processing-step.ttl +0 -0
  30. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/shapes/research-problem.ttl +0 -0
  31. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark/semantics/vocabulary.py +0 -0
  32. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark.egg-info/SOURCES.txt +0 -0
  33. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark.egg-info/dependency_links.txt +0 -0
  34. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark.egg-info/entry_points.txt +0 -0
  35. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/src/semantic_benchmark.egg-info/top_level.txt +0 -0
  36. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/tests/test_loader.py +0 -0
  37. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/tests/test_published_runs_query.py +0 -0
  38. {semantic_benchmark-0.4.2 → semantic_benchmark-0.4.4}/tests/test_upload_match_query.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: semantic-benchmark
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Reusable helpers for semantic benchmark metadata, RO-Crate aggregation, and RoHub provenance workflows.
5
5
  Author-email: Mahdi Jafarkhani <mahdi.jafarkhani@tik.uni-stuttgart.de>
6
6
  License-Expression: MIT
@@ -28,9 +28,9 @@ Provides-Extra: test
28
28
  Requires-Dist: pytest; extra == "test"
29
29
  Provides-Extra: rocrate
30
30
  Requires-Dist: rocrate; extra == "rocrate"
31
- Requires-Dist: roc-validator; extra == "rocrate"
31
+ Requires-Dist: roc-validator==0.12.2; extra == "rocrate"
32
32
  Provides-Extra: all
33
- Requires-Dist: roc-validator; extra == "all"
33
+ Requires-Dist: roc-validator==0.12.2; extra == "all"
34
34
  Requires-Dist: rocrate; extra == "all"
35
35
  Dynamic: license-file
36
36
 
@@ -96,11 +96,18 @@ It accepts `--provenance_folderpath` (the RO-Crate ZIP), `--benchmark-name`,
96
96
  `--use-production-rohub` arguments. The software URL and version are read from
97
97
  the SoftwareApplication node in the RO-Crate ZIP.
98
98
 
99
- `semantic_benchmark.rocrate.create_main_ro(...)` can validate the generated
100
- aggregate crate by passing `validation_profile`. The package writes the RO-Crate
99
+ `semantic_benchmark.rocrate.create_main_ro(...)` always validates the generated
100
+ aggregate crate against `workflow-run-crate-0.5`. The package writes the RO-Crate
101
101
  zip, unpacks it to a validation directory, and runs `validate_rocrate(...)`.
102
102
  Aggregate creation requires `subcrate_paths`, the exact reporter RO-Crate ZIPs
103
- from completed runs. The CLI accepts one `--subcrate-path` per run.
103
+ from completed runs, and `workflow_path`, a YAML file used as the main workflow.
104
+ The CLI accepts `--workflow-path` and one `--subcrate-path` per run. Each subcrate
105
+ is the result of a `CreateAction` whose instrument is the YAML workflow; matched
106
+ configuration values are action inputs and extracted metrics are action results.
107
+ Pass `agent_name` and `agent_orcid` from the reporter configuration's
108
+ `report-rocrate-agent-name` and `report-rocrate-agent-orcid` values. They identify
109
+ the workflow author and creator in the aggregate crate. The CLI accepts
110
+ `--agent-name` and `--agent-orcid`.
104
111
 
105
112
  Repository-specific projects can override the packaged RoHub defaults with:
106
113
 
@@ -60,11 +60,18 @@ It accepts `--provenance_folderpath` (the RO-Crate ZIP), `--benchmark-name`,
60
60
  `--use-production-rohub` arguments. The software URL and version are read from
61
61
  the SoftwareApplication node in the RO-Crate ZIP.
62
62
 
63
- `semantic_benchmark.rocrate.create_main_ro(...)` can validate the generated
64
- aggregate crate by passing `validation_profile`. The package writes the RO-Crate
63
+ `semantic_benchmark.rocrate.create_main_ro(...)` always validates the generated
64
+ aggregate crate against `workflow-run-crate-0.5`. The package writes the RO-Crate
65
65
  zip, unpacks it to a validation directory, and runs `validate_rocrate(...)`.
66
66
  Aggregate creation requires `subcrate_paths`, the exact reporter RO-Crate ZIPs
67
- from completed runs. The CLI accepts one `--subcrate-path` per run.
67
+ from completed runs, and `workflow_path`, a YAML file used as the main workflow.
68
+ The CLI accepts `--workflow-path` and one `--subcrate-path` per run. Each subcrate
69
+ is the result of a `CreateAction` whose instrument is the YAML workflow; matched
70
+ configuration values are action inputs and extracted metrics are action results.
71
+ Pass `agent_name` and `agent_orcid` from the reporter configuration's
72
+ `report-rocrate-agent-name` and `report-rocrate-agent-orcid` values. They identify
73
+ the workflow author and creator in the aggregate crate. The CLI accepts
74
+ `--agent-name` and `--agent-orcid`.
68
75
 
69
76
  Repository-specific projects can override the packaged RoHub defaults with:
70
77
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "semantic-benchmark"
7
- version = "0.4.2"
7
+ version = "0.4.4"
8
8
  description = "Reusable helpers for semantic benchmark metadata, RO-Crate aggregation, and RoHub provenance workflows."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -38,10 +38,10 @@ test = [
38
38
  ]
39
39
  rocrate = [
40
40
  "rocrate",
41
- "roc-validator",
41
+ "roc-validator==0.12.2",
42
42
  ]
43
43
  all = [
44
- "roc-validator",
44
+ "roc-validator==0.12.2",
45
45
  "rocrate",
46
46
  ]
47
47
 
@@ -6,13 +6,13 @@ import argparse
6
6
  import json
7
7
  import logging
8
8
  import re
9
- import shutil
10
9
  import tempfile
11
10
  import uuid
12
11
  import zipfile
13
12
  from pathlib import Path
14
- from typing import Any, Literal, TypedDict, get_args
13
+ from typing import Any, TypedDict
15
14
 
15
+ from rocrate.model.computationalworkflow import ComputationalWorkflow
16
16
  from rocrate.model.softwareapplication import SoftwareApplication
17
17
  from rocrate.rocrate import ROCrate
18
18
  from semantic_benchmark import semantics
@@ -21,42 +21,33 @@ from semantic_benchmark.rocrate.validation import validate_rocrate
21
21
  LOG_FORMAT = "%(levelname)s:%(name)s:%(message)s"
22
22
  LOGGER = logging.getLogger(__name__)
23
23
 
24
- WorkflowLanguage = Literal["nextflow", "snakemake"]
25
-
26
24
  ROCRATE_CONFORMS_TO = [
27
25
  {"@id": "https://w3id.org/ro/crate/1.1"},
28
- {"@id": "https://w3id.org/workflowhub/workflow-ro-crate/1.0"},
29
26
  ]
30
27
  ROOT_DATASET_CONFORMS_TO = [
31
- {"@id": "https://w3id.org/ro/wfrun/process/0.4"},
32
- {"@id": "https://w3id.org/ro/wfrun/workflow/0.4"},
33
- {"@id": "https://w3id.org/ro/wfrun/provenance/0.4"},
34
- {"@id": "https://w3id.org/workflowhub/workflow-ro-crate/1.0"},
28
+ {"@id": "https://w3id.org/ro/wfrun/process/0.6"},
29
+ {"@id": "https://w3id.org/ro/wfrun/workflow/0.6"},
30
+ {"@id": "https://w3id.org/workflowhub/workflow-ro-crate/1.1"},
35
31
  ]
32
+ AGGREGATE_VALIDATION_PROFILE = "workflow-run-crate-0.5"
36
33
  PROFILE_CREATIVE_WORKS = (
37
34
  {
38
- "@id": "https://w3id.org/ro/wfrun/process/0.4",
35
+ "@id": "https://w3id.org/ro/wfrun/process/0.6",
39
36
  "@type": "CreativeWork",
40
37
  "name": "Process Run Crate",
41
- "version": "0.4",
38
+ "version": "0.6",
42
39
  },
43
40
  {
44
- "@id": "https://w3id.org/ro/wfrun/workflow/0.4",
41
+ "@id": "https://w3id.org/ro/wfrun/workflow/0.6",
45
42
  "@type": "CreativeWork",
46
43
  "name": "Workflow Run Crate",
47
- "version": "0.4",
48
- },
49
- {
50
- "@id": "https://w3id.org/ro/wfrun/provenance/0.4",
51
- "@type": "CreativeWork",
52
- "name": "Provenance Run Crate",
53
- "version": "0.4",
44
+ "version": "0.6",
54
45
  },
55
46
  {
56
- "@id": "https://w3id.org/workflowhub/workflow-ro-crate/1.0",
47
+ "@id": "https://w3id.org/workflowhub/workflow-ro-crate/1.1",
57
48
  "@type": "CreativeWork",
58
49
  "name": "Workflow RO-Crate",
59
- "version": "1.0",
50
+ "version": "1.1",
60
51
  },
61
52
  )
62
53
 
@@ -150,23 +141,6 @@ def _add_subcrates_to_main(
150
141
  )
151
142
 
152
143
 
153
- def _create_action_object_ids(input_path: Path, subcrates: list[Path]) -> dict[str, str]:
154
- """Map run folder names to relative subcrate object ids.
155
-
156
- Args:
157
- input_path: Root simulation result directory.
158
- subcrates: Reporter RO-Crate ZIPs for completed runs.
159
-
160
- Returns:
161
- Mapping from run folder name to the subcrate ZIP path relative to
162
- ``input_path``.
163
- """
164
- object_ids: dict[str, str] = {}
165
- for subcrate in subcrates:
166
- object_ids.setdefault(subcrate.parent.name, str(subcrate.relative_to(input_path)))
167
- return object_ids
168
-
169
-
170
144
  def _formal_parameter_key(part: semantics.ParameterEntry) -> tuple[Any, ...]:
171
145
  """Build a stable de-duplication key for a benchmark parameter.
172
146
 
@@ -205,15 +179,17 @@ def _formal_parameter_payload(
205
179
  }
206
180
 
207
181
  unit = getattr(part, "unit", None)
208
- payload["additionalType"] = ""
182
+ payload["additionalType"] = (
183
+ "Float" if isinstance(part, (semantics.NumericalParameter, semantics.NumericalVariable)) else "Text"
184
+ )
209
185
 
210
186
  if unit is not None:
211
187
  payload["unitText"] = {"@id": unit}
212
188
 
213
189
  if isinstance(part, semantics.NumericalParameter):
214
- payload["defaultValue"] = part.numerical_value
190
+ payload["value"] = part.numerical_value
215
191
  elif isinstance(part, semantics.TextParameter):
216
- payload["defaultValue"] = part.string_value
192
+ payload["value"] = part.string_value
217
193
  elif (
218
194
  isinstance(part, semantics.NumericalVariable)
219
195
  and part.quantity_kind is not None
@@ -515,10 +491,10 @@ def _metric_result_payload(
515
491
  "@id": result_id,
516
492
  "@type": "PropertyValue",
517
493
  "name": metric.label,
518
- "defaultValue": value,
494
+ "value": value,
519
495
  }
520
496
  if metric.unit:
521
- payload["additionalType"] = metric.unit
497
+ payload["unitText"] = {"@id": metric.unit}
522
498
  return payload
523
499
 
524
500
 
@@ -597,12 +573,12 @@ def _configuration_entries_for_step(
597
573
 
598
574
  def _add_run_actions(
599
575
  crate: ROCrate,
600
- subfolders: list[Path],
601
- object_ids_by_run: dict[str, str],
576
+ subcrates: list[Path],
577
+ input_path: Path,
602
578
  processing_steps: list[semantics.ProcessingStep],
603
579
  configuration_entries: list[ConfigurationEntry],
604
580
  run_results_by_name: dict[str, list[dict[str, str]]],
605
- software_id: str,
581
+ workflow_id: str,
606
582
  ) -> None:
607
583
  """Add run action nodes connecting runs, configurations, and results.
608
584
 
@@ -618,14 +594,11 @@ def _add_run_actions(
618
594
  Returns:
619
595
  None. The function mutates ``crate`` by adding ``CreateAction`` nodes.
620
596
  """
621
- for run_folder in subfolders:
597
+ for subcrate in subcrates:
598
+ run_folder = subcrate.parent
622
599
  run_name = run_folder.name
623
- run_object_id = object_ids_by_run.get(run_name)
624
- if not run_object_id:
625
- continue
626
-
627
600
  result_ids = run_results_by_name.get(run_name, [])
628
-
601
+ object_ids = []
629
602
  for processing_step in processing_steps:
630
603
  step_configuration_entries = _configuration_entries_for_step(
631
604
  configuration_entries, processing_step
@@ -634,19 +607,20 @@ def _add_run_actions(
634
607
  run_folder, step_configuration_entries
635
608
  )
636
609
 
637
- step_name = processing_step.label or processing_step.id
638
- run_action: dict[str, Any] = {
639
- "@id": _new_jsonld_id(),
640
- "@type": "CreateAction",
641
- "name": f"{step_name} {run_name}",
642
- "object": [{"@id": run_object_id}],
643
- "instrument": {"@id": software_id},
644
- }
645
610
  if config_id:
646
- run_action["object"].append({"@id": config_id})
647
- if result_ids:
648
- run_action["result"] = result_ids
649
- crate.add_jsonld(run_action)
611
+ object_ids.append({"@id": config_id})
612
+ run_action: dict[str, Any] = {
613
+ "@id": _new_jsonld_id(),
614
+ "@type": "CreateAction",
615
+ "name": f"Benchmark run {run_name}",
616
+ "instrument": {"@id": workflow_id},
617
+ "result": [{"@id": str(subcrate.relative_to(input_path))}, *result_ids],
618
+ }
619
+ if object_ids:
620
+ run_action["object"] = object_ids
621
+ action_id = run_action["@id"]
622
+ crate.add_jsonld(run_action)
623
+ crate.root_dataset.append_to("mentions", {"@id": action_id})
650
624
 
651
625
 
652
626
  def _configure_crate_metadata(
@@ -714,33 +688,48 @@ def _add_software_node(
714
688
  )
715
689
 
716
690
 
691
+ def _add_yaml_language_node(crate: ROCrate) -> dict[str, str]:
692
+ """Add the YAML language node and return its JSON-LD reference."""
693
+ yaml_id = "https://www.wikidata.org/entity/Q281876"
694
+ crate.add_jsonld(
695
+ {
696
+ "@id": yaml_id,
697
+ "@type": "ComputerLanguage",
698
+ "identifier": yaml_id,
699
+ "name": "YAML",
700
+ "url": "https://yaml.org/",
701
+ }
702
+ )
703
+ return {"@id": yaml_id}
704
+
705
+
717
706
  def _add_workflow_node(
718
707
  crate: ROCrate,
719
708
  workflow_path: Path,
720
709
  software_id: str,
721
- workflow_id: str,
722
- lang: WorkflowLanguage,
710
+ agent_name: str,
711
+ agent_orcid: str,
723
712
  ) -> None:
724
713
  """Add the main workflow file to the aggregate crate.
725
714
 
726
715
  Args:
727
716
  crate: Aggregate RO-Crate being built.
728
717
  workflow_path: Location of the workflow file to add.
729
- software_id: JSON-LD id of the software application linked as ``hasPart``.
730
- workflow_id: JSON-LD id of the workflow file.
731
- lang: Workflow language used by the workflow file.
718
+ software_id: JSON-LD id of the software application requirement.
719
+ agent_name: Name of the workflow author and creator.
720
+ agent_orcid: ORCID URL identifying the author and creator.
732
721
 
733
722
  Returns:
734
723
  None. The function mutates ``crate``.
735
724
  """
736
- crate.add_workflow(
737
- source=str(workflow_path),
738
- lang=lang,
739
- properties={
740
- "hasPart": {"@id": software_id},
741
- "mainEntity": {"@id": workflow_id},
742
- },
743
- )
725
+ workflow = crate.add(ComputationalWorkflow(crate, source=str(workflow_path)))
726
+ workflow["encodingFormat"] = "application/yaml"
727
+ workflow["softwareRequirements"] = {"@id": software_id}
728
+ workflow["programmingLanguage"] = _add_yaml_language_node(crate)
729
+ person = {"@id": agent_orcid}
730
+ workflow["author"] = person
731
+ workflow["creator"] = person
732
+ crate.add_jsonld({"@id": agent_orcid, "@type": "Person", "name": agent_name})
744
733
 
745
734
 
746
735
  def create_main_ro(
@@ -752,12 +741,11 @@ def create_main_ro(
752
741
  crate_name: str,
753
742
  crate_description: str,
754
743
  subcrate_paths: list[str | Path],
755
- workflow_path: str | Path | None = None,
756
- lang: WorkflowLanguage = "snakemake",
757
- validation_profile: str | None = None,
758
- validation_dir: str | Path | None = None,
744
+ workflow_path: str | Path = "",
759
745
  software_url: str | None = None,
760
746
  software_version: str | None = None,
747
+ agent_name: str | None = None,
748
+ agent_orcid: str | None = None,
761
749
  ) -> None:
762
750
  """Create and write an aggregate RO-Crate for a benchmark result directory.
763
751
 
@@ -770,16 +758,12 @@ def create_main_ro(
770
758
  crate_license: License URL stored on the aggregate RO-Crate.
771
759
  crate_name: Human-readable name stored on the aggregate RO-Crate.
772
760
  crate_description: Description stored on the aggregate RO-Crate.
773
- workflow_path: Location of the workflow file to add to the crate. Defaults
774
- to the workflow found alongside the first subcrate.
775
- lang: Workflow language. Defaults to ``snakemake``.
776
- validation_profile: Optional RO-Crate profile identifier. When provided,
777
- the written crate is unpacked and validated against this profile.
778
- validation_dir: Optional directory used for unpacked validation content.
779
- Defaults to ``unpacked_rocrate`` next to ``rocrate_path``.
761
+ workflow_path: Required YAML workflow file to add as the main entity.
780
762
  software_url: Stable software identifier (for example, a zbMATH URL).
781
763
  If omitted, a local identifier is generated.
782
764
  software_version: Software version to record, if known.
765
+ agent_name: Name supplied by the reporter configuration.
766
+ agent_orcid: ORCID URL supplied by the reporter configuration.
783
767
  subcrate_paths: Exact reporter RO-Crate ZIPs to include.
784
768
 
785
769
  Returns:
@@ -788,20 +772,23 @@ def create_main_ro(
788
772
 
789
773
  Raises:
790
774
  NotADirectoryError: If ``path`` is not a directory.
791
- FileNotFoundError: If the explicit or inferred workflow path is not a file.
792
- ValueError: If ``lang`` is unsupported or no matching subcrate zip files
793
- are found.
775
+ FileNotFoundError: If the workflow path is not a file.
776
+ ValueError: If no matching subcrate zip files are found.
794
777
  """
795
778
  crate = ROCrate(version="1.1")
796
779
  input_path = Path(path)
797
780
 
798
781
  if not input_path.is_dir():
799
782
  raise NotADirectoryError(f"{path} is not a valid directory")
800
- if lang not in get_args(WorkflowLanguage):
801
- raise ValueError(
802
- f"Unsupported workflow language {lang!r}; expected one of "
803
- f"{', '.join(get_args(WorkflowLanguage))}"
804
- )
783
+ if not workflow_path:
784
+ raise ValueError("workflow_path is required")
785
+ if not agent_name or not agent_orcid:
786
+ raise ValueError("agent_name and agent_orcid are required")
787
+ workflow_file = Path(workflow_path).resolve()
788
+ if not workflow_file.is_file():
789
+ raise FileNotFoundError(f"{workflow_file} is not a valid workflow file")
790
+ if workflow_file.suffix.lower() not in {".yaml", ".yml"}:
791
+ raise ValueError("workflow_path must be a YAML file")
805
792
  LOGGER.info(
806
793
  "Creating aggregate RO-Crate from simulation results in %s...",
807
794
  input_path,
@@ -825,35 +812,23 @@ def create_main_ro(
825
812
 
826
813
  _add_subcrates_to_main(crate, subcrates, input_path)
827
814
 
828
- object_ids_by_run = _create_action_object_ids(input_path, subcrates)
829
815
  configuration_entries = _add_configuration_nodes(crate, benchmark_object)
830
816
  run_results = _add_evaluates_nodes(
831
817
  crate, benchmark_object, extracted_subfolders
832
818
  )
833
819
  run_results_by_name = _run_results_by_name(run_results)
834
820
 
835
- workflow_id = get_workflow_id(
836
- subcrates[0],
837
- fallback=Path(workflow_path).name if workflow_path is not None else "Snakefile",
838
- )
839
- workflow_file = (
840
- Path(workflow_path)
841
- if workflow_path is not None
842
- else extraction_path / subcrates[0].parent.relative_to(input_path) / workflow_id
843
- )
844
- if not workflow_file.is_file():
845
- raise FileNotFoundError(f"{workflow_file} is not a valid workflow file")
846
-
821
+ workflow_id = workflow_file.name
847
822
  software_id = software_url or _new_jsonld_id()
848
823
 
849
824
  _add_run_actions(
850
825
  crate=crate,
851
- subfolders=subfolders,
852
- object_ids_by_run=object_ids_by_run,
826
+ subcrates=subcrates,
827
+ input_path=input_path,
853
828
  processing_steps=benchmark_object.processing_steps,
854
829
  configuration_entries=configuration_entries,
855
830
  run_results_by_name=run_results_by_name,
856
- software_id=software_id,
831
+ workflow_id=workflow_id,
857
832
  )
858
833
  _configure_crate_metadata(
859
834
  crate,
@@ -864,28 +839,18 @@ def create_main_ro(
864
839
  )
865
840
  _add_software_node(crate, software_id, software_name, software_version)
866
841
  _add_profile_creative_works(crate)
867
- _add_workflow_node(crate, workflow_file, software_id, workflow_id, lang)
842
+ _add_workflow_node(crate, workflow_file, software_id, agent_name, agent_orcid)
868
843
 
869
844
  crate.write_zip(rocrate_path)
870
845
 
871
- if validation_profile:
872
- validation_path = Path(validation_dir) if validation_dir else (
873
- Path(rocrate_path).parent / "unpacked_rocrate"
874
- )
875
- if validation_path.exists():
876
- shutil.rmtree(validation_path)
877
- validation_path.mkdir(parents=True, exist_ok=True)
878
-
879
- try:
880
- with zipfile.ZipFile(rocrate_path, "r") as zip_ref:
881
- zip_ref.extractall(validation_path)
846
+ with tempfile.TemporaryDirectory(prefix="benchmark-validation-") as validation_dir:
847
+ with zipfile.ZipFile(rocrate_path, "r") as zip_ref:
848
+ zip_ref.extractall(validation_dir)
882
849
 
883
- validate_rocrate(
884
- rocrate_path=str(validation_path),
885
- profile=validation_profile,
886
- )
887
- finally:
888
- shutil.rmtree(validation_path)
850
+ validate_rocrate(
851
+ rocrate_path=validation_dir,
852
+ profile=AGGREGATE_VALIDATION_PROFILE,
853
+ )
889
854
 
890
855
 
891
856
  def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
@@ -948,30 +913,20 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
948
913
  required=True,
949
914
  help="Description recorded in the generated aggregate RO-Crate",
950
915
  )
951
- parser.add_argument(
952
- "--lang",
953
- dest="workflow_lang",
954
- default="snakemake",
955
- choices=get_args(WorkflowLanguage),
956
- help="Language of the workflow file (default: snakemake)",
957
- )
958
916
  parser.add_argument(
959
917
  "--workflow-path",
960
- default=None,
961
- help=(
962
- "Path to the workflow file added to the generated RO-Crate "
963
- "(default: workflow alongside the first subcrate)"
964
- ),
918
+ required=True,
919
+ help="Path to the YAML workflow file used as the main entity",
965
920
  )
966
921
  parser.add_argument(
967
- "--validation-profile",
968
- default=None,
969
- help="Optional RO-Crate profile identifier used to validate the generated crate",
922
+ "--agent-name",
923
+ required=True,
924
+ help="Workflow author and creator name from report-rocrate-agent-name",
970
925
  )
971
926
  parser.add_argument(
972
- "--validation-dir",
973
- default=None,
974
- help="Optional directory for unpacked validation content",
927
+ "--agent-orcid",
928
+ required=True,
929
+ help="Workflow author and creator ORCID URL from report-rocrate-agent-orcid",
975
930
  )
976
931
 
977
932
  return parser.parse_args(argv)
@@ -999,22 +954,10 @@ def main() -> None:
999
954
  crate_description=args.crate_description,
1000
955
  subcrate_paths=args.subcrate_paths,
1001
956
  workflow_path=args.workflow_path,
1002
- lang=args.workflow_lang,
1003
- validation_profile=args.validation_profile,
1004
- validation_dir=args.validation_dir,
957
+ agent_name=args.agent_name,
958
+ agent_orcid=args.agent_orcid,
1005
959
  )
1006
960
 
1007
961
 
1008
- def get_workflow_id(subcrate: str | Path, fallback: str = "Snakefile") -> str:
1009
- """Return the computational workflow id stored in a subcrate."""
1010
- crate = ROCrate(subcrate)
1011
-
1012
- for e in crate.get_entities():
1013
- if e.type == ["ComputationalWorkflow"]:
1014
- return e.id
1015
-
1016
- return fallback
1017
-
1018
-
1019
962
  if __name__ == "__main__":
1020
963
  main()
@@ -194,14 +194,14 @@ def _value_block(
194
194
  {parent} {relation} ?{node_prefix}_{safe_name} .
195
195
  ?{node_prefix}_{safe_name} a {_node_type(node_prefix)} ;
196
196
  {name_predicate} "{escaped_name}" ;
197
- schema:defaultValue ?{safe_name} .
197
+ (schema:value|schema:defaultValue) ?{safe_name} .
198
198
  """.strip()
199
199
 
200
200
 
201
201
  def _parameter_block(
202
202
  name: str,
203
203
  var_map: dict[str, str],
204
- name_predicate: str = "schema:name",
204
+ name_predicate: str = FOAF_NAME,
205
205
  ) -> str:
206
206
  """Build a graph pattern that extracts one configuration parameter."""
207
207
  return _value_block(
@@ -217,7 +217,7 @@ def _parameter_block(
217
217
  def _metric_block(
218
218
  name: str,
219
219
  var_map: dict[str, str],
220
- name_predicate: str = "schema:name",
220
+ name_predicate: str = FOAF_NAME,
221
221
  ) -> str:
222
222
  """Build an optional graph pattern that extracts one run metric."""
223
223
  value_block = _value_block(
@@ -300,9 +300,9 @@ def build_dynamic_query(
300
300
  inner_query = _join_blocks(
301
301
  "\n".join(_create_action_links(include_tool=True)),
302
302
  "\n".join(
303
- _parameter_block(name, var_map, FOAF_NAME) for name in parameters
303
+ _parameter_block(name, var_map) for name in parameters
304
304
  ),
305
- "\n".join(_metric_block(name, var_map, FOAF_NAME) for name in metrics),
305
+ "\n".join(_metric_block(name, var_map) for name in metrics),
306
306
  )
307
307
 
308
308
  order_name = parameters[0] if parameters else None
@@ -203,7 +203,9 @@ def create_aggregate_rocrate(
203
203
  crate_name: str,
204
204
  crate_description: str,
205
205
  subcrate_paths: list[str | Path],
206
- validation_dir: Path | None = None,
206
+ workflow_path: str | Path,
207
+ agent_name: str,
208
+ agent_orcid: str,
207
209
  software_url: str | None = None,
208
210
  software_version: str | None = None,
209
211
  ) -> None:
@@ -212,9 +214,6 @@ def create_aggregate_rocrate(
212
214
  # the core runner helpers remain available without the ``rocrate`` extra.
213
215
  from . import rocrate
214
216
 
215
- options = {}
216
- if validation_dir is not None:
217
- options["validation_dir"] = validation_dir
218
217
  rocrate.create_main_ro(
219
218
  path=str(results_dir),
220
219
  benchmark_object=benchmark,
@@ -226,6 +225,7 @@ def create_aggregate_rocrate(
226
225
  crate_name=crate_name,
227
226
  crate_description=crate_description,
228
227
  subcrate_paths=subcrate_paths,
229
- validation_profile="provenance-run-crate-0.5",
230
- **options,
228
+ workflow_path=workflow_path,
229
+ agent_name=agent_name,
230
+ agent_orcid=agent_orcid,
231
231
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: semantic-benchmark
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Reusable helpers for semantic benchmark metadata, RO-Crate aggregation, and RoHub provenance workflows.
5
5
  Author-email: Mahdi Jafarkhani <mahdi.jafarkhani@tik.uni-stuttgart.de>
6
6
  License-Expression: MIT
@@ -28,9 +28,9 @@ Provides-Extra: test
28
28
  Requires-Dist: pytest; extra == "test"
29
29
  Provides-Extra: rocrate
30
30
  Requires-Dist: rocrate; extra == "rocrate"
31
- Requires-Dist: roc-validator; extra == "rocrate"
31
+ Requires-Dist: roc-validator==0.12.2; extra == "rocrate"
32
32
  Provides-Extra: all
33
- Requires-Dist: roc-validator; extra == "all"
33
+ Requires-Dist: roc-validator==0.12.2; extra == "all"
34
34
  Requires-Dist: rocrate; extra == "all"
35
35
  Dynamic: license-file
36
36
 
@@ -96,11 +96,18 @@ It accepts `--provenance_folderpath` (the RO-Crate ZIP), `--benchmark-name`,
96
96
  `--use-production-rohub` arguments. The software URL and version are read from
97
97
  the SoftwareApplication node in the RO-Crate ZIP.
98
98
 
99
- `semantic_benchmark.rocrate.create_main_ro(...)` can validate the generated
100
- aggregate crate by passing `validation_profile`. The package writes the RO-Crate
99
+ `semantic_benchmark.rocrate.create_main_ro(...)` always validates the generated
100
+ aggregate crate against `workflow-run-crate-0.5`. The package writes the RO-Crate
101
101
  zip, unpacks it to a validation directory, and runs `validate_rocrate(...)`.
102
102
  Aggregate creation requires `subcrate_paths`, the exact reporter RO-Crate ZIPs
103
- from completed runs. The CLI accepts one `--subcrate-path` per run.
103
+ from completed runs, and `workflow_path`, a YAML file used as the main workflow.
104
+ The CLI accepts `--workflow-path` and one `--subcrate-path` per run. Each subcrate
105
+ is the result of a `CreateAction` whose instrument is the YAML workflow; matched
106
+ configuration values are action inputs and extracted metrics are action results.
107
+ Pass `agent_name` and `agent_orcid` from the reporter configuration's
108
+ `report-rocrate-agent-name` and `report-rocrate-agent-orcid` values. They identify
109
+ the workflow author and creator in the aggregate crate. The CLI accepts
110
+ `--agent-name` and `--agent-orcid`.
104
111
 
105
112
  Repository-specific projects can override the packaged RoHub defaults with:
106
113
 
@@ -5,12 +5,12 @@ rocrate
5
5
  rohub
6
6
 
7
7
  [all]
8
- roc-validator
8
+ roc-validator==0.12.2
9
9
  rocrate
10
10
 
11
11
  [rocrate]
12
12
  rocrate
13
- roc-validator
13
+ roc-validator==0.12.2
14
14
 
15
15
  [test]
16
16
  pytest