Commit 79c271f8 authored by Daniele Venzano's avatar Daniele Venzano

Implement pagination for execution list page (closes #64 on github)

parent ea39c392
......@@ -11,7 +11,7 @@ variables:
POSTGRES_USER: zoeuser
POSTGRES_PASSWORD: zoepass
ZOE_TEST_IMAGE: zoe-test:$CI_PIPELINE_ID
ZOE_COMMON_OPTIONS: --debug --backend-swarm-url ${SWARM_URL} --deployment-name test${CI_BUILD_REF} --dbuser ${POSTGRES_USER} --dbhost postgres --dbport 5432 --dbname ${POSTGRES_DB} --dbpass ${POSTGRES_PASSWORD} --master-url tcp://localhost:4850 --auth-type text --listen-port 5100 --workspace-base-path /tmp
ZOE_COMMON_OPTIONS: --debug --deployment-name test${CI_BUILD_REF} --dbuser ${POSTGRES_USER} --dbhost postgres --dbport 5432 --dbname ${POSTGRES_DB} --dbpass ${POSTGRES_PASSWORD} --master-url tcp://localhost:4850 --auth-type text --listen-port 5100 --workspace-base-path /tmp
cache:
paths:
......
......@@ -17,6 +17,7 @@
import logging
import os
from typing import Mapping
import zoe_api.exceptions
import zoe_api.master_api
......@@ -51,11 +52,19 @@ class APIEndpoint:
raise zoe_api.exceptions.ZoeAuthException()
return e
def execution_list(self, uid, role, **filters):
def execution_list(self, uid: str, role: str, **filters: Mapping[str, str]):
"""Generate a optionally filtered list of executions."""
if role != 'admin':
filters['user_id'] = uid
execs = self.sql.executions.select(**filters)
ret = [e for e in execs if e.user_id == uid or role == 'admin']
return ret
return execs
def execution_count(self, uid: str, role: str, **filters: Mapping[str, str]):
"""Count the number of executions optionally filtered."""
if role != 'admin':
filters['user_id'] = uid
execs = self.sql.executions.count(**filters)
return execs
def zapp_validate(self, application_description):
"""Validates the passed ZApp description against the supported schema."""
......
......@@ -41,6 +41,7 @@ def web_init(api_endpoint) -> List[tornado.web.URLSpec]:
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/([0-9]+)', zoe_api.web.executions.ExecutionListWeb, route_args, name='execution_list_page'),
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'),
tornado.web.url(r'/executions/terminate/([0-9]+)', zoe_api.web.executions.ExecutionTerminateWeb, route_args, name='execution_terminate'),
......
......@@ -17,6 +17,7 @@
import datetime
import json
import math
import time
from zoe_lib.config import get_conf
......@@ -53,25 +54,32 @@ class ExecutionStartWeb(ZoeRequestHandler):
class ExecutionListWeb(ZoeRequestHandler):
"""Handler class"""
PAGINATION_ITEM_COUNT = 50
def initialize(self, **kwargs):
"""Initializes the request handler."""
super().initialize(**kwargs)
self.api_endpoint = kwargs['api_endpoint'] # type: APIEndpoint
@catch_exceptions
def get(self):
def get(self, page=0):
"""Home page with authentication."""
uid, role = get_auth(self)
if uid is None:
self.redirect(self.get_argument('next', u'/login'))
return
executions = self.api_endpoint.execution_list(uid, role)
page = int(page)
executions_count = self.api_endpoint.execution_count(uid, role)
executions = self.api_endpoint.execution_list(uid, role, base=page*self.PAGINATION_ITEM_COUNT, limit=self.PAGINATION_ITEM_COUNT)
template_vars = {
"uid": uid,
"role": role,
'executions': sorted(executions, key=lambda e: e.id, reverse=True)
'executions': sorted(executions, key=lambda e: e.id, reverse=True),
'current_page': page,
'max_page': math.ceil(executions_count / self.PAGINATION_ITEM_COUNT),
'last_page': len(executions) < self.PAGINATION_ITEM_COUNT
}
self.render('execution_list.html', **template_vars)
......
......@@ -17,6 +17,17 @@
{% block content %}
<div id="my_executions">
<label class="filter">All executions <input class="filter" placeholder="Filter" /></label>
{% if max_page > 0 %}
<p>Pages:
{% for page_n in range(0, max_page) %}
{% if page_n == current_page %}
{{ page_n + 1 }}&nbsp;
{% else %}
<a href="/executions/{{ page_n }}">{{ page_n + 1 }}</a>&nbsp;
{% endif %}
{% endfor %}
</p>
{% endif %}
<table id="exec_list" class="app_list sortable">
<thead>
<tr>
......@@ -61,6 +72,17 @@
{% endfor %}
</tbody>
</table>
{% if max_page > 0 %}
<p>Pages:
{% for page_n in range(0, max_page) %}
{% if page_n == current_page %}
{{ page_n + 1 }}&nbsp;
{% else %}
<a href="/executions/{{ page_n }}">{{ page_n + 1 }}</a>&nbsp;
{% endif %}
{% endfor %}
</p>
{% endif %}
</div>
<script>
......
......@@ -251,7 +251,7 @@ class ExecutionTable(BaseTable):
self.sql_manager.commit()
return self.cursor.fetchone()[0]
def select(self, only_one=False, limit=-1, **kwargs):
def select(self, only_one=False, limit=-1, base=0, **kwargs):
"""
Return a list of executions.
......@@ -259,6 +259,8 @@ class ExecutionTable(BaseTable):
:type only_one: bool
:param limit: limit the result to this number of entries
:type limit: int
:type base: int
:param base: the base value to use when limiting result count
:param kwargs: filter executions based on their fields/columns
:return: one or more executions
"""
......@@ -285,11 +287,11 @@ class ExecutionTable(BaseTable):
args_list.append(value)
q += ' AND '.join(filter_list)
if limit > 0:
q += ' ORDER BY id DESC LIMIT {}'.format(limit)
q += ' ORDER BY id DESC LIMIT {} OFFSET {}'.format(limit, base)
query = self.cursor.mogrify(q, args_list)
else:
if limit > 0:
q_base += ' ORDER BY id DESC LIMIT {}'.format(limit)
q_base += ' ORDER BY id DESC LIMIT {} OFFSET {}'.format(limit, base)
query = self.cursor.mogrify(q_base)
self.cursor.execute(query)
......@@ -300,3 +302,40 @@ class ExecutionTable(BaseTable):
return Execution(row, self.sql_manager)
else:
return [Execution(x, self.sql_manager) for x in self.cursor]
def count(self, **kwargs):
"""
Return a list of executions.
:param kwargs: filter executions based on their fields/columns
:return: one or more executions
"""
q_base = 'SELECT COUNT(*) FROM execution'
if len(kwargs) > 0:
q = q_base + " WHERE "
filter_list = []
args_list = []
for key, value in kwargs.items():
if key == 'earlier_than_submit':
filter_list.append('"time_submit" <= to_timestamp(%s)')
elif key == 'earlier_than_start':
filter_list.append('"time_start" <= to_timestamp(%s)')
elif key == 'earlier_than_end':
filter_list.append('"time_end" <= to_timestamp(%s)')
elif key == 'later_than_submit':
filter_list.append('"time_submit" >= to_timestamp(%s)')
elif key == 'later_than_start':
filter_list.append('"time_start" >= to_timestamp(%s)')
elif key == 'later_than_end':
filter_list.append('"time_end" >= to_timestamp(%s)')
else:
filter_list.append('{} = %s'.format(key))
args_list.append(value)
q += ' AND '.join(filter_list)
query = self.cursor.mogrify(q, args_list)
else:
query = self.cursor.mogrify(q_base)
self.cursor.execute(query)
row = self.cursor.fetchone()
return row[0]
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