Commit 1f3450a9 authored by Daniele Venzano's avatar Daniele Venzano

Start visual refactoring

parent 3345d3d5
......@@ -39,6 +39,7 @@ def web_init(api_endpoint) -> List[tornado.web.URLSpec]:
tornado.web.url(r'/login', zoe_api.web.start.LoginWeb, route_args, name='login'),
tornado.web.url(r'/logout', zoe_api.web.start.LogoutWeb, route_args, name='logout'),
tornado.web.url(r'/executions', zoe_api.web.executions.ExecutionListWeb, route_args, name='execution_list'),
tornado.web.url(r'/executions/new', zoe_api.web.executions.ExecutionDefineWeb, route_args, name='execution_define'),
tornado.web.url(r'/executions/start', zoe_api.web.executions.ExecutionStartWeb, route_args, name='execution_start'),
tornado.web.url(r'/executions/restart/([0-9]+)', zoe_api.web.executions.ExecutionRestartWeb, route_args, name='execution_restart'),
......
......@@ -67,6 +67,30 @@ class ExecutionStartWeb(ZoeRequestHandler):
self.redirect(self.reverse_url('execution_inspect', new_id))
class ExecutionListWeb(ZoeRequestHandler):
"""Handler class"""
def initialize(self, **kwargs):
"""Initializes the request handler."""
super().initialize(**kwargs)
self.api_endpoint = kwargs['api_endpoint'] # type: APIEndpoint
@catch_exceptions
def get(self):
"""Home page with authentication."""
uid, role = get_auth(self)
if uid is None:
return self.redirect(self.get_argument('next', u'/login'))
executions = self.api_endpoint.execution_list(uid, role)
template_vars = {
"uid": uid,
"role": role,
'executions': sorted(executions, key=lambda e: e.id)
}
self.render('execution_list.html', **template_vars)
class ExecutionRestartWeb(ZoeRequestHandler):
"""Handler class"""
def initialize(self, **kwargs):
......
......@@ -86,22 +86,16 @@ class HomeWeb(ZoeRequestHandler):
if uid is None:
return self.redirect(self.get_argument('next', u'/login'))
if role == 'guest':
return self._aml_homepage(uid)
filters = {
"user_id": uid,
"limit": 5,
executions = self.api_endpoint.execution_list(uid, role)
}
executions = self.api_endpoint.execution_list(uid, role, **filters)
template_vars = {
"uid": uid,
"role": role,
'executions': sorted(executions, key=lambda e: e.id),
'is_admin': role == 'admin',
'executions': sorted(executions, key=lambda e: e.id)
}
self.render('home_user.html', **template_vars)
def _aml_homepage(self, uid):
"""Home page for students of the AML course."""
template_vars = {
'uid': uid
}
return self.render('home_guest.html', **template_vars)
......@@ -14,94 +14,73 @@ a:visited {
text-decoration: none;
}
div.user_info {
font-size: smaller;
clear: both;
padding-top: 20px;
}
/* header */
table.app_list {
border-collapse: collapse;
div.header {
width: 100%;
height: 5em;
display: flex;
align-items: center;
border-bottom: 1px solid rgba(79, 140, 30, 1);
}
table.app_list tr {
border-top: 1px black solid;
#logos img {
height: 4em;
}
table.app_list td {
padding-right: 1.5em;
#user_info {
padding-right: 1em;
padding-top: 0.4em;
position: absolute;
right: 0;
}
table.app_list tr.even {
background-color: #F3FEEA;
#nav {
font-size: 1.4em;
}
div.status_line {
.nav-item {
float: left;
padding-left: 1em;
}
#footer {
font-size: smaller;
}
#wrapper {
width: 800px;
.nav-item:hover a {
color: #000;
}
#navigation {
background-color: #fff;
border: #ddd 1px solid;
border-radius: 10px;
margin: 10px;
padding: 10px;
/* content */
#content {
clear: both;
}
#navigation li {
margin: 2px 0;
table.app_list {
border-collapse: collapse;
}
label.error {
color: #ff0000;
margin-left: 10px;
position: relative;
table.app_list tr {
border-top: 1px black solid;
}
.navigation {
border-top: #ddd 1px solid;
margin-top: 10px;
padding-top: 10px;
table.app_list td {
padding-right: 1.5em;
}
.navigation ul {
margin: 0;
padding: 0;
list-style: none;
table.app_list tr.even {
background-color: #F3FEEA;
}
.navigation li {
div.status_line {
float: left;
margin-right: 10px;
}
.clearfix:before, .clearfix:after {
content: "\0020";
display: block;
height: 0;
visibility: hidden;
}
.clearfix:after {
clear: both;
#footer {
font-size: smaller;
}
input {
margin-top: 5px;
}
section {
padding-bottom: 10px;
}
#loginbox {
width: 25em;
margin: 0 auto;
......@@ -112,10 +91,6 @@ section {
width: 100%;
}
fieldset {
border: 0;
}
textarea.logoutput {
width: 100%;
height: 40em;
......@@ -126,14 +101,6 @@ textarea.logoutput {
overflow-y: auto;
}
#userinfo {
position: absolute;
top: 0;
right: 0;
padding-right: 1em;
padding-top: 0.4em;
}
.readable_description {
display: none;
}
......@@ -20,11 +20,8 @@
<div class="status_line">
<p>
<span id="app_name">
<a href="http://zoe-analytics.eu">Zoe</a> v. {{ zoe_version }}
<a href="http://zoe-analytics.eu">Zoe Analytics</a> v. {{ zoe_version }}
</span>
<!-- <span id="status">
[<span id="num_nodes">N/A</span> swarm nodes, <span id="num_containers">N/A</span> active containers]
</span> -->
&nbsp;&mdash;&nbsp;
<span>
<a href="https://github.com/DistributedSystemsGroup/zoe/issues">Give us some feedback!</a>
......
{% extends "base.html" %}
{% block content_header %}
{% if uid %}
<div id="userinfo">
<div class="header">
<div id="logos">
<img class="logo" src="/static/logo.png" alt="Zoe logo"/>
</div>
<div id="nav">
<div class="nav-item">
<a href="{{ reverse_url("home_user") }}">Home</a>
</div>
<div class="nav-item">
<a href="{{ reverse_url("zappshop") }}">ZApp shop</a>
</div>
<div class="nav-item">
<a href="{{ reverse_url("execution_list") }}">My executions</a>
</div>
</div>
<div id="user_info">
{{ uid }} ({{ role }}) <a href="{{ reverse_url("logout") }}">logout</a>
</div>
{% endif %}
</div>
{% endblock %}
{% block content %}
......
{% extends "base.html" %}
{% extends "base_user.html" %}
{% block title %}Error{% endblock %}
{% block content %}
<h1 style="color: red">Error!</h1>
<h1 style="color: red;">Error!</h1>
<p>{{ error }}</p>
......
{% extends "base_user.html" %}
{% block title %}Home{% endblock %}
{% block custom_head %}
<script src="/static/sorttable.js" type="application/javascript"></script>
<script src="/static/moment.min.js" type="application/javascript"></script>
<script>
moment.locale(window.navigator.userLanguage || window.navigator.language);
function format_timestamp(ts) {
document.write(moment(ts).calendar())
}
</script>
{% endblock %}
{% block content %}
<div id="my_executions">
<h3>Executions</h3>
<p><a href="{{ reverse_url('execution_define') }}">New execution</a></p>
<table id="exec_list" class="app_list sortable">
<thead>
<tr>
<th>ID</th>
<th>Execution name</th>
{% if is_admin %}
<th>User</th>
{% endif %}
<th>Status</th>
<th>Scheduled</th>
<th>Started</th>
<th>Finished</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for e in executions %}
<tr class="{{ loop.cycle('odd', 'even') }}">
<td>{{ e.id }}</td>
<td><a href="/executions/inspect/{{ e.id }}">{{ e.name }}</a></td>
{% if is_admin %}
<td>{{ e.user_id }}</td>
{% endif %}
<td>{{ e.status }}</td>
<td><script>format_timestamp("{{ e.time_submit }}")</script></td>
{% if e.time_start == None %}
<td>not yet</td>
{% else %}
<td><script>format_timestamp("{{ e.time_start }}")</script></td>
{% endif %}
{% if e.time_end == None %}
<td>not yet</td>
{% else %}
<td><script>format_timestamp("{{ e.time_end }}")</script></td>
{% endif %}
{% if e.is_active %}
<td><a href="/executions/terminate/{{ e.id }}">Terminate</a></td>
{% else %}
<td><a href="/executions/restart/{{ e.id }}">Restart</a></td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block custom_head %}
<script type="application/javascript">
const AJAX_URL = "{{ reverse_url('ajax') }}";
const SLOW_UPDATE = 60000;
const FAST_UPDATE = 1000;
let update_interval = null;
function ajax(data, success_cb) {
$.ajax({
url: AJAX_URL,
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
async: true,
success: success_cb,
error: function () {
show_error('AJAX communication error, the operation will be retried');
}
});
}
function show_error(msg) {
let error_box = $("#ajax-error");
error_box.text("Error: " + msg);
error_box.show();
}
let state = "init";
let execution_id = -1;
function state_machine() {
if (state == "init") {
clearInterval(update_interval);
update_interval = setInterval(function(){update_zoe_status();}, FAST_UPDATE);
$("#state-init").show();
$("#state-starting").hide();
$("#state-started").hide();
ajax({'type': 'start'},
function (data) {
if (data['status'] == 'ok') {
$("#ajax-error").hide();
state = "starting";
execution_id = data['execution_id'];
} else {
show_error(data.message);
}
},
function () {
show_error('AJAX communication error, the operation will be retried');
}
);
} else if (state == "starting") {
$("#state-init").hide();
$("#state-starting").show();
$("#state-started").hide();
} else if (state == "started") {
clearInterval(update_interval);
update_interval = setInterval(function(){update_zoe_status();}, SLOW_UPDATE);
$("#state-init").hide();
$("#state-starting").hide();
$("#state-started").show();
}
}
function update_zoe_status() {
if (execution_id < 0) {
$("#zoe-status").text('off');
state = "init";
} else {
ajax({'type': 'query_status', 'exec_id': execution_id},
function (data) {
if (data['status'] == 'ok') {
$("#ajax-error").hide();
$("#zoe-status").text(data['exec_status']);
if (data['exec_status'] == 'running') {
state = "started";
$('#time_remaining').text(moment.duration(data['ttl'] * 1000).humanize());
let s = "";
for (let ep of data['endpoints']) {
s += "<li><a href=\"" + ep[1] + "\">" + ep[0] + "</a></li>\n";
}
$("#endpoints").html(s);
} else if (data['exec_status'] == 'terminated' || data['exec_status'] == 'none' || data['exec_status'] == 'error') {
state = "init";
update_interval = setInterval(function(){update_zoe_status();}, FAST_UPDATE);
}
} else {
show_error(data.message);
}
}
);
}
state_machine();
}
state_machine();
</script>
<style>
body {
width: 80%;
}
.state-box {
border: 1px solid black;
margin-top: 2em;
margin-bottom: 2em;
width: 40%;
padding-left: 10px;
padding-right: 10px;
}
</style>
{% endblock %}
{% block content %}
<h2>Algorithmic Machine Learning cluster management</h2>
<p>You are logged in as {{ uid }}.</p>
<p>Through this page you will be able to access the Jupyter notebook web interface, which you will use to upload and work on the notebooks provided on the <a href="https://github.com/DistributedSystemsGroup/Algorithmic-Machine-Learning">Algorithmic Machine Learning course GitHub page</a>.</p>
<p>The work environment contains also an Apache Spark cluster and is created dynamically when you first access this page. After a fixed amount of time, the resources are freed and the Notebook and Spark are terminated. The files you saved in your workspace will be available for your next session.</p>
<span style="color: darkred; display: none;" id="ajax-error">AJAX communication error, retrying...</span>
<div class="state-box">
<div id="state-init">
<p>Checking your cluster status...</p>
</div>
<div id="state-starting" style="display: none;">
<p>Please wait, your cluster is <span id="zoe-status">...</span></p>
</div>
<div id="state-started" style="display: none;">
<p>Your cluster is running, it will be destroyed in about <span id="time_remaining"></span></p>
<ul id="endpoints"></ul>
</div>
</div>
<p>Useful resources:</p>
<ul>
<li><a href="https://github.com/DistributedSystemsGroup/Algorithmic-Machine-Learning">Algorithmic Machine Learning course GitHub page</a></li>
<li><a href="https://spark.apache.org/docs/1.5.2/api/python/index.html">Spark Python API</a></li>
</ul>
{% endblock %}
......@@ -14,11 +14,8 @@
{% endblock %}
{% block content %}
<h1>Zoe - Analytics on demand</h1>
<div id="my_executions">
<h3>Executions</h3>
<p><a href="{{ reverse_url('execution_define') }}">New execution</a></p>
<table id="exec_list" class="app_list sortable">
<thead>
<tr>
......
{% extends "base.html" %}
{% block title %}Start{% endblock %}
{% block content %}
<p>Welcome to Zoe</p>
<ul>
<li><a href="{{ reverse_url('home_user') }}">User start page</a></li>
</ul>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment