From c56d137a90d0a3bb4b977e33ebf413cef9eecc00 Mon Sep 17 00:00:00 2001 From: Gal Margalit Date: Thu, 31 Dec 2015 17:02:50 +0000 Subject: [PATCH] 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 --- mistraldashboard/api.py | 32 ++++++ mistraldashboard/cron_triggers/__init__.py | 0 mistraldashboard/cron_triggers/panel.py | 28 +++++ mistraldashboard/cron_triggers/tables.py | 105 ++++++++++++++++++ .../templates/cron_triggers/detail.html | 99 +++++++++++++++++ .../templates/cron_triggers/index.html | 9 ++ mistraldashboard/cron_triggers/urls.py | 28 +++++ mistraldashboard/cron_triggers/views.py | 57 ++++++++++ mistraldashboard/dashboard.py | 3 +- mistraldashboard/handle_errors.py | 3 +- 10 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 mistraldashboard/cron_triggers/__init__.py create mode 100644 mistraldashboard/cron_triggers/panel.py create mode 100644 mistraldashboard/cron_triggers/tables.py create mode 100644 mistraldashboard/cron_triggers/templates/cron_triggers/detail.html create mode 100644 mistraldashboard/cron_triggers/templates/cron_triggers/index.html create mode 100644 mistraldashboard/cron_triggers/urls.py create mode 100644 mistraldashboard/cron_triggers/views.py diff --git a/mistraldashboard/api.py b/mistraldashboard/api.py index 7054f67..59cfe06 100644 --- a/mistraldashboard/api.py +++ b/mistraldashboard/api.py @@ -300,3 +300,35 @@ def action_delete(request, 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) diff --git a/mistraldashboard/cron_triggers/__init__.py b/mistraldashboard/cron_triggers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/cron_triggers/panel.py b/mistraldashboard/cron_triggers/panel.py new file mode 100644 index 0000000..76479ed --- /dev/null +++ b/mistraldashboard/cron_triggers/panel.py @@ -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) diff --git a/mistraldashboard/cron_triggers/tables.py b/mistraldashboard/cron_triggers/tables.py new file mode 100644 index 0000000..9c38359 --- /dev/null +++ b/mistraldashboard/cron_triggers/tables.py @@ -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,) diff --git a/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html b/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html new file mode 100644 index 0000000..883d53a --- /dev/null +++ b/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html @@ -0,0 +1,99 @@ +{% extends 'base.html' %} +{% load i18n %} + +{% block title %}{% trans "Cron Trigger Details" %}{% endblock %} + +{% block page_header %} +

+ {% trans "Cron Trigger Details" %} +

+ +{% endblock page_header %} + +{% block main %} + {% load i18n sizeformat %} +
+

{% trans "Overview" %}

+
+
+
{% trans "Name" %}
+
{{ cron_trigger.name }}
+
{% trans "ID" %}
+
{{ cron_trigger.id }}
+
{% trans "Pattern" %}
+
{{ cron_trigger.pattern }}
+
+ +
{% trans "Creation Date" %}
+
{{ cron_trigger.created_at|parse_isotime }}
+
{% trans "Time Since Created" %}
+
{{ cron_trigger.created_at|parse_isotime|timesince }}
+
+ +
{% trans "First Execution" %}
+
+ {% with time=cron_trigger.first_execution_time %} + {{ time|parse_isotime|default:"Empty" }} + {% endwith %} +
+
{% trans "Time Since first Execution" %}
+
+ {% with time=cron_trigger.first_execution_time %} + {{ time|parse_isotime|timesince|default:"Empty" }} + {% endwith %} +
+
+ +
{% trans "Update Date" %}
+
+ {{ cron_trigger.updated_at|parse_isotime|default:"Empty" }} +
+
{% trans "Time Since Updated" %}
+
+ {% with time=cron_trigger.updated_at %} + {{ time|parse_isotime|timesince|default:"Empty" }} + {% endwith %} +
+
+ +
{% trans "Next Execution Time" %}
+
+ {% with time=cron_trigger.next_execution_time %} + {{ time|parse_isotime|default:"Empty" }} + {% endwith %} +
+
{% trans "Time Until Next Execution" %}
+
+ {% with time=cron_trigger.next_execution_time %} + {{ time|parse_isotime|timeuntil|default:"Empty" }} + {% endwith %} +
+
{% trans "Remaining Executions" %}
+
{{ cron_trigger.remaining_executions|default:"0"}}
+
+
+
+ +
+

{% trans "Workflow" %}

+
+
+
{% trans "Workflow Name" %}
+
+ + {{ cron_trigger.workflow_name }} + +
+
+
+{% endblock %} diff --git a/mistraldashboard/cron_triggers/templates/cron_triggers/index.html b/mistraldashboard/cron_triggers/templates/cron_triggers/index.html new file mode 100644 index 0000000..d09b598 --- /dev/null +++ b/mistraldashboard/cron_triggers/templates/cron_triggers/index.html @@ -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 %} diff --git a/mistraldashboard/cron_triggers/urls.py b/mistraldashboard/cron_triggers/urls.py new file mode 100644 index 0000000..88f9da2 --- /dev/null +++ b/mistraldashboard/cron_triggers/urls.py @@ -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[^/]+)/%s$' + +urlpatterns = patterns( + '', + url(r'^$', views.IndexView.as_view(), name='index'), + url(CRON_TRIGGERS % 'detail', views.OverviewView.as_view(), name='detail'), +) diff --git a/mistraldashboard/cron_triggers/views.py b/mistraldashboard/cron_triggers/views.py new file mode 100644 index 0000000..f12546d --- /dev/null +++ b/mistraldashboard/cron_triggers/views.py @@ -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) diff --git a/mistraldashboard/dashboard.py b/mistraldashboard/dashboard.py index 92c386e..166f219 100644 --- a/mistraldashboard/dashboard.py +++ b/mistraldashboard/dashboard.py @@ -28,7 +28,8 @@ class MistralDashboard(horizon.Dashboard): 'workflows', 'executions', 'tasks', - 'actions' + 'actions', + 'cron_triggers' ) default_panel = 'default' roles = ('admin',) diff --git a/mistraldashboard/handle_errors.py b/mistraldashboard/handle_errors.py index 3947d25..4d929af 100644 --- a/mistraldashboard/handle_errors.py +++ b/mistraldashboard/handle_errors.py @@ -58,9 +58,10 @@ def handle_errors(error_message, error_default=None, request_arg=None): return func(*args, **kwargs) try: return func(*args, **kwargs) - except Exception: + except Exception as e: callargs = inspect.getcallargs(func, *args, **kwargs) request = callargs[_request_arg] + _error_message += ': ' + str(e) horizon.exceptions.handle(request, _error_message, ignore=_error_ignore, redirect=_error_redirect)