From 2d76928b93c208d27f942b9a9eb649df1d5b2192 Mon Sep 17 00:00:00 2001 From: Oleg Broytman Date: Sun, 1 Apr 2018 01:38:28 +0300 Subject: [PATCH] Feat: Web UI Start developing web UI using bottle microframework. Implement simple server. --- devscripts/requirements/requirements.txt | 1 + docs-ru/command_line.rst | 17 ++++++++++++++ docs/command_line.rst | 16 +++++++++++++ m_librarian/web/__init__.py | 1 + m_librarian/web/app.py | 6 +++++ m_librarian/web/server.py | 26 ++++++++++++++++++++ m_librarian/web/utils.py | 10 ++++++++ scripts/ml-web.py | 30 ++++++++++++++++++++++++ setup.py | 6 +++-- 9 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 m_librarian/web/__init__.py create mode 100644 m_librarian/web/app.py create mode 100644 m_librarian/web/server.py create mode 100644 m_librarian/web/utils.py create mode 100755 scripts/ml-web.py diff --git a/devscripts/requirements/requirements.txt b/devscripts/requirements/requirements.txt index 93bfbe4..3c9963b 100644 --- a/devscripts/requirements/requirements.txt +++ b/devscripts/requirements/requirements.txt @@ -5,3 +5,4 @@ SQLObject>=2.2.1; python_version >= '2.7' and python_version < '3.0' SQLObject>=3.0.0; python_version >= '3.4' m_lib.defenc>=1.0 +bottle diff --git a/docs-ru/command_line.rst b/docs-ru/command_line.rst index c646cde..2a68661 100644 --- a/docs-ru/command_line.rst +++ b/docs-ru/command_line.rst @@ -257,4 +257,21 @@ ml-search.py При использовании опции `-v` также выводится id из БД. + +ml-web.py +------------ + +Использование:: + + ml-web.py [-p port] + +Опции:: + + -p, --port port Порт протокола HTTP + +Запускает web-сервер. Если указан порт, то используется указанный порт. +Иначе выбирается случайный порт из числа свободных. Программа запускает +браузер (или открывает новое окно уже запущенного web-обозревателя) с +адресом, указывающим на сервер. + .. vim: set tw=72 : diff --git a/docs/command_line.rst b/docs/command_line.rst index 56824a9..4bde53d 100644 --- a/docs/command_line.rst +++ b/docs/command_line.rst @@ -253,4 +253,20 @@ Options:: With one option `-v` it also prints database id. + +ml-web.py +------------ + +Usage:: + + ml-web.py [-p port] + +Options:: + + -p, --port port HTTP port to listen to + +Run a web server. If a port is given listen on the given port. Else +choose a random port. Start a browser (or open a new window of a running +browser) pointing it to the server. + .. vim: set tw=72 : diff --git a/m_librarian/web/__init__.py b/m_librarian/web/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/m_librarian/web/__init__.py @@ -0,0 +1 @@ +# diff --git a/m_librarian/web/app.py b/m_librarian/web/app.py new file mode 100644 index 0000000..01b15dd --- /dev/null +++ b/m_librarian/web/app.py @@ -0,0 +1,6 @@ +from bottle import route + + +@route('/') +def hello(): + return "Hello World!" diff --git a/m_librarian/web/server.py b/m_librarian/web/server.py new file mode 100644 index 0000000..bc8e552 --- /dev/null +++ b/m_librarian/web/server.py @@ -0,0 +1,26 @@ +from bottle import run + +from wsgiref import simple_server +from wsgiref.handlers import SimpleHandler +from wsgiref.simple_server import WSGIServer +from bottle import route + +simple_server.ServerHandler = SimpleHandler # Stop logging to stdout + + +class QuitWSGIServer(WSGIServer): + _quit_flag = False + + def serve_forever(self): + while not self._quit_flag: + self.handle_request() + + +@route('/quit') +def quit(): + QuitWSGIServer._quit_flag = True + return "The program has finished. Have a nice day!" + + +def run_server(host='localhost', port=0): + run(host=host, port=port, server_class=QuitWSGIServer, debug=True) diff --git a/m_librarian/web/utils.py b/m_librarian/web/utils.py new file mode 100644 index 0000000..2efe724 --- /dev/null +++ b/m_librarian/web/utils.py @@ -0,0 +1,10 @@ + +def get_open_port(): + # https://stackoverflow.com/a/2838309/7976758 + import socket + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.bind(("", 0)) + # s.listen(1) + port = s.getsockname()[1] + s.close() + return port diff --git a/scripts/ml-web.py b/scripts/ml-web.py new file mode 100755 index 0000000..f5a579e --- /dev/null +++ b/scripts/ml-web.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python + +import argparse +import time +import webbrowser + +from bottle import thread # portable import + +import m_librarian.web.app # noqa: F401 imported but unused +from m_librarian.web.server import run_server +from m_librarian.web.utils import get_open_port + + +def start_browser(port): + time.sleep(1) # A small timeout to allow the main thread to run the server + webbrowser.open_new('http://localhost:%d/' % port) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Init') + parser.add_argument('-p', '--port', help='HTTP server port') + args = parser.parse_args() + + if args.port: + port = args.port + else: + port = get_open_port() + + thread.start_new_thread(start_browser, (port,)) + run_server(port=port) diff --git a/setup.py b/setup.py index 29a8ec4..d342061 100755 --- a/setup.py +++ b/setup.py @@ -44,14 +44,14 @@ setup(name='m_librarian', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], - packages=['m_librarian'], + packages=['m_librarian', 'm_librarian.web'], package_data={'m_librarian': [ 'glst/*.txt', 'glst/genres_*.glst', 'translations/*.mo' ] }, scripts=['scripts/ml-import.py', 'scripts/ml-initdb.py', - 'scripts/ml-search.py'], + 'scripts/ml-search.py', 'scripts/ml-web.py'], python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=[ 'SQLObject>=2.2.1; python_version=="2.7"', @@ -61,5 +61,7 @@ setup(name='m_librarian', extras_require={ 'm_lib': ['m_lib>=3.1'], 'pbar': ['m_lib>=3.1'], + 'web': ['bottle'], + 'bottle': ['bottle'], }, ) -- 2.39.2