Skip to content

Commit

Permalink
Refactor and add loading latest model from folder
Browse files Browse the repository at this point in the history
  • Loading branch information
vrachieru committed Jul 28, 2019
1 parent 08d8411 commit 1cf0e91
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 53 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
Simple webserver for externalizing RASA models.
</p>

### About

You can [configure RASA to fetch models](https://rasa.com/docs/rasa/user-guide/running-the-server/#fetching-models-from-a-server) from this server either by:
1. pointing to a specific model (`.tar.gz`) and overriding said file when you want the model to change
`http://localhost:8080/bot/model.tar.gz`
2. pointing to a folder (suffixing the url with `@latest`) containing multiple models (`.tar.gz`) and getting the latest model sorted by modified date
`http://localhost:8080/bot@latest`

### Quick start

Expand Down
75 changes: 26 additions & 49 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,45 @@
from os import scandir, listdir, environ
from os.path import isfile, isdir, dirname, basename, relpath, join, exists, getsize
from datetime import datetime
from flask import Flask, render_template, send_from_directory
from os import sep
from os.path import isdir, dirname, basename, relpath, join, exists

from config import models_dir, server_port
from filesystem import Scaner

class Scaner:
def __init__(self, path):
self.entries = [Entry(entry) for entry in scandir(path.encode())]

class Entry:
def __init__(self, entry):
self.name = entry.name.decode()
self.path = entry.path.decode()
self.rel_path = relpath(self.path, models_dir)
self.is_dir = entry.is_dir()
self.created_time = datetime.fromtimestamp(entry.stat().st_ctime).ctime()
self.modified_time = datetime.fromtimestamp(entry.stat().st_mtime).ctime()
self.size = self._human_readable_size(self._get_size(entry.path))

def _get_size(self, path):
total_size = getsize(path)
if isdir(path):
for item in listdir(path):
item_path = join(path, item)
if isfile(item_path):
total_size += getsize(item_path)
elif isdir(item_path):
total_size += self._get_size(item_path)
return total_size

def _human_readable_size(self, size):
units = ['B', 'KB', 'MB', 'GB', 'TB']
human_fmt = '{0:.2f} {1}'
human_radix = 1024.

for unit in units[:-1]:
if size < human_radix:
return human_fmt.format(size, unit)
size /= humanradix

return human_fmt.format(size, units[-1])


models_dir = environ.get('MODELS_DIR', 'models')

app = Flask(__name__)

@app.route('/', methods=['GET'])
def index():
return render_template('index.html', path='/', entries=Scaner(models_dir).entries)
return list_dir(models_dir)

@app.route('/<path:path>', methods=['GET'])
def serve(path):
real_path = join(models_dir, path)
parent_path = relpath(dirname(real_path), models_dir)

fetch_latest = '@latest' in path
real_path = join(models_dir, path.replace('@latest', ''))
if not exists(real_path):
return 'Not Found', 404

if isdir(real_path):
return render_template('index.html', parent_path=parent_path, path=path, entries=Scaner(real_path).entries)
if fetch_latest:
latest_entry = Scaner(real_path).latest_entry
if not latest_entry:
return 'No Models Found', 404
return download_file(latest_entry.path)
else:
return list_dir(real_path)
else:
return send_from_directory(dirname(real_path), basename(real_path), as_attachment=True, attachment_filename=basename(real_path))
return download_file(real_path)


def list_dir(path):
rel_path = relpath(path, models_dir)
parent_path = dirname(rel_path)
return render_template('index.html', sep=sep, parent_path=parent_path, path=rel_path, entries=Scaner(path).entries)

def download_file(path):
return send_from_directory(dirname(path), basename(path), as_attachment=True, attachment_filename=basename(path))


if __name__ == '__main__':
app.run(host='0.0.0.0', port=environ.get('PORT', 8080))
app.run(host='0.0.0.0', port=server_port)
4 changes: 4 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from os import environ

models_dir = environ.get('MODELS_DIR', 'models')
server_port = environ.get('PORT', 8080)
43 changes: 43 additions & 0 deletions filesystem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from datetime import datetime
from os import scandir, listdir
from os.path import isfile, isdir, relpath, join, getsize

from config import models_dir

class Scaner:
def __init__(self, path):
self.entries = [Entry(entry) for entry in scandir(path.encode())]
self.latest_entry = next(iter(sorted(self.entries, key=lambda e: e.modified_time, reverse=True)), None)

class Entry:
def __init__(self, entry):
self.name = entry.name.decode()
self.path = entry.path.decode()
self.rel_path = relpath(self.path, models_dir)
self.is_dir = entry.is_dir()
self.created_time = datetime.fromtimestamp(entry.stat().st_ctime).ctime()
self.modified_time = datetime.fromtimestamp(entry.stat().st_mtime).ctime()
self.size = self._human_readable_size(self._get_size(entry.path))

def _get_size(self, path):
total_size = getsize(path)
if isdir(path):
for item in listdir(path):
item_path = join(path, item)
if isfile(item_path):
total_size += getsize(item_path)
elif isdir(item_path):
total_size += self._get_size(item_path)
return total_size

def _human_readable_size(self, size):
units = ['B', 'KB', 'MB', 'GB', 'TB']
human_fmt = '{0:.2f} {1}'
human_radix = 1024.

for unit in units[:-1]:
if size < human_radix:
return human_fmt.format(size, unit)
size /= human_radix

return human_fmt.format(size, units[-1])
8 changes: 4 additions & 4 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@
<h1>{{ path }}</h1>
<hr/>
<table>
{% if parent_path %}
{% if path != '.' %}
<tr>
<td>
<a href="{{ url_for('serve', path=parent_path) }}">../</a>
<a href="{{ url_for('serve', path=parent_path) | replace('%5C', '/') }}">..{{ sep }}</a>
</td>
</tr>
{% endif %}
{% for entry in entries %}
<tr>
<td>
<a href="{{ url_for('serve', path=entry.rel_path) }}">
{{ entry.name|truncate(50, True) }}{% if entry.is_dir %}/{% endif %}
<a href="{{ url_for('serve', path=entry.rel_path) | replace('%5C', '/') }}">
{{ entry.name|truncate(50, True) }}{% if entry.is_dir %}{{ sep }}{% endif %}
</a>
</td>
<td>{{ entry.created_time }}</td>
Expand Down

0 comments on commit 1cf0e91

Please sign in to comment.