diff --git a/.gitignore b/.gitignore index 5701a17..0b8570c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ _site/ +__pycache__ .sass-cache/ .jekyll-cache/ .jekyll-metadata diff --git a/docs/fractal_tasks.md b/docs/fractal_tasks.md index 10a9ab5..6effda8 100644 --- a/docs/fractal_tasks.md +++ b/docs/fractal_tasks.md @@ -20,7 +20,7 @@ hide: } -Here is a list of task packages which can be used in Fractal (note that not all existing packages are currently listed). +Here is a list of task packages which can be used in Fractal. Note that not all existing packages are currently listed, see https://fractal-analytics-platform.github.io/fractal-tasks-core/all_tasks for a more complete list. diff --git a/tasks_data_retrieval/create_tasks_data.py b/tasks_data_retrieval/create_tasks_data.py index 638fe1b..7a0a80c 100644 --- a/tasks_data_retrieval/create_tasks_data.py +++ b/tasks_data_retrieval/create_tasks_data.py @@ -5,7 +5,14 @@ from zipfile import ZipFile from typing import Any from pydantic import BaseModel -from typing import Optional, Literal +from typing import Optional +from typing import Literal + +import sys + +sys.path.append(Path(__file__).parent) +from install_instructions import get_github_install_instructions +from install_instructions import get_pypi_install_instructions DOWNLOAD_FOLDER = Path(__file__).parent / "downloads" @@ -14,7 +21,7 @@ class TaskReadV2(BaseModel): """ - Based on + Customization of https://github.com/fractal-analytics-platform/fractal-server/blob/main/fractal_server/app/schemas/v2/task.py """ @@ -30,6 +37,7 @@ class TaskReadV2(BaseModel): modality: Optional[str] = None authors: Optional[str] = None tags: list[str] + install_instructions: Optional[str] = None class Config: extra = "forbid" @@ -117,7 +125,16 @@ def handle_pypi_project(pypi_project_url: str) -> dict[str, Any]: manifest = load_manifest_from_zip(wheel_path) Path(wheel_path).unlink() - return dict(manifest=manifest, **info) + install_instructions = get_pypi_install_instructions( + project_name=project_name, + version=info["version"], + ) + + return dict( + manifest=manifest, + install_instructions=install_instructions, + **info, + ) def handle_github_repository(github_url: str) -> dict[str, Any]: @@ -160,7 +177,16 @@ def handle_github_repository(github_url: str) -> dict[str, Any]: manifest = load_manifest_from_zip(wheel_path) Path(wheel_path).unlink() - return dict(manifest=manifest, **info) + install_instructions = get_github_install_instructions( + wheel_name=Path(wheel_path).name, + wheel_url=wheel_asset_browser_download_url, + ) + + return dict( + manifest=manifest, + install_instructions=install_instructions, + **info, + ) def get_package_info(source: str) -> dict[str, Any]: @@ -210,11 +236,7 @@ def _get_task_type( sources_file = Path(__file__).parent / "sources.txt" with sources_file.open("r") as f: sources = f.read().splitlines() -sources = [ - source - for source in sources - if not (source.startswith("#") or source == "") -] +sources = [source for source in sources if not (source.startswith("#") or source == "")] TASK_GROUPS = [] for source in sources: @@ -226,6 +248,7 @@ def _get_task_type( pkg_name = data["name"] pkg_version = data.get("version") authors = data["manifest"].get("authors") + install_instructions = data.get("install_instructions") pkg_task_list = data["manifest"]["task_list"] for task in pkg_task_list: new_task = dict() @@ -236,6 +259,7 @@ def _get_task_type( new_task["version"] = pkg_version new_task["type"] = _get_task_type(task) new_task["authors"] = authors + new_task["install_instructions"] = install_instructions TaskReadV2(**new_task) task_list.append(new_task) @@ -253,7 +277,12 @@ def _get_task_type( TASK_GROUPS.append(task_group) t_end = time.perf_counter() - print(f"END processing {source=} - version={pkg_version}' - added {ntasks} tasks - elapsed {t_end-t_start:.3f} s.") + print( + f"END processing {source=} - " + f"version={pkg_version}' - " + f"added {ntasks} tasks - " + f"elapsed {t_end-t_start:.3f} s." + ) print() output_file = Path(__file__).parent / "tasks.json" diff --git a/tasks_data_retrieval/install_instructions.py b/tasks_data_retrieval/install_instructions.py new file mode 100644 index 0000000..d1e85cd --- /dev/null +++ b/tasks_data_retrieval/install_instructions.py @@ -0,0 +1,78 @@ +INSTALL_INSTRUCTIONS_TITLE_1 = "## How to add this task to a Fractal instance\n" +INSTALL_INSTRUCTIONS_TITLE_2 = "## How to install this task in a Python environment\n" + + +def _get_default_template_pypi() -> str: + lines = [ + INSTALL_INSTRUCTIONS_TITLE_1, + "Trigger a _PyPI_ task collection with package `__PROJECT_NAME__` and package version `__VERSION__`.", + "", + INSTALL_INSTRUCTIONS_TITLE_2, + "```", + 'pip install "__PROJECT_NAME__==__VERSION__"', + "```", + ] + template = "\n".join(lines) + "\n" + return template + + +def _get_default_template_pypi_with_extra(extra: str) -> str: + lines = [ + INSTALL_INSTRUCTIONS_TITLE_1, + f"Trigger a _PyPI_ task collection for package `__PROJECT_NAME__`, package version `__VERSION__` and package extras {extra}.", + "", + INSTALL_INSTRUCTIONS_TITLE_2, + "```", + f'pip install "__PROJECT_NAME__[{extra}]==__VERSION__"', + "```", + ] + template = "\n".join(lines) + "\n" + return template + + +def _get_default_template_wheel_url() -> str: + lines = [ + INSTALL_INSTRUCTIONS_TITLE_1, + "1. Download the wheel file from [this link](__WHEEL_URL__),", + "2. Trigger a _local_ task collection by uploading the wheel file.", + "", + INSTALL_INSTRUCTIONS_TITLE_2, + "1. Download the wheel file from [this link](__WHEEL_URL__)", + "2. `pip install __WHEEL_NAME__`", + ] + template = "\n".join(lines) + "\n" + return template + + +def _get_default_template_wheel_url_with_extra(extra: str) -> str: + lines = [ + INSTALL_INSTRUCTIONS_TITLE_1, + "1. Download the wheel file from [this link](__WHEEL_URL__),", + f"2. Trigger a _local_ task collection by uploading the wheel file, with package extras {extra}.", + "", + INSTALL_INSTRUCTIONS_TITLE_2, + "1. Download the wheel file from [this link](__WHEEL_URL__)", + f"2. `pip install \"__WHEEL_NAME__[{extra}]\"`", + ] + template = "\n".join(lines) + "\n" + return template + + +def get_pypi_install_instructions(*, project_name: str, version: str) -> str: + instructions = _get_default_template_pypi() + if project_name == "fractal-tasks-core": + instructions = _get_default_template_pypi_with_extra(extra="fractal-tasks") + instructions = instructions.replace("__PROJECT_NAME__", project_name) + instructions = instructions.replace("__VERSION__", version) + print(instructions) + return instructions + + +def get_github_install_instructions(*, wheel_name: str, wheel_url: str) -> str: + instructions = _get_default_template_wheel_url() + if wheel_name.startswith("scmultiplex"): + instructions = _get_default_template_wheel_url_with_extra(extra="fractal-tasks") + instructions = instructions.replace("__WHEEL_NAME__", wheel_name) + instructions = instructions.replace("__WHEEL_URL__", wheel_url) + print(instructions) + return instructions diff --git a/tasks_data_retrieval/sources.txt b/tasks_data_retrieval/sources.txt index 7a418a7..431cb81 100644 --- a/tasks_data_retrieval/sources.txt +++ b/tasks_data_retrieval/sources.txt @@ -1,11 +1,7 @@ -https://pypi.org/project/fractal-tasks-core/ +https://pypi.org/project/fractal-tasks-core https://pypi.org/project/fractal-faim-ipa https://pypi.org/project/fractal-lif-converters https://pypi.org/project/operetta-compose https://github.com/fractal-analytics-platform/fractal-helper-tasks https://github.com/fmi-basel/gliberal-scMultipleX - -# https://github.com/Apricot-Therapeutics/APx_fractal_task_collection -# https://github.com/fractal-analytics-platform/fractal-plantseg-tasks -# https://github.com/m-albert/fractal-ome-zarr-hcs-stitching/archive -# https://github.com/fractal-analytics-platform/fractal-ilastik-tasksC/archive/refs/tags/0.1.1.zip +https://github.com/m-albert/fractal-ome-zarr-hcs-stitching