UI: Cron Trigger screens - list and overview

* Added new Cron Trigger screens
  - main list
  - detail overview

Screenshots:
 http://pasteboard.co/rPZIMnT.png
 http://pasteboard.co/rPX6730.png

Partially implements blueprint: mistral-dashboard-cron-trigger-screen

Change-Id: I69f8b143f6ab4ca91f860e66b34471542cbdb6dd
This commit is contained in:
Gal Margalit 2015-12-31 17:02:50 +00:00
parent 3a1e52ed63
commit c56d137a90
10 changed files with 362 additions and 2 deletions

View File

@ -300,3 +300,35 @@ def action_delete(request, action_name):
""" """
return mistralclient(request).actions.delete(action_name) return mistralclient(request).actions.delete(action_name)
@handle_errors(_("Unable to retrieve cron trigger list"), [])
def cron_trigger_list(request):
"""Returns all cron triggers.
:param request: Request data
"""
return mistralclient(request).cron_triggers.list()
@handle_errors(_("Unable to retrieve cron trigger"), [])
def cron_trigger_get(request, cron_trigger_name):
"""Get specific cron trigger.
:param request: Request data
:param cron_trigger_name: Cron trigger name
"""
return mistralclient(request).cron_triggers.get(cron_trigger_name)
@handle_errors(_("Unable to delete cron trigger/s"), [])
def cron_trigger_delete(request, cron_trigger_name):
"""Delete Cron Trigger.
:param request: Request data
:param cron_trigger_name: Cron Trigger name
"""
return mistralclient(request).cron_triggers.delete(cron_trigger_name)

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 - Alcatel-Lucent.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
from mistraldashboard import dashboard
class CronTriggers(horizon.Panel):
name = _("Cron Triggers")
slug = 'cron_triggers'
dashboard.MistralDashboard.register(CronTriggers)

View File

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 - Alcatel-Lucent.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import tables
from mistraldashboard import api
from mistraldashboard.default.utils import humantime
class DeleteCronTrigger(tables.DeleteAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Cron Trigger",
u"Delete Cron Triggers",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Cron Trigger",
u"Deleted Cron Triggers",
count
)
def delete(self, request, cron_trigger_name):
api.cron_trigger_delete(request, cron_trigger_name)
class WorkflowColumn(tables.Column):
def get_link_url(self, datum):
workflow_url = "horizon:mistral:workflows:detail"
obj_id = datum.workflow_name
return reverse(workflow_url, args=[obj_id])
class CronTriggersTable(tables.DataTable):
id = tables.Column(
"id",
verbose_name=_("ID"),
link="horizon:mistral:cron_triggers:detail"
)
name = tables.Column(
"name",
verbose_name=_("Name")
)
workflow_name = WorkflowColumn(
"workflow_name",
verbose_name=_("Workflow"),
link=True
)
pattern = tables.Column(
"pattern",
verbose_name=_("Pattern"),
)
next_execution_time = tables.Column(
"next_execution_time",
verbose_name=_("Next Execution Time"),
)
remaining_executions = tables.Column(
"remaining_executions",
verbose_name=_("Remaining Executions"),
)
first_execution_time = tables.Column(
"first_execution_time",
verbose_name=_("First Execution Time"),
)
created_at = tables.Column(
"created_at",
verbose_name=_("Created at"),
filters=[humantime]
)
updated_at = tables.Column(
"updated_at",
verbose_name=_("Updated at"),
filters=[humantime]
)
def get_object_id(self, datum):
return datum.name
class Meta(object):
name = "cron trigger"
verbose_name = _("Cron Trigger")
table_actions = (tables.FilterAction, DeleteCronTrigger)
row_actions = (DeleteCronTrigger,)

View File

