From c7c6c62d5f135af8ec9982242fc913f47424b0cd Mon Sep 17 00:00:00 2001 From: Chris van Run Date: Tue, 29 Oct 2024 20:41:34 +0100 Subject: [PATCH] Use example values for json interfaces --- grand_challenge_forge/generation_utils.py | 47 +++++++++++++--- .../algorithm-template/inference.py.j2 | 3 +- .../example-algorithm/inference.py.j2 | 3 +- grand_challenge_forge/partials/filters.py | 5 ++ pyproject.toml | 1 + tests/utils.py | 54 ++++++++++++------- 6 files changed, 87 insertions(+), 26 deletions(-) diff --git a/grand_challenge_forge/generation_utils.py b/grand_challenge_forge/generation_utils.py index f6f6127..5660a22 100644 --- a/grand_challenge_forge/generation_utils.py +++ b/grand_challenge_forge/generation_utils.py @@ -1,9 +1,11 @@ +import json import os import shutil import uuid from datetime import datetime, timezone from pathlib import Path +import black from jinja2 import FileSystemLoader, StrictUndefined, TemplateNotFound from jinja2.sandbox import ImmutableSandboxedEnvironment @@ -27,20 +29,36 @@ def is_file(component_interface): ] == "File" and not component_interface["relative_path"].endswith(".json") +def has_example_value(component_interface): + return ( + "example_value" in component_interface + and component_interface["example_value"] is not None + ) + + def create_civ_stub_file(*, target_path, component_interface): """Creates a stub based on a component interface""" target_path.parent.mkdir(parents=True, exist_ok=True) + + if has_example_value(component_interface): + target_path.write_text( + json.dumps( + component_interface["example_value"], + indent=4, + ) + ) + return + + # Copy over an example if is_json(component_interface): - src = RESOURCES_PATH / "example.json" + shutil.copy(RESOURCES_PATH / "example.json", target_path) elif is_image(component_interface): target_path = target_path / f"{str(uuid.uuid4())}.mha" target_path.parent.mkdir(parents=True, exist_ok=True) - src = RESOURCES_PATH / "example.mha" + shutil.copy(RESOURCES_PATH / "example.mha", target_path) else: target_path.parent.mkdir(parents=True, exist_ok=True) - src = RESOURCES_PATH / "example.txt" - - shutil.copy(src, target_path) + shutil.copy(RESOURCES_PATH / "example.txt", target_path) def ci_to_civ(component_interface): @@ -60,7 +78,9 @@ def ci_to_civ(component_interface): f"{component_interface['relative_path']}" ) if component_interface["super_kind"] == "Value": - civ["value"] = '{"some_key": "some_value"}' + civ["value"] = component_interface.get( + "example_value", {"some_key": "some_value"} + ) return { **civ, "interface": component_interface, @@ -140,6 +160,8 @@ def copy_and_render( # Copy non-template files shutil.copy2(source_file, output_file) + apply_black(output_path) + def check_allowed_source(path): if PARTIALS_PATH.resolve() not in path.resolve().parents: @@ -147,3 +169,16 @@ def check_allowed_source(path): f"Only files under {PARTIALS_PATH} are allowed " "to be copied or rendered" ) + + +def apply_black(target_path): + for python_file in target_path.glob("**/*.py"): + # Use direct black format call because black + # CLI entrypoint ignores files in .gitignore + + black.format_file_in_place( + python_file, + fast=False, + mode=black.Mode(), + write_back=black.WriteBack.YES, + ) diff --git a/grand_challenge_forge/partials/algorithm-template/inference.py.j2 b/grand_challenge_forge/partials/algorithm-template/inference.py.j2 index 4598d2f..7474feb 100644 --- a/grand_challenge_forge/partials/algorithm-template/inference.py.j2 +++ b/grand_challenge_forge/partials/algorithm-template/inference.py.j2 @@ -64,7 +64,8 @@ def run(): # For now, let us make bogus predictions {%- for ci in algorithm.outputs %} output_{{ ci.slug.replace("-", "_")}} = - {%- if ci | is_image %} numpy.eye(4, 2) + {%- if ci | has_example_value %} {{ ci.example_value }} + {%- elif ci | is_image %} numpy.eye(4, 2) {%- elif ci | is_json %} {"content": "should match the required format"} {%- elif ci | is_file %} "content: should match the required format" {% endif %} diff --git a/grand_challenge_forge/partials/example-algorithm/inference.py.j2 b/grand_challenge_forge/partials/example-algorithm/inference.py.j2 index c421883..4d56624 100644 --- a/grand_challenge_forge/partials/example-algorithm/inference.py.j2 +++ b/grand_challenge_forge/partials/example-algorithm/inference.py.j2 @@ -62,7 +62,8 @@ def run(): # For now, let us make bogus predictions {%- for ci in phase.algorithm_outputs %} output_{{ ci.slug.replace("-", "_")}} = - {%- if ci | is_image %} numpy.eye(4, 2) + {%- if ci | has_example_value %} {{ ci.example_value }} + {%- elif ci | is_image %} numpy.eye(4, 2) {%- elif ci | is_json %} {"content": "should match the required format"} {%- elif ci | is_file %} "content: should match the required format" {% endif %} diff --git a/grand_challenge_forge/partials/filters.py b/grand_challenge_forge/partials/filters.py index cf4e291..4d0983e 100644 --- a/grand_challenge_forge/partials/filters.py +++ b/grand_challenge_forge/partials/filters.py @@ -36,3 +36,8 @@ def is_file(arg): @register_simple_filter def has_file(arg): return any(generation_utils.is_file(item) for item in arg) + + +@register_simple_filter +def has_example_value(arg): + return generation_utils.has_example_value(arg) diff --git a/pyproject.toml b/pyproject.toml index ed2c069..cbc3eab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ python = ">3.10" jinja2 = "*" click = "*" jsonschema = "*" +black = "23.9.1" [tool.poetry.dev-dependencies] pytest = "*" diff --git a/tests/utils.py b/tests/utils.py index 49a7bbc..b364a46 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -38,25 +38,29 @@ "slug": "input-ci-slug", "kind": "Segmentation", "super_kind": "Image", - "relative_path": "images/input-value" + "relative_path": "images/input-value", + "example_value": None }, { "slug": "another-input-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "another-input-value.json" + "relative_path": "another-input-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-input-ci-slug", "kind": "Anything", "super_kind": "Value", - "relative_path": "yet-another-input-value.json" + "relative_path": "yet-another-input-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-non-json-input-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "yet-another-non-json-input-value" + "relative_path": "yet-another-non-json-input-value", + "example_value": None } ], "algorithm_outputs": [ @@ -64,25 +68,29 @@ "slug": "output-ci-slug", "kind": "Image", "super_kind": "Image", - "relative_path": "images/output-value" + "relative_path": "images/output-value", + "example_value": None }, { "slug": "another-output-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "output-value.json" + "relative_path": "output-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-output-ci-slug", "kind": "Anything", "super_kind": "Value", - "relative_path": "yet-another-output-value.json" + "relative_path": "yet-another-output-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-non-json-output-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "yet-another-non-json-output-value" + "relative_path": "yet-another-non-json-output-value", + "example_value": None } ] }, @@ -97,7 +105,8 @@ "slug": "input-ci-slug", "kind": "Image", "super_kind": "Image", - "relative_path": "images/input-value" + "relative_path": "images/input-value", + "example_value": None } ], "algorithm_outputs": [ @@ -105,7 +114,8 @@ "slug": "another-output-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "output-value.json" + "relative_path": "output-value.json", + "example_value": {"key": "value"} } ] } @@ -124,25 +134,29 @@ "slug": "input-ci-slug", "kind": "Segmentation", "super_kind": "Image", - "relative_path": "images/input-value" + "relative_path": "images/input-value", + "example_value": None }, { "slug": "another-input-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "another-input-value.json" + "relative_path": "another-input-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-input-ci-slug", "kind": "Anything", "super_kind": "Value", - "relative_path": "yet-another-input-value.json" + "relative_path": "yet-another-input-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-non-json-input-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "yet-another-non-json-input-value" + "relative_path": "yet-another-non-json-input-value", + "example_value": None } ], "outputs": [ @@ -150,25 +164,29 @@ "slug": "output-ci-slug", "kind": "Image", "super_kind": "Image", - "relative_path": "images/output-value" + "relative_path": "images/output-value", + "example_value": None }, { "slug": "another-output-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "output-value.json" + "relative_path": "output-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-output-ci-slug", "kind": "Anything", "super_kind": "Value", - "relative_path": "yet-another-output-value.json" + "relative_path": "yet-another-output-value.json", + "example_value": {"key": "value"} }, { "slug": "yet-another-non-json-output-ci-slug", "kind": "Anything", "super_kind": "File", - "relative_path": "yet-another-non-json-output-value" + "relative_path": "yet-another-non-json-output-value", + "example_value": None } ] }