Skip to content

Commit

Permalink
refactoring examples to use riverapi (#2)
Browse files Browse the repository at this point in the history
Signed-off-by: vsoch <vsoch@users.noreply.github.com>
Co-authored-by: vsoch <vsoch@users.noreply.github.com>
  • Loading branch information
vsoch and vsoch committed Feb 28, 2022
1 parent a706b5b commit 4081cb4
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 91 deletions.
26 changes: 26 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"projectName": "django-river-ml",
"projectOwner": "vsoch",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "none",
"contributors": [
{
"login": "vsoch",
"name": "Vanessasaurus",
"contributions": [
"code"
],
"profile": "https://vsoch.github.io",
"avatar_url": [
"https://avatars.githubusercontent.com/u/814322?v=4"
]
}
],
"contributorsPerLine": 7
}
54 changes: 54 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Contributing

Contributions are welcome, and they are greatly appreciated! Every
little bit helps, and credit will always be given.

You can contribute in many ways:

## Types of Contributions

### Report Bugs

Report bugs at https://github.com/vsoch/django-river-ml/issues.

If you are reporting a bug, please include:

* Your operating system name and version.
* Any details about your local setup that might be helpful in troubleshooting.
* Detailed steps to reproduce the bug.

### Fix Bugs
Look through the GitHub issues for bugs. Anything tagged with "bug"
is open to whoever wants to implement it.

### Implement Features

Look through the GitHub issues for features. Anything tagged with "feature"
is open to whoever wants to implement it.

### Write Documentation

django-river-ml could always use more documentation, whether as part of the
official django-river-ml docs, in docstrings, or even on the web in blog posts,
articles, and such.

### Submit Feedback

The best way to send feedback is to file an issue at https://github.com/vsoch/django-river-ml/issues.

If you are proposing a feature:

* Explain in detail how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that contributions
are welcome :)

## Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

1. The pull request should include tests.
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.rst.
3. The pull request should work for minimally Python 3.5 and for PyPy.
31 changes: 31 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Release Python Package

on:
release:
types: [created]

jobs:
deploy:
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2

- name: Install
run: conda create --quiet --name djriver twine