@ -0,0 +1,99 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Cron Trigger Details" %}{% endblock %}
{% block page_header %}
<h1>
{% trans "Cron Trigger Details" %}
</h1>
<ol class="breadcrumb">
<li><a href={{ cron_trigger.list_url }} title="Cron Triggers">
Cron Triggers
</a>
</li>
<li class="active" title={{ cron_trigger.name }}>
{{ cron_trigger.name }}
</li>
</ol>
{% endblock page_header %}
{% block main %}
{% load i18n sizeformat %}
<div class="detail">
<h4>{% trans "Overview" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ cron_trigger.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ cron_trigger.id }}</dd>
<dt>{% trans "Pattern" %}</dt>
<dd>{{ cron_trigger.pattern }}</dd>
<br/>
<dt>{% trans "Creation Date" %}</dt>
<dd>{{ cron_trigger.created_at|parse_isotime }}</dd>
<dt>{% trans "Time Since Created" %}</dt>
<dd>{{ cron_trigger.created_at|parse_isotime|timesince }}</dd>
<br/>
<dt>{% trans "First Execution" %}</dt>
<dd>
{% with time=cron_trigger.first_execution_time %}
{{ time|parse_isotime|default:"Empty" }}
{% endwith %}
</dd>
<dt>{% trans "Time Since first Execution" %}</dt>
<dd>
{% with time=cron_trigger.first_execution_time %}
{{ time|parse_isotime|timesince|default:"Empty" }}
{% endwith %}
</dd>
<br/>
<dt>{% trans "Update Date" %}</dt>
<dd>
{{ cron_trigger.updated_at|parse_isotime|default:"Empty" }}
</dd>
<dt>{% trans "Time Since Updated" %}</dt>
<dd>
{% with time=cron_trigger.updated_at %}
{{ time|parse_isotime|timesince|default:"Empty" }}
{% endwith %}
</dd>
<br/>
<dt>{% trans "Next Execution Time" %}</dt>
<dd>
{% with time=cron_trigger.next_execution_time %}
{{ time|parse_isotime|default:"Empty" }}
{% endwith %}
</dd>
<dt>{% trans "Time Until Next Execution" %}</dt>
<dd>
{% with time=cron_trigger.next_execution_time %}
{{ time|parse_isotime|timeuntil|default:"Empty" }}
{% endwith %}
</dd>
<dt>{% trans "Remaining Executions" %}</dt>
<dd>{{ cron_trigger.remaining_executions|default:"0"}}</dd>
<br/>
</dl>
</div>
<div class="detail">
<h4>{% trans "Workflow" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Workflow Name" %}</dt>
<dd>
<a href="{{ cron_trigger.workflow_url }}"
title=
"{{cron_trigger.workflow_name}} {% trans "overview" %}">
{{ cron_trigger.workflow_name }}
</a>
</dd>
</dl>
</div>
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends 'mistral/default/table.html' %}
{% load i18n %}
{% block title %}
{% trans "Cron Triggers" %}
{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Cron Triggers")%}
{% endblock page_header %}

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 - Alcatel-Lucent.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from django.conf.urls import patterns # noqa
from django.conf.urls import url # noqa
from mistraldashboard.cron_triggers import views
CRON_TRIGGERS = r'^(?P<cron_trigger_name>[^/]+)/%s$'
urlpatterns = patterns(
'',
url(r'^$', views.IndexView.as_view(), name='index'),
url(CRON_TRIGGERS % 'detail', views.OverviewView.as_view(), name='detail'),
)

View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 - Alcatel-Lucent.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.views import generic
from horizon import tables
from mistraldashboard import api
from mistraldashboard.cron_triggers.tables import CronTriggersTable
class OverviewView(generic.TemplateView):
template_name = 'mistral/cron_triggers/detail.html'
page_title = _("Cron Trigger Details")
workflow_url = 'horizon:mistral:workflows:detail'
list_url = 'horizon:mistral:cron_triggers:index'
def get_context_data(self, **kwargs):
context = super(OverviewView, self).get_context_data(**kwargs)
cron_trigger = {}
cron_trigger = api.cron_trigger_get(
self.request,
kwargs['cron_trigger_name']
)
cron_trigger.workflow_url = reverse(
self.workflow_url,
args=[cron_trigger.workflow_name]
)
cron_trigger.list_url = reverse_lazy(self.list_url)
context['cron_trigger'] = cron_trigger
return context
class IndexView(tables.DataTableView):
table_class = CronTriggersTable
template_name = 'mistral/cron_triggers/index.html'
def get_data(self):
return api.cron_trigger_list(self.request)

View File

@ -28,7 +28,8 @@ class MistralDashboard(horizon.Dashboard):
'workflows', 'workflows',
'executions', 'executions',
'tasks', 'tasks',
'actions' 'actions',
'cron_triggers'
) )
default_panel = 'default' default_panel = 'default'
roles = ('admin',) roles = ('admin',)

View File

@ -58,9 +58,10 @@ def handle_errors(error_message, error_default=None, request_arg=None):
return func(*args, **kwargs) return func(*args, **kwargs)
try: try:
return func(*args, **kwargs) return func(*args, **kwargs)
except Exception: except Exception as e:
callargs = inspect.getcallargs(func, *args, **kwargs) callargs = inspect.getcallargs(func, *args, **kwargs)
request = callargs[_request_arg] request = callargs[_request_arg]
_error_message += ': ' + str(e)
horizon.exceptions.handle(request, _error_message, horizon.exceptions.handle(request, _error_message,
ignore=_error_ignore, ignore=_error_ignore,
redirect=_error_redirect) redirect=_error_redirect)