Skip to content

Commit

Permalink
🐠
Browse files Browse the repository at this point in the history
  • Loading branch information
jennydaman committed Mar 4, 2022
1 parent 7661c46 commit f54f622
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 127 deletions.
114 changes: 114 additions & 0 deletions .github/workflows/build.yml
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 }}
13 changes: 5 additions & 8 deletions Dockerfile
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"]
86 changes: 6 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,8 @@
# _ChRIS_ ds Plugin Template
# ep-interpolate-surface-with-sphere

<!--
[![Version](https://img.shields.io/docker/v/fnndsc/pl-appname?sort=semver)](https://hub.docker.com/r/fnndsc/pl-appname)
[![MIT License](https://img.shields.io/github/license/fnndsc/pl-appname)](https://github.com/FNNDSC/pl-appname/blob/main/LICENSE)
[![Build](https://github.com/FNNDSC/pl-appname/actions/workflows/ci.yml/badge.svg)](https://github.com/FNNDSC/pl-appname/actions)
-->
[![Version](https://img.shields.io/docker/v/fnndsc/ep-interpolate-surface-with-sphere?sort=semver)](https://hub.docker.com/r/fnndsc/ep-interpolate-surface-with-sphere)
[![MIT License](https://img.shields.io/github/license/fnndsc/ep-interpolate-surface-with-sphere)](https://github.com/FNNDSC/ep-interpolate-surface-with-sphere/blob/main/LICENSE)
[![Build](https://github.com/FNNDSC/ep-interpolate-surface-with-sphere/actions/workflows/ci.yml/badge.svg)](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.
26 changes: 0 additions & 26 deletions app.py

This file was deleted.

93 changes: 93 additions & 0 deletions isws.py
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()
4 changes: 3 additions & 1 deletion requirements.txt
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
24 changes: 12 additions & 12 deletions setup.py
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',
Expand Down

0 comments on commit f54f622

Please sign in to comment.