generated from FNNDSC/python-chrisapp-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7661c46
commit f54f622
Showing
7 changed files
with
233 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Automatically build multi-architectural tagged container images and push them to DockerHub | ||
# https://github.com/FNNDSC/cookiecutter-chrisapp/wiki/Automatic-Builds | ||
|
||
name: build | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
tags: [ '**' ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
publish: | ||
if: github.event_name == 'push' || github.event_name == 'release' | ||
runs-on: ubuntu-20.04 | ||
|
||
services: | ||
registry: | ||
image: registry:2 | ||
ports: | ||
- 5000:5000 | ||
|
||
steps: | ||
- name: Get git tag | ||
id: git_info | ||
if: startsWith(github.ref, 'refs/tags/') | ||
run: echo "::set-output name=tag::${GITHUB_REF##*/}" | ||
- name: Decide image tag name | ||
id: determine | ||
env: | ||
git_tag: ${{ steps.git_info.outputs.tag }} | ||
run: | | ||
repo="${GITHUB_REPOSITORY,,}" # to lower case | ||
# if build triggered by tag, use tag name | ||
tag="${git_tag:-latest}" | ||
# if tag is a version number prefixed by 'v', remove the 'v' | ||
if [[ "$tag" =~ ^v[0-9].* ]]; then | ||
tag="${tag:1}" | ||
fi | ||
dock_image=$repo:$tag | ||
echo $dock_image | ||
echo "::set-output name=dock_image::$dock_image" | ||
echo "::set-output name=repo::$repo" | ||
- uses: actions/checkout@v2 | ||
- uses: docker/setup-qemu-action@v1 | ||
- uses: docker/setup-buildx-action@v1 | ||
with: | ||
driver-opts: network=host | ||
- name: Cache Docker layers | ||
uses: actions/cache@v2 | ||
with: | ||
path: /tmp/.buildx-cache | ||
key: ${{ runner.os }}-buildx-${{ github.sha }} | ||
restore-keys: | | ||
${{ runner.os }}-buildx- | ||
- name: Login to DockerHub | ||
id: dockerhub_login | ||
uses: docker/login-action@v1 | ||
with: | ||
username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
password: ${{ secrets.DOCKERHUB_PASSWORD }} | ||
|
||
- name: Login to GitHub Container Registry | ||
uses: docker/login-action@v1 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.repository_owner }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Build and push | ||
uses: docker/build-push-action@v2 | ||
id: docker_build | ||
with: | ||
context: . | ||
file: ./Dockerfile | ||
tags: | | ||
${{ steps.determine.outputs.dock_image }} | ||
localhost:5000/${{ steps.determine.outputs.dock_image }} | ||
ghcr.io/${{ steps.determine.outputs.dock_image }} | ||
platforms: linux/amd64,linux/arm64,linux/ppc64le | ||
push: true | ||
cache-from: type=local,src=/tmp/.buildx-cache | ||
cache-to: type=local,dest=/tmp/.buildx-cache | ||
|
||
- name: Get plugin meta | ||
id: pluginmeta | ||
run: | | ||
repo=${{ steps.determine.outputs.repo }} | ||
dock_image=${{ steps.determine.outputs.dock_image }} | ||
docker run --rm localhost:5000/$dock_image chris_plugin_info > /tmp/description.json | ||
jq < /tmp/description.json # pretty print in log | ||
echo "::set-output name=title::$(jq -r '.title' < /tmp/description.json)" | ||
- name: Update DockerHub description | ||
uses: peter-evans/dockerhub-description@v2 | ||
continue-on-error: true # it is not crucial that this works | ||
with: | ||
username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
password: ${{ secrets.DOCKERHUB_PASSWORD }} | ||
short-description: ${{ steps.pluginmeta.outputs.title }} | ||
readme-filepath: ./README.md | ||
repository: ${{ steps.determine.outputs.repo }} | ||
|
||
- name: Upload to ChRIS Store | ||
if: "!endsWith(steps.determine.outputs.dock_image, ':latest')" | ||
uses: FNNDSC/chrisstore-action@master | ||
with: | ||
descriptor_file: /tmp/description.json | ||
auth: ${{ secrets.CHRIS_STORE_USER }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,15 @@ | ||
# Python version can be changed, e.g. | ||
# FROM python:3.8 | ||
# FROM docker.io/fnndsc/conda:python3.10.2-cuda11.6.0 | ||
FROM docker.io/python:3.10.2-slim-buster | ||
FROM docker.io/fnndsc/mni-conda-base:civet2.1.1-python3.10.2 | ||
|
||
LABEL org.opencontainers.image.authors="FNNDSC <[email protected]>" \ | ||
org.opencontainers.image.title="ChRIS Plugin Title" \ | ||
org.opencontainers.image.description="A ChRIS ds plugin that..." | ||
org.opencontainers.image.title="ep-interpolate-surface-with-sphere" \ | ||
org.opencontainers.image.description="Resample surface meshes to have 81,920 triangles." | ||
|
||
WORKDIR /usr/local/src/app | ||
WORKDIR /usr/local/src/ep-interpolate-surface-with-sphere | ||
|
||
COPY requirements.txt . | ||
RUN pip install -r requirements.txt | ||
|
||
COPY . . | ||
RUN pip install . | ||
|
||
CMD ["commandname", "--help"] | ||
CMD ["isws", "--help"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,8 @@ | ||
# _ChRIS_ ds Plugin Template | ||
# ep-interpolate-surface-with-sphere | ||
|
||
<!-- | ||
[](https://hub.docker.com/r/fnndsc/pl-appname) | ||
[](https://github.com/FNNDSC/pl-appname/blob/main/LICENSE) | ||
[](https://github.com/FNNDSC/pl-appname/actions) | ||
--> | ||
[](https://hub.docker.com/r/fnndsc/ep-interpolate-surface-with-sphere) | ||
[](https://github.com/FNNDSC/ep-interpolate-surface-with-sphere/blob/main/LICENSE) | ||
[](https://github.com/FNNDSC/ep-interpolate-surface-with-sphere/actions) | ||
|
||
|
||
This is a minimal template repository for _ChRIS_ _ds_ plugin applications. | ||
For a more comprehensive boilerplate, use | ||
|
||
https://github.com/fnndsc/cookiecutter-chrisapp | ||
|
||
## How to Use This Template | ||
|
||
1. Click "Use this template" | ||
2. Clone the newly created repository | ||
3. Replace placeholder text | ||
|
||
```shell | ||
function replace () { | ||
find . -type f -not -path '*/\.*/*' -not -path '*/\venv/*' -exec sed -i -e "s/$1/$2/g" '{}' \; | ||
} | ||
|
||
replace commandname my_command_name | ||
replace pl-appname pl-my-plugin-name | ||
replace fnndsc my_username | ||
``` | ||
|
||
### Template Examples | ||
|
||
Here are some good, complete examples of _ChRIS_ plugins created from this template. | ||
|
||
- https://github.com/FNNDSC/pl-nums2mask | ||
- https://github.com/FNNDSC/pl-nii2mnc-u8 | ||
|
||
Advanced users can `cp -rv .github/workflows` into their own repositories to enable | ||
automatic builds. | ||
|
||
## Abstract | ||
|
||
PROGRAMNAME is a [_ChRIS_](https://chrisproject.org/) | ||
_ds_ plugin which takes in ... as input files and | ||
creates ... as output files. | ||
|
||
## Usage | ||
|
||
```shell | ||
singularity exec docker://fnndsc/pl-appname commandname [--args values...] input/ output/ | ||
``` | ||
|
||
## Examples | ||
|
||
```shell | ||
mkdir incoming/ outgoing/ | ||
mv some.dat other.dat incoming/ | ||
singularity exec docker://fnndsc/pl-appname:latest commandname [--args] incoming/ outgoing/ | ||
``` | ||
|
||
## Development | ||
|
||
### Building | ||
|
||
```shell | ||
docker build -t localhost/fnndsc/pl-appname . | ||
``` | ||
|
||
### Get JSON Representation | ||
|
||
```shell | ||
docker run --rm localhost/fnndsc/pl-appname chris_plugin_info > MyProgram.json | ||
``` | ||
|
||
### Local Test Run | ||
|
||
```shell | ||
docker run --rm -it --userns=host -u $(id -u):$(id -g) \ | ||
-v $PWD/app.py:/usr/local/lib/python3.10/site-packages/app.py:ro \ | ||
-v $PWD/in:/incoming:ro -v $PWD/out:/outgoing:rw -w /outgoing \ | ||
localhost/fnndsc/pl-appname commandname /incoming /outgoing | ||
``` | ||
`ep-interpolate-surface-with-sphere` resamples surface meshes | ||
created by the marching-cubes algorithm to have 81,920 triangles. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
#!/usr/bin/env python | ||
|
||
import os | ||
import sys | ||
from argparse import ArgumentParser, Namespace, ArgumentDefaultsHelpFormatter | ||
from concurrent.futures import ThreadPoolExecutor | ||
from pathlib import Path | ||
|
||
from typing import BinaryIO, Sequence, Literal, Optional | ||
import subprocess | ||
from loguru import logger | ||
from chris_plugin import chris_plugin, PathMapper | ||
from civet.extraction import Side, IrregularSurface | ||
|
||
SIDE_OPTIONS = ('left', 'right', 'auto', 'none') | ||
SideStr = Literal['left', 'right', 'auto', 'none'] | ||
|
||
|
||
parser = ArgumentParser(description='Resample surface mesh to have 81,920 triangles.', | ||
formatter_class=ArgumentDefaultsHelpFormatter) | ||
parser.add_argument('-s', '--side', default='auto', choices=SIDE_OPTIONS, | ||
help='brain hemisphere side. "auto" => infer from file name') | ||
parser.add_argument('-p', '--pattern', default='**/*.obj', | ||
help='pattern for file names to include') | ||
parser.add_argument('-q', '--quiet', action='store_true', | ||
help='disable status messages') | ||
|
||
|
||
def isws(surface: Path, output: Path, side: SideStr) -> None: | ||
log_path = output.with_suffix('.log') | ||
try: | ||
logger.info('Processing {} to {}, log: {}', surface, output, log_path) | ||
with log_path.open('wb') as log: | ||
IrregularSurface(surface)\ | ||
.interpolate_with_sphere(pick_side(surface, side))\ | ||
.save(output, shell=curry_log(log)) | ||
logger.info('Completed {}', output) | ||
except Exception as e: | ||
logger.exception('Failed to process {}', surface) | ||
raise e | ||
|
||
|
||
def curry_log(log: BinaryIO): | ||
def run_with_log(cmd: Sequence[str | os.PathLike]) -> None: | ||
subprocess.run(cmd, stderr=log, stdout=log, check=True) | ||
return run_with_log | ||
|
||
|
||
def pick_side(input_path: Path, side: SideStr) -> Optional[Side]: | ||
if side == 'left': | ||
return Side.LEFT | ||
if side == 'right': | ||
return Side.RIGHT | ||
if side == 'auto': | ||
path = str(input_path).lower() | ||
if 'left' in path: | ||
return Side.LEFT | ||
if 'right' in path: | ||
return Side.RIGHT | ||
raise ValueError(f'Substring "left" nor "right" found in: {path}') | ||
if side == 'none': | ||
return None | ||
raise ValueError(f'side must be one of: {SIDE_OPTIONS}') | ||
|
||
|
||
@chris_plugin( | ||
parser=parser, | ||
category='MRI Processing', | ||
min_memory_limit='100Mi', | ||
min_cpu_limit='1000m', | ||
) | ||
def main(options: Namespace, inputdir: Path, outputdir: Path): | ||
if options.quiet: | ||
logger.remove() | ||
logger.add(sys.stderr, level='WARNING') | ||
|
||
nproc = len(os.sched_getaffinity(0)) | ||
logger.debug('Using {} threads.', nproc) | ||
|
||
results = [] | ||
with ThreadPoolExecutor(max_workers=nproc) as pool: | ||
mapper = PathMapper(inputdir, outputdir, glob=options.pattern, suffix='._81920.obj') | ||
for mc_surface, output in mapper: | ||
results.append(pool.submit(isws, mc_surface, output, options.side)) | ||
|
||
for future in results: | ||
e = future.exception() | ||
if e is not None: | ||
raise e | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
chris_plugin~=0.0.12 | ||
chris_plugin~=0.0.13 | ||
pycivet==0.0.6 | ||
loguru~=0.6.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,21 @@ | ||
from setuptools import setup | ||
|
||
setup( | ||
name = 'chris-plugin-template', | ||
version = '1.0.0', | ||
description = 'A ChRIS DS plugin template', | ||
author = 'FNNDSC', | ||
author_email = '[email protected]', | ||
url = 'https://github.com/FNNDSC/python-chrisapp-template', | ||
py_modules = ['app'], | ||
install_requires = ['chris_plugin'], | ||
name = 'interpolate-surface-with-sphere', | ||
version = '0.1.0', | ||
description = 'Resample a surface mesh to have 81,920 triangles.', | ||
author = 'Jennings Zhang', | ||
author_email = '[email protected]', | ||
url = 'https://github.com/FNNDSC/ep-interpolate-surface-with-sphere', | ||
py_modules = ['isws'], | ||
install_requires = ['chris_plugin', 'pycivet', 'loguru'], | ||
license = 'MIT', | ||
python_requires = '>=3.8.2', | ||
python_requires = '>=3.10.2', | ||
entry_points = { | ||
'console_scripts': [ | ||
'commandname = app:main' | ||
] | ||
}, | ||
'isws = isws:main' | ||
] | ||
}, | ||
classifiers = [ | ||
'License :: OSI Approved :: MIT License', | ||
'Programming Language :: Python :: 3.10', | ||
|