- name: Install dependencies
run: |
export PATH="/usr/share/miniconda/bin:$PATH"
source activate djriver
pip install -e .
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USER }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASS }}
run: |
export PATH="/usr/share/miniconda/bin:$PATH"
source activate djriver
python setup.py sdist bdist_wheel
twine upload dist/*
82 changes: 82 additions & 0 deletions .github/workflows/update-contributors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: allcontributors-auto-detect

on:
push:
branches:
- main

jobs:
Update:
name: Generate
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Tributors Update

# Important! Update to release https://github.com/con/tributors
uses: con/tributors@0.0.20
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

# Single text list (space separated) of parsers, leave unset to auto-detect
parsers: unset

# Update lookup with GitHub metadata
update_lookup: github

# INFO, DEBUG, ERROR, WARNING, etc.
log_level: DEBUG

# If files already exist and an init is done, force overwrite
force: true

# the minimum number of contributions required to add a user
threshold: 1

- name: Checkout New Branch
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH_AGAINST: "main"
run: |
printf "GitHub Actor: ${GITHUB_ACTOR}\n"
export BRANCH_FROM="contributors/update-$(date '+%Y-%m-%d')"
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
BRANCH_EXISTS=$(git ls-remote --heads origin ${BRANCH_FROM})
if [[ -z ${BRANCH_EXISTS} ]]; then
printf "Branch does not exist in remote.\n"
else
printf "Branch already exists in remote.\n"
exit 1
fi
git branch
git checkout -b "${BRANCH_FROM}" || git checkout "${BRANCH_FROM}"
git branch
git config --global user.name "github-actions"
git config --global user.email "github-actions@users.noreply.github.com"
git status
if git diff-index --quiet HEAD --; then
export OPEN_PULL_REQUEST=0
printf "No changes\n"
else
export OPEN_PULL_REQUEST=1
printf "Changes\n"
git commit -a -m "Automated deployment to update contributors $(date '+%Y-%m-%d')"
git push origin "${BRANCH_FROM}"
fi
echo "OPEN_PULL_REQUEST=${OPEN_PULL_REQUEST}" >> $GITHUB_ENV
echo "PULL_REQUEST_FROM_BRANCH=${BRANCH_FROM}" >> $GITHUB_ENV
echo "PULL_REQUEST_TITLE=[tributors] ${BRANCH_FROM}" >> $GITHUB_ENV
echo "PULL_REQUEST_BODY=Tributors update automated pull request." >> $GITHUB_ENV
- name: Open Pull Request
uses: vsoch/pull-request-action@1.0.12
if: ${{ env.OPEN_PULL_REQUEST == '1' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_BRANCH: "main"
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,35 @@
Django models to deploy [river](https://riverml.xyz) online machine learning.
This is a Django version of [chantilly](https://github.com/online-ml/chantilly) that aims to use the
same overall design. We also include [example clients](examples/) and a test application in [tests](tests).
We also are developing an [API client](https://github.com/vsoch/riverapi) and early work on a specification that can be extended to other
Python based servers intended for river.

See the ⭐️ [Documentation](https://vsoch.github.io/django-river-ml/) ⭐️ to get started!

## Contributors

We use the [all-contributors](https://github.com/all-contributors/all-contributors)
tool to generate a contributors graphic below.

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->

<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->


## TODO

- tests
- should we have a server generic client to plug in here instead?
- create client that can easily interact with API
- add some basic set of frontend views?
- do we want a spec? [issue](https://github.com/online-ml/river/issues/845)
- clean up docstrings -> docs and python docs -> envars list and how to define -> pretty docs
- implement more examples?
- add and test authenticated views
- do we want a default interface for something?
- upload to pypi and automated release after that

5 changes: 3 additions & 2 deletions django_river_ml/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,10 @@ def update_metrics(self, prediction, ground_truth, model_name):
pred = max(prediction, key=prediction.get)
metric.update(y_true=ground_truth, y_pred=pred)

# In some cases the prediction dict either has or doesn't have the ground truth
# is this correct?
# In some cases we have a dict that isn't a Classification Metric
elif isinstance(prediction, dict):

# The ground truth may not be present
y_pred = prediction.get(ground_truth)
if y_pred:
metric.update(y_true=ground_truth, y_pred=y_pred)
Expand Down
11 changes: 7 additions & 4 deletions django_river_ml/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
# Default views to Authenticate
# TODO update here
authenticated_views = [
# "django_river_ml.views.learn.",
# "django_river_ml.views.blobs.BlobDownload",
# "django_river_ml.views.image.ImageTags",
# "django_river_ml.views.image.ImageManifest",
# "django_river_ml.views.learn.view",
# "django_river_ml.views.metrics.view",
# "django_river_ml.views.model.view",
# "django_river_ml.views.predict.view",
# "django_river_ml.views.metrics.view",
# "django_river_ml.views.metrics.stream_events",
# "django_river_ml.views.metrics.stream_metrics",
]

# Defaults for models and storage
Expand Down
10 changes: 9 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# Examples

The following examples will show you how to interact with the test application
provided. The subdirectories are named based on the model type. E.g., for
provided. All examples use the [riverapi](https://github.com/vsoch/riverapi)
library that you can install from pip.

```bash
$ pip install riverapi
```

The subdirectories are named based on the model type. E.g., for
"regression" make sure you set the settings.py `MODEL_FLAVOR` to regression first.
And then start the server:

Expand All @@ -10,3 +17,4 @@ $ python manage.py runserver
```

- [regression](regression)

2 changes: 1 addition & 1 deletion examples/binary/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
requests
riverapi
57 changes: 19 additions & 38 deletions examples/binary/run.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,46 @@
from river import datasets
from river import naive_bayes
from river import preprocessing
from river import stream

import json
import dill
import requests


def print_response(r):
assert r.status_code in [200, 201]
response = r.json()
print("%s: %s" % (r.url, json.dumps(response, indent=4)))
from riverapi.main import Client


def main():
host = "http://localhost:8000"

# This is the default, just to show how to customize
cli = Client("http://localhost:8000")

# Upload a model
model = preprocessing.StandardScaler() | naive_bayes.GaussianNB()
r = requests.post(host + "/api/model/binary/", data=dill.dumps(model))
model_name = r.json()["name"]
print_response(r)

# Save the model name for other endpoint interaction
model_name = cli.upload_model(model, "binary")
print("Created model %s" % model_name)

# Train on some data
X = [[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]]
Y = [1, 1, 1, 2, 2, 2]

for x, y in stream.iter_array(X, Y):
r = requests.post(
host + "/api/learn/",
json={"model": model_name, "features": x, "ground_truth": y},
)
print_response(r)
cli.learn(model_name, x=x, y=y)

# Get the model (this is a json representation)
r = requests.get(host + "/api/model/%s/" % model_name)
assert r.status_code == 200
print(json.dumps(r.json(), indent=4))

# Get the model (this is a download of the pickled model with dill)
r = requests.get(host + "/api/model/download/%s/" % model_name)
assert r.status_code == 200
model_json = cli.get_model_json(model_name)
model_json

# Here is how to save and load
# with open("%s.pkl" % model_name, 'wb') as f:
# for chunk in r:
# f.write(chunk)

# with open("muffled-pancake-9439.pkl", "rb") as fd:
# content=pickle.load(fd)
# Saves to model-name>.pkl in pwd unless you provide a second arg, dest
cli.download_model(model_name)

# Make predictions
for x, y in stream.iter_array(X, Y):
r = requests.post(
host + "/api/predict/",
json={"model": model_name, "features": x},
)
print_response(r)
print(cli.predict(model_name, x=x))

# Get all models
print(cli.models())


if __name__ == "__main__":
main()

if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion examples/regression/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
requests
riverapi

0 comments on commit 4081cb4

Please sign in to comment.