Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions saber/webanno/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
.venv

# Created by https://www.gitignore.io/api/code,python,macos
# Edit at https://www.gitignore.io/?templates=code,python,macos

### Code ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# End of https://www.gitignore.io/api/code,python,macos
55 changes: 55 additions & 0 deletions saber/webanno/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# webanno

A human-facing web annotation tool for different flavors of data.



## Overview

The `webanno` tool runs a Python process that exposes a web server with a single user-facing web-page which holds developer-defined data to annotate (perhaps it's text, perhaps it's a webform, perhaps it's imagery...). Once the data are annotated and submitted, the web server shuts down gracefully (exitcode 0), which means you can use the output data in a SABER workflow.

### Why not a conventional web app?

A conventional web application process does not stop once the data are annotated, which means you cannot run it "in-line" with the rest of a SABER workflow.

## How do I pick the data to annotate?

`webanno` uses a plugin architecture system: A plugin defines both the HTML of the page as well as what to do with the data when annotation is over.

There are many already-existing annotation plugins:

### `ButtonPressPlugin`

This plugin is the simplest (and a good example to learn from!), and just monitors when a button is pressed.

### `FormInputPlugin`

This plugin lets you create a custom form in HTML for a user to fill out.

### `CentroidsPlugin`

This plugin lets a user place points in a 2D image, and returns a JSON file of the coordinates of those clicks.

### `BossVolumePlugin`

This plugin lets a user place points in a 3D volume downloaded from BossDB.


## Installation

To get started, you need only pip-install this package and its dependencies:

```shell
pip3 install -r requirements.txt
pip3 install -e .
```

## Usage

Use the `webanno` tool from the command-line:

```shell
webanno examples/example_centroids_image.json
```

Results will be stored in `results.json` in PWD. If you would like to store them elsewhere, set `json_file_output` in any config file (all plugins accept this config value).
4 changes: 4 additions & 0 deletions saber/webanno/examples/example_basic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugin": "webanno.plugins.ButtonPressPlugin",
"config": {}
}
22 changes: 22 additions & 0 deletions saber/webanno/examples/example_boss_centroids.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"plugin": "webanno.plugins.BossVolumePlugin",
"config": {
"boss_uri": "bossdb://https://api.bossdb.io/wilson2019/P7/em",
"start": [
32820,
26731,
32
],
"stop": [
33616,
27500,
46
],
"res": 0,
"dot_color": [
255,
0,
255
]
}
}
8 changes: 8 additions & 0 deletions saber/webanno/examples/example_centroids_image.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugin": "webanno.plugins.CentroidsPlugin",
"config": {
"max_count": 5,
"prompt": "<b>Click on the eyes.</b>",
"image_url": "http://www.fillmurray.com/600/600/"
}
}
20 changes: 20 additions & 0 deletions saber/webanno/examples/example_form_input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"plugin": "webanno.plugins.FormInputPlugin",
"config": {
"fields": [
{
"name": "name",
"type": "text"
},
{
"name": "age",
"type": "number"
},
{
"name": "todays_date",
"type": "time",
"label": "When did you wake up today?"
}
]
}
}
5 changes: 5 additions & 0 deletions saber/webanno/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
flask
flask_cors
pillow
intern
numpy
5 changes: 5 additions & 0 deletions saber/webanno/scripts/webanno
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python3

from webanno import run

run()
30 changes: 30 additions & 0 deletions saber/webanno/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os
from distutils.core import setup

"""
git tag {VERSION}
git push --tags
python setup.py sdist upload -r pypi
"""

VERSION = "0.1.0"

setup(
name="webanno",
version=VERSION,
author="Jordan Matelsky",
author_email="j6k4m8@gmail.com",
description=("Web annotation plugin platform"),
license="Apache 2.0",
keywords=[
],
url="https://github.com/aplbrain/webanno/tarball/" + VERSION,
packages=['webanno'],
scripts=[
'scripts/webanno'
],
classifiers=[],
install_requires=[
'flask', 'flask_cors'
],
)
Binary file added saber/webanno/test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions saber/webanno/webanno/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import sys
import importlib
import json
from flask import Flask, request, render_template
from flask_cors import CORS


from webanno.plugins import Plugin


def shutdown_server():
func = request.environ.get("werkzeug.server.shutdown")
if func is None:
raise RuntimeError("Not running with the Werkzeug Server")
func()


def attach_plugin(plugin: Plugin, app: Flask):
CORS(app)

def _prompt():
return plugin.prompt().replace("[[SUBMIT_URL]]", "http://localhost:5000/submit")

def _submit():
plugin.collect(request.json, request)
shutdown_server()
return "ok"

for path, route_fn in plugin.routes().items():
app.add_url_rule("/plugin/" + path.lstrip("/"), path, route_fn)

app.add_url_rule("/", "prompt", _prompt)
app.add_url_rule("/submit", "submit", _submit, methods=["POST"])


def run():
APP = Flask(__name__)
config = json.load(open(sys.argv[1], "r"))
plugin = importlib.import_module(config["plugin"])
attach_plugin(plugin._Plugin(config["config"]), APP)
APP.run(debug=True)
Loading