Skip to content

Commit

Permalink
Merge pull request #81 from ottogroup/feature/activate-web-service
Browse files Browse the repository at this point in the history
Add 'activate' web service endpoint
  • Loading branch information
alattner committed May 18, 2018
2 parents 1c750ed + 1099664 commit af9aaa8
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 3 deletions.
11 changes: 9 additions & 2 deletions docs/user/web-service.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ some example output:
"properties": {"active-model": "8", "db-version": "1.2"}
}
Fit and Update Model Cache
--------------------------
Fit, Update Model Cache, and Activate
-------------------------------------

Palladium allows for periodic updates of the model by use of the
:class:`palladium.persistence.CachedUpdatePersister`. For this to
Expand Down Expand Up @@ -283,3 +283,10 @@ endpoints is this:
'methods': ['POST'],
},
],
Another endpoint that's not registered by default is */activate*,
which works just like its command line counterpart: it takes a model
version and activates it in the model persister such that the next
prediction will use the active model. The handler can be found at
:func:`palladium.server.activate`. It requires a request parameter
called ``model_version``.
12 changes: 12 additions & 0 deletions palladium/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from werkzeug.exceptions import BadRequest

from . import __version__
from .fit import activate as activate_base
from .fit import fit as fit_base
from .interfaces import PredictError
from .util import args_from_config
Expand Down Expand Up @@ -413,6 +414,17 @@ def update_model_cache(model_persister):
return make_ujson_response({}, status_code=503)


@PluggableDecorator('activate_decorators')
def activate():
model_version = int(request.form['model_version'])
try:
activate_base(model_version=model_version)
except LookupError:
return make_ujson_response({}, status_code=503)
else:
return list()


def add_url_rule(rule, endpoint=None, view_func=None, app=app, **options):
if isinstance(view_func, str):
view_func = resolve_dotted_name(view_func)
Expand Down
39 changes: 39 additions & 0 deletions palladium/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,45 @@ def test_unavailable(self, update_model_cache, config, jobs, flask_app):
assert resp.status_code == 503


class TestActivateFunctional:
@pytest.fixture
def activate(self):
from palladium.server import activate
return activate

@pytest.fixture
def activate_base_mock(self, monkeypatch):
func = Mock()
monkeypatch.setattr('palladium.server.activate_base', func)
return func

def test_success(self, activate, activate_base_mock, config, flask_app):
model_persister = Mock(
list_models=lambda: {'be': 'first'},
list_properties=lambda: {'be': 'twice'},
)
config['model_persister'] = model_persister
with flask_app.test_request_context(
method='POST',
data={'model_version': 123},
):
resp = activate()
assert resp.status_code == 200
assert resp.json == {
'models': {'be': 'first'},
'properties': {'be': 'twice'},
}

def test_lookuperror(self, activate, activate_base_mock, flask_app):
activate_base_mock.side_effect = LookupError
with flask_app.test_request_context(
method='POST',
data={'model_version': 123},
):
resp = activate()
assert resp.status_code == 503


def _test_add_url_rule_func():
return b'A OK'

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ certifi==2018.4.16
chardet==3.0.4
click==6.7
docopt==0.6.2
flask==0.12.2
flask==1.0.2
idna==2.6
itsdangerous==0.24
Jinja2==2.10
Expand Down

0 comments on commit af9aaa8

Please sign in to comment.