diff --git a/mistraldashboard/action_executions/__init__.py b/mistraldashboard/action_executions/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mistraldashboard/action_executions/forms.py b/mistraldashboard/action_executions/forms.py
new file mode 100644
index 0000000..1b6beca
--- /dev/null
+++ b/mistraldashboard/action_executions/forms.py
@@ -0,0 +1,102 @@
+# Copyright 2016 - Nokia.
+# 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 _
+
+from horizon import forms
+
+from mistraldashboard import api
+from mistraldashboard.handle_errors import handle_errors
+
+
+class UpdateForm(forms.SelfHandlingForm):
+ action_execution_id = forms.CharField(label=_("Action Execution ID"),
+ widget=forms.HiddenInput(),
+ required=False)
+ output_source = forms.ChoiceField(
+ label=_('Output'),
+ help_text=_('Content for output. '
+ 'Select either file, raw content or Null value.'),
+ choices=[('null', _(' (sends empty value)')),
+ ('file', _('File')),
+ ('raw', _('Direct Input'))],
+ widget=forms.Select(
+ attrs={'class': 'switchable',
+ 'data-slug': 'outputsource'}
+ ),
+ required=False
+ )
+ output_upload = forms.FileField(
+ label=_('Output File'),
+ help_text=_('A local output to upload'),
+ widget=forms.FileInput(
+ attrs={'class': 'switched',
+ 'data-switch-on': 'outputsource',
+ 'data-outputsource-file': _('Output File')}
+ ),
+ required=False
+ )
+ output_data = forms.CharField(
+ label=_('Output Data'),
+ help_text=_('The raw content for output'),
+ widget=forms.widgets.Textarea(
+ attrs={'class': 'switched',
+ 'data-switch-on': 'outputsource',
+ 'data-outputsource-raw': _('Output Data'),
+ 'rows': 4}
+ ),
+ required=False
+ )
+
+ state = forms.ChoiceField(
+ label=_('State'),
+ help_text=_('Select state to update'),
+ choices=[('null', _(' (sends empty value)')),
+ ('SUCCESS', _('Success')),
+ ('ERROR', _('Error'))],
+ widget=forms.Select(
+ attrs={'class': 'switchable'}
+ ),
+ required=False
+
+ )
+
+ def clean(self):
+ cleaned_data = super(UpdateForm, self).clean()
+ cleaned_data['output'] = None
+
+ if cleaned_data.get('output_upload'):
+ files = self.request.FILES
+ cleaned_data['output'] = files['output_upload'].read()
+ elif cleaned_data.get('output_data'):
+ cleaned_data['output'] = cleaned_data['output_data']
+ elif cleaned_data.get('output_source') == 'null':
+ cleaned_data['output'] = None
+
+ del cleaned_data['output_upload']
+ del cleaned_data['output_data']
+ del cleaned_data['output_source']
+
+ if cleaned_data['state'] == 'null':
+ cleaned_data['state'] = None
+
+ return cleaned_data
+
+ @handle_errors(_("Unable to update Action Execution"), [])
+ def handle(self, request, data):
+ return api.action_execution_update(
+ request,
+ data['action_execution_id'],
+ data['state'],
+ data['output'],
+ )
diff --git a/mistraldashboard/action_executions/panel.py b/mistraldashboard/action_executions/panel.py
new file mode 100644
index 0000000..f4f1442
--- /dev/null
+++ b/mistraldashboard/action_executions/panel.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2016 - Nokia.
+#
+# 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 Tasks(horizon.Panel):
+ name = _("Action Executions")
+ slug = 'action_executions'
+
+
+dashboard.MistralDashboard.register(Tasks)
diff --git a/mistraldashboard/action_executions/tables.py b/mistraldashboard/action_executions/tables.py
new file mode 100644
index 0000000..e8c5958
--- /dev/null
+++ b/mistraldashboard/action_executions/tables.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2016 - Nokia.
+#
+# 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 exceptions
+from horizon import tables
+
+from mistraldashboard import api
+import mistraldashboard.default.SmartCell as SmartCell
+from mistraldashboard.default import utils
+
+SmartCell.init()
+
+
+class UpdateRow(tables.Row):
+ ajax = True
+
+ def get_data(self, request, id):
+ try:
+ instance = api.action_execution_get(request, id)
+ except Exception as e:
+ msg = _("Unable to get action execution by ID %(id)s: %(e)s.") % {
+ 'id': id, 'e': str(e)
+ }
+ exceptions.handle(request, msg)
+
+ return instance
+
+
+class DeleteActionExecution(tables.DeleteAction):
+ @staticmethod
+ def action_present(count):
+ return ungettext_lazy(
+ u"Delete Action Execution",
+ u"Delete Action Executions",
+ count
+ )
+
+ @staticmethod
+ def action_past(count):
+ return ungettext_lazy(
+ u"Deleted Action Execution",
+ u"Deleted Action Executions",
+ count
+ )
+
+ def delete(self, request, action_execution_id):
+ api.action_execution_delete(request, action_execution_id)
+
+
+class UpdateActionExecution(tables.LinkAction):
+ name = "updateAE"
+ verbose_name = _("Update")
+ url = "horizon:mistral:action_executions:update"
+ classes = ("ajax-modal",)
+
+
+class TaskExecutionIDColumn(tables.Column):
+ def get_link_url(self, datum):
+ task_url = "horizon:mistral:tasks:detail"
+ obj_id = datum.task_execution_id
+ return reverse(task_url, args=[obj_id])
+
+
+class WorkflowNameColumn(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 ActionExecutionsTable(tables.DataTable):
+
+ def getHoverHelp(data):
+ if hasattr(data, 'state_info') and data.state_info:
+
+ return {'title': data.state_info}
+
+ STATE_STATUS_CHOICES = (
+ ("success", True),
+ ("error", False),
+ ("idle", None),
+ ("running", None),
+ ("canceled", None),
+ )
+
+ id = tables.Column(
+ "id",
+ verbose_name=_("ID"),
+ link="horizon:mistral:action_executions:detail"
+ )
+ name = tables.Column(
+ "name",
+ verbose_name=_("Name")
+ )
+ tags = tables.Column(
+ "tags",
+ verbose_name=_("Tags")
+ )
+ workflow_name = WorkflowNameColumn(
+ "workflow_name",
+ verbose_name=_("Workflow Name"),
+ link=True
+ )
+ task_execution_id = TaskExecutionIDColumn(
+ "task_execution_id",
+ verbose_name=_("Task Execution ID"),
+ link=True
+ )
+ task_name = tables.Column(
+ "task_name",
+ verbose_name=_("Task name")
+ )
+ description = tables.Column(
+ "description",
+ verbose_name=_("Description")
+ )
+ input = tables.Column(
+ "",
+ verbose_name=_("Input"),
+ empty_value=_("View"),
+ link="horizon:mistral:action_executions:input",
+ link_classes=("ajax-modal",)
+ )
+ output = tables.Column(
+ "",
+ verbose_name=_("Output"),
+ empty_value=_("View"),
+ link="horizon:mistral:action_executions:output",
+ link_classes=("ajax-modal",)
+ )
+ created_at = tables.Column(
+ "created_at",
+ verbose_name=_("Created at"),
+ filters=[utils.humantime]
+ )
+ updated_at = tables.Column(
+ "updated_at",
+ verbose_name=_("Updated at"),
+ filters=[utils.humantime]
+ )
+ accepted = tables.Column(
+ "accepted",
+ verbose_name=_("Accepted"),
+ filters=[utils.booleanfield],
+ )
+ state = tables.Column(
+ "state",
+ status=True,
+ status_choices=STATE_STATUS_CHOICES,
+ verbose_name=_("State"),
+ filters=[utils.label],
+ cell_attributes_getter=getHoverHelp
+ )
+
+ class Meta(object):
+ name = "actionExecutions"
+ verbose_name = _("Action Executions")
+ status_columns = ["state"]
+ row_class = UpdateRow
+ table_actions = (
+ tables.FilterAction,
+ DeleteActionExecution
+ )
+ row_actions = (UpdateActionExecution, DeleteActionExecution)
diff --git a/mistraldashboard/action_executions/templates/action_executions/_update.html b/mistraldashboard/action_executions/templates/action_executions/_update.html
new file mode 100644
index 0000000..7c0bc55
--- /dev/null
+++ b/mistraldashboard/action_executions/templates/action_executions/_update.html
@@ -0,0 +1,17 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block modal-body-right %}
+ {% trans "Description:" %}
+
+ {% trans "Enter new output and or state to update the corresponding Action Execution." %}
+
+
+ {% trans "For more info refer to:" %}
+
+
+ {% trans "Mistral documentation - Action Executions" %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/mistraldashboard/action_executions/templates/action_executions/detail.html b/mistraldashboard/action_executions/templates/action_executions/detail.html
new file mode 100644
index 0000000..e6b649f
--- /dev/null
+++ b/mistraldashboard/action_executions/templates/action_executions/detail.html
@@ -0,0 +1,96 @@
+
+{% extends 'mistral/default/base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Action Execution Details" %}{% endblock %}
+
+{% block page_header %}
+
+ {% trans "Action Execution Details" %}
+
+{% endblock page_header %}
+
+{% block main %}
+ {% load i18n sizeformat %}
+
+
+
{% trans "Overview" %}
+
+
+ - {% trans "Name" %}
+ - {{ action_execution.name }}
+ - {% trans "ID" %}
+ - {{ action_execution.id }}
+ {% if action_execution.description %}
+ - {% trans "Description" %}
+ - {{ action_execution.description }}
+ {% endif %}
+
+
- {% trans "State" %}
+ - {{ action_execution.state }}
+
+ {% if action_execution.state_info %}
+ - {% trans "State Info" %}
+ - {{ action_execution.state_info }}
+ {% endif %}
+ - {% trans "Accepted" %}
+ - {{ action_execution.accepted }}
+ - {% trans "Tags" %}
+ - {{ action_execution.tags }}
+
+ - {% trans "Creation Date" %}
+ - {{ action_execution.created_at|parse_isotime}}
+ - {% trans "Time Since Created" %}
+ - {{ action_execution.created_at|parse_isotime|timesince }}
+
+ - {% trans "Update Date" %}
+ - {{ action_execution.updated_at|parse_isotime}}
+ - {% trans "Time Since Updated" %}
+ - {{ action_execution.updated_at|parse_isotime|timesince }}
+
+ - {% trans "Input" %}
+ - {{ action_execution.input }}
+ - {% trans "Output" %}
+ - {{ action_execution.output }}
+
+
+
+ {% if action_execution.workflow_url %}
+
+ {% endif %}
+ {% if action_execution.task_execution_url %}
+
+ {% endif %}
+
+{% endblock %}
+
\ No newline at end of file
diff --git a/mistraldashboard/action_executions/templates/action_executions/filtered.html b/mistraldashboard/action_executions/templates/action_executions/filtered.html
new file mode 100644
index 0000000..bae249b
--- /dev/null
+++ b/mistraldashboard/action_executions/templates/action_executions/filtered.html
@@ -0,0 +1,13 @@
+{% extends 'mistral/default/table.html' %}
+{% load i18n %}
+{% block title %}
+ {% trans "Action Executions" %}
+ {{ task_id }}
+{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Action Executions of Task ID:") %}
+
+ {{ action_execution_id }}
+
+{% endblock page_header %}
diff --git a/mistraldashboard/action_executions/templates/action_executions/index.html b/mistraldashboard/action_executions/templates/action_executions/index.html
new file mode 100644
index 0000000..ca07db2
--- /dev/null
+++ b/mistraldashboard/action_executions/templates/action_executions/index.html
@@ -0,0 +1,9 @@
+{% extends 'mistral/default/table.html' %}
+{% load i18n %}
+{% block title %}
+ {% trans "Action Executions" %}
+{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Action Executions")%}
+{% endblock page_header %}
diff --git a/mistraldashboard/action_executions/templates/action_executions/update.html b/mistraldashboard/action_executions/templates/action_executions/update.html
new file mode 100644
index 0000000..5a39946
--- /dev/null
+++ b/mistraldashboard/action_executions/templates/action_executions/update.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include 'mistral/executions/_update.html' %}
+{% endblock %}
\ No newline at end of file
diff --git a/mistraldashboard/action_executions/urls.py b/mistraldashboard/action_executions/urls.py
new file mode 100644
index 0000000..0133baa
--- /dev/null
+++ b/mistraldashboard/action_executions/urls.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2016 - Nokia.
+#
+# 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 url # noqa
+
+from mistraldashboard.action_executions import views
+
+ACTION_EXECUTIONS = r'^(?P[^/]+)/%s$'
+
+urlpatterns = [
+ url(r'^$', views.IndexView.as_view(), name='index'),
+ url(ACTION_EXECUTIONS % 'detail', views.OverviewView.as_view(),
+ name='detail'),
+ url(ACTION_EXECUTIONS % 'input', views.CodeView.as_view(),
+ {'column': 'input'}, name='input'),
+ url(ACTION_EXECUTIONS % 'output', views.CodeView.as_view(),
+ {'column': 'output'}, name='output'),
+ url(ACTION_EXECUTIONS % 'update', views.UpdateView.as_view(),
+ name='update'),
+ url(ACTION_EXECUTIONS % 'task', views.FilteredByTaskView.as_view(),
+ name='task')
+]
diff --git a/mistraldashboard/action_executions/views.py b/mistraldashboard/action_executions/views.py
new file mode 100644
index 0000000..8273eb3
--- /dev/null
+++ b/mistraldashboard/action_executions/views.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2016 - Nokia.
+#
+# 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 exceptions
+from horizon import forms
+from horizon import tables
+
+from mistraldashboard.action_executions import forms as action_execution_forms
+from mistraldashboard.action_executions import tables as mistral_tables
+from mistraldashboard import api
+from mistraldashboard.default import utils
+from mistraldashboard import forms as mistral_forms
+
+
+def get_single_action_execution_data(request, **kwargs):
+ try:
+ action_execution_id = kwargs['action_execution_id']
+ action_execution = api.action_execution_get(
+ request,
+ action_execution_id
+ )
+ except Exception:
+ msg = _('Unable to get action execution "%s".') % action_execution_id
+ redirect = reverse('horizon:mistral:action_execution:index')
+ exceptions.handle(request, msg, redirect=redirect)
+
+ return action_execution
+
+
+class OverviewView(generic.TemplateView):
+ template_name = 'mistral/action_executions/detail.html'
+ page_title = _("Action Execution Details")
+ workflow_url = 'horizon:mistral:workflows:detail'
+ task_execution_url = 'horizon:mistral:tasks:detail'
+
+ def get_context_data(self, **kwargs):
+ context = super(OverviewView, self).get_context_data(**kwargs)
+ action_execution = get_single_action_execution_data(
+ self.request,
+ **kwargs
+ )
+ if action_execution.workflow_name:
+ action_execution.workflow_url = reverse(
+ self.workflow_url,
+ args=[action_execution.workflow_name])
+ if action_execution.task_execution_id:
+ action_execution.task_execution_url = reverse(
+ self.task_execution_url,
+ args=[action_execution.task_execution_id]
+ )
+ if action_execution.input:
+ action_execution.input = utils.prettyprint(action_execution.input)
+ if action_execution.output:
+ action_execution.output = utils.prettyprint(
+ action_execution.output
+ )
+ if action_execution.state:
+ action_execution.state = utils.label(action_execution.state)
+ if action_execution.accepted:
+ action_execution.accepted = utils.booleanfield(
+ action_execution.accepted
+ )
+
+ breadcrumb = [(action_execution.id, reverse(
+ 'horizon:mistral:action_executions:detail',
+ args=[action_execution.id]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
+ context['action_execution'] = action_execution
+
+ return context
+
+
+class CodeView(forms.ModalFormView):
+ template_name = 'mistral/default/code.html'
+ modal_header = _("Code view")
+ form_id = "code_view"
+ form_class = mistral_forms.EmptyForm
+ cancel_label = "OK"
+ cancel_url = reverse_lazy("horizon:mistral:action_executions:index")
+ page_title = _("Code view")
+
+ def get_context_data(self, **kwargs):
+ context = super(CodeView, self).get_context_data(**kwargs)
+ column = self.kwargs['column']
+ action_execution = get_single_action_execution_data(
+ self.request,
+ **self.kwargs
+ )
+ io = {}
+
+ if column == 'input':
+ io['name'] = _('Input')
+ io['value'] = utils.prettyprint(action_execution.input)
+ elif column == 'output':
+ io['name'] = _('Output')
+ io['value'] = (
+ utils.prettyprint(action_execution.output)
+ if action_execution.output
+ else _("No available output yet")
+ )
+
+ context['io'] = io
+
+ return context
+
+
+class IndexView(tables.DataTableView):
+ table_class = mistral_tables.ActionExecutionsTable
+ template_name = 'mistral/action_executions/index.html'
+
+ def get_data(self):
+
+ return api.action_executions_list(self.request)
+
+
+class UpdateView(forms.ModalFormView):
+ template_name = 'mistral/action_executions/update.html'
+ modal_header = _("Update Action Execution")
+ form_id = "update_action_execution"
+ form_class = action_execution_forms.UpdateForm
+ submit_label = _("Update")
+ success_url = reverse_lazy("horizon:mistral:action_executions:index")
+ submit_url = "horizon:mistral:action_executions:update"
+ cancel_url = "horizon:mistral:action_executions:index"
+ page_title = _("Update Action Execution")
+
+ def get_initial(self):
+ return {"action_execution_id": self.kwargs["action_execution_id"]}
+
+ def get_context_data(self, **kwargs):
+ context = super(UpdateView, self).get_context_data(**kwargs)
+ context['submit_url'] = reverse(
+ self.submit_url,
+ args=[self.kwargs["action_execution_id"]]
+ )
+
+ return context
+
+
+class FilteredByTaskView(tables.DataTableView):
+ table_class = mistral_tables.ActionExecutionsTable
+ template_name = 'mistral/action_executions/filtered.html'
+ data = {}
+
+ def get_data(self, **kwargs):
+ try:
+ task_id = self.kwargs['action_execution_id']
+ data = api.action_executions_list(self.request, task_id)
+ except Exception:
+ msg = (
+ _('Unable to get action execution by task id "%s".') % task_id
+ )
+ redirect = reverse('horizon:mistral:action_executions:index')
+ exceptions.handle(self.request, msg, redirect=redirect)
+
+ return data
diff --git a/mistraldashboard/actions/tables.py b/mistraldashboard/actions/tables.py
index ca35056..d3ecc5d 100644
--- a/mistraldashboard/actions/tables.py
+++ b/mistraldashboard/actions/tables.py
@@ -19,6 +19,7 @@ from horizon import tables
from horizon.utils import filters
from mistraldashboard import api
+from mistraldashboard.default import utils
class CreateAction(tables.LinkAction):
@@ -92,7 +93,8 @@ class ActionsTable(tables.DataTable):
)
is_system = tables.Column(
"is_system",
- verbose_name=_("Is System")
+ verbose_name=_("Is System"),
+ filters=[utils.booleanfield]
)
tags = tables.Column(
tags_to_string,
diff --git a/mistraldashboard/actions/templates/actions/detail.html b/mistraldashboard/actions/templates/actions/detail.html
index 52aabdf..18dae19 100644
--- a/mistraldashboard/actions/templates/actions/detail.html
+++ b/mistraldashboard/actions/templates/actions/detail.html
@@ -1,4 +1,5 @@
-{% extends 'base.html' %}
+
+{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Action Definition" %}{% endblock %}
@@ -31,8 +32,7 @@
{{ action.description }}
{% trans "Definition" %}
{{ action.definition }}
-
{% endblock %}
-
+
diff --git a/mistraldashboard/actions/views.py b/mistraldashboard/actions/views.py
index b0ca540..a5afb75 100644
--- a/mistraldashboard/actions/views.py
+++ b/mistraldashboard/actions/views.py
@@ -24,6 +24,7 @@ from horizon import tables
from mistraldashboard.actions import forms as mistral_forms
from mistraldashboard.actions import tables as mistral_tables
from mistraldashboard import api
+from mistraldashboard.default import utils
class CreateView(forms.ModalFormView):
@@ -112,6 +113,14 @@ class DetailView(generic.TemplateView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
action = self.get_data(self.request, **kwargs)
+ if action.is_system:
+ action.is_system = utils.booleanfield(action.is_system)
+ breadcrumb = [(action.name, reverse(
+ 'horizon:mistral:actions:detail',
+ args=[action.id]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
context['action'] = action
return context
diff --git a/mistraldashboard/api.py b/mistraldashboard/api.py
index 304c832..d10f588 100644
--- a/mistraldashboard/api.py
+++ b/mistraldashboard/api.py
@@ -330,6 +330,49 @@ def action_delete(request, action_name):
return mistralclient(request).actions.delete(action_name)
+@handle_errors(_("Unable to retrieve action executions list"), [])
+def action_executions_list(request, task_execution_id=None):
+ """Returns all actions executions.
+
+ :param request: Request data
+ :param task_execution_id: (Optional) Task Execution ID to filter by
+ """
+
+ return mistralclient(request).action_executions.list(task_execution_id)
+
+
+@handle_errors(_("Unable to retrieve action execution"), [])
+def action_execution_get(request, action_execution_id):
+ """Get specific action execution.
+
+ :param action_execution_id: Action Execution ID
+ """
+
+ return mistralclient(request).action_executions.get(action_execution_id)
+
+
+@handle_errors(_("Unable to delete action execution/s"), [])
+def action_execution_delete(request, action_execution_id):
+ """Delete action execution.
+
+ :param action_execution_id: Action execution ID
+ """
+
+ return mistralclient(request).action_executions.delete(action_execution_id)
+
+
+@handle_errors(_("Unable to update action execution"), [])
+def action_execution_update(request, id, state=None, output=None):
+ """Update action execution output and or state.
+
+ :param id: action execution id
+ :param output: action execution output
+ :param state: action execution state
+ """
+
+ return mistralclient(request).action_executions.update(id, state, output)
+
+
@handle_errors(_("Unable to retrieve cron trigger list"), [])
def cron_trigger_list(request):
"""Returns all cron triggers.
diff --git a/mistraldashboard/cron_triggers/templates/cron_triggers/_create.html b/mistraldashboard/cron_triggers/templates/cron_triggers/_create.html
index 7f8545c..b523185 100644
--- a/mistraldashboard/cron_triggers/templates/cron_triggers/_create.html
+++ b/mistraldashboard/cron_triggers/templates/cron_triggers/_create.html
@@ -17,7 +17,7 @@
{% trans "For more info" %}:
-
+
-
diff --git a/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html b/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html
index 883d53a..fbfb2df 100644
--- a/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html
+++ b/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html
@@ -1,4 +1,5 @@
-{% extends 'base.html' %}
+
+{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Cron Trigger Details" %}{% endblock %}
@@ -8,7 +9,7 @@
{% trans "Cron Trigger Details" %}
- -
+
-
Cron Triggers
@@ -97,3 +98,4 @@
{% endblock %}
+
\ No newline at end of file
diff --git a/mistraldashboard/cron_triggers/views.py b/mistraldashboard/cron_triggers/views.py
index f4af8b5..2ca2ffc 100644
--- a/mistraldashboard/cron_triggers/views.py
+++ b/mistraldashboard/cron_triggers/views.py
@@ -44,6 +44,12 @@ class OverviewView(generic.TemplateView):
args=[cron_trigger.workflow_name]
)
cron_trigger.list_url = reverse_lazy(self.list_url)
+ breadcrumb = [(cron_trigger.name, reverse(
+ 'horizon:mistral:cron_triggers:detail',
+ args=[cron_trigger.name]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
context['cron_trigger'] = cron_trigger
return context
diff --git a/mistraldashboard/dashboard.py b/mistraldashboard/dashboard.py
index 3bac723..40b64a2 100644
--- a/mistraldashboard/dashboard.py
+++ b/mistraldashboard/dashboard.py
@@ -26,10 +26,11 @@ class MistralDashboard(horizon.Dashboard):
'default',
'workbooks',
'workflows',
+ 'actions',
'executions',
'tasks',
- 'actions',
- 'cron_triggers'
+ 'action_executions',
+ 'cron_triggers',
)
default_panel = 'default'
roles = ('admin',)
diff --git a/mistraldashboard/default/templates/default/_booleanfield.html b/mistraldashboard/default/templates/default/_booleanfield.html
new file mode 100644
index 0000000..64ba35e
--- /dev/null
+++ b/mistraldashboard/default/templates/default/_booleanfield.html
@@ -0,0 +1,4 @@
+
+
+ {{ bool }}
+
diff --git a/mistraldashboard/default/utils.py b/mistraldashboard/default/utils.py
index 970a186..44b2a48 100644
--- a/mistraldashboard/default/utils.py
+++ b/mistraldashboard/default/utils.py
@@ -26,6 +26,17 @@ TYPES = {
'RUNNING': 'label-info',
}
+BOOLEAN_FIELD = {
+ 'True': {
+ 'color': 'green',
+ 'icon': 'fa fa-check'
+ },
+ 'False': {
+ 'color': 'red',
+ 'icon': 'fa fa-remove'
+ }
+}
+
def label(x):
return render_to_string("mistral/default/_label.html",
@@ -33,6 +44,15 @@ def label(x):
"type": TYPES.get(x)})
+def booleanfield(x):
+ # todo: check undefined instead of the if blocks in view
+ # todo: check the red version
+
+ return render_to_string("mistral/default/_booleanfield.html",
+ {"bool": str(x),
+ "type": BOOLEAN_FIELD.get(str(x))})
+
+
def humantime(x):
return render_to_string("mistral/default/_humantime.html",
{"datetime": iso8601.parse_date(x)})
diff --git a/mistraldashboard/executions/templates/executions/detail.html b/mistraldashboard/executions/templates/executions/detail.html
index 5f2288f..dbe9574 100644
--- a/mistraldashboard/executions/templates/executions/detail.html
+++ b/mistraldashboard/executions/templates/executions/detail.html
@@ -1,4 +1,5 @@
-{% extends 'base.html' %}
+
+{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Execution Details" %}{% endblock %}
@@ -22,7 +23,7 @@
- {{ execution.description }}
{% endif %}
- {% trans "State" %}
- - {{ execution.state }}
+ - {{ execution.state }}
{% if execution.state_info %}
- {% trans "State Info" %}
- {{ execution.state_info }}
@@ -66,4 +67,5 @@
-{% endblock %}
\ No newline at end of file
+{% endblock %}
+
\ No newline at end of file
diff --git a/mistraldashboard/executions/views.py b/mistraldashboard/executions/views.py
index 371312e..2e7ea54 100644
--- a/mistraldashboard/executions/views.py
+++ b/mistraldashboard/executions/views.py
@@ -25,7 +25,7 @@ from horizon import forms
from horizon import tables
from mistraldashboard import api
-from mistraldashboard.default.utils import prettyprint
+from mistraldashboard.default import utils
from mistraldashboard.executions import forms as m_forms
from mistraldashboard.executions import tables as mistral_tables
from mistraldashboard import forms as mistral_forms
@@ -160,10 +160,18 @@ class DetailView(generic.TemplateView):
execution.workflow_url = reverse(self.workflow_url,
args=[execution.workflow_name])
- execution.input = prettyprint(execution.input)
- execution.output = prettyprint(execution.output)
- execution.params = prettyprint(execution.params)
+ execution.input = utils.prettyprint(execution.input)
+ execution.output = utils.prettyprint(execution.output)
+ execution.params = utils.prettyprint(execution.params)
+ execution.state = utils.label(execution.state)
task.url = reverse(self.task_url, args=[execution.id])
+
+ breadcrumb = [(execution.id, reverse(
+ 'horizon:mistral:executions:detail',
+ args=[execution.id]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
context['execution'] = execution
context['task'] = task
@@ -189,10 +197,13 @@ class CodeView(forms.ModalFormView):
io = {}
if column == 'input':
io['name'] = _('Input')
- io['value'] = execution.input = prettyprint(execution.input)
+ io['value'] = execution.input = utils.prettyprint(execution.input)
elif column == 'output':
io['name'] = _('Output')
- io['value'] = execution.output = prettyprint(execution.output)
+ io['value'] = execution.output = utils.prettyprint(
+ execution.output
+ )
+
context['io'] = io
return context
diff --git a/mistraldashboard/static/mistraldashboard/css/style.css b/mistraldashboard/static/mistraldashboard/css/style.css
index 55888dc..3d39558 100644
--- a/mistraldashboard/static/mistraldashboard/css/style.css
+++ b/mistraldashboard/static/mistraldashboard/css/style.css
@@ -1,4 +1,4 @@
-.list{
+.mistral-wrapper.list{
list-style: inherit;
}
@@ -9,3 +9,43 @@
.mistral-wrapper #actions a.btn{
width:initial;
}
+
+.mistral-wrapper.detail-screen .page-breadcrumb ol li{
+ max-width: inherit;
+}
+
+.mistral-wrapper.detail-screen .page-breadcrumb li:last-child{
+ display:none;
+}
+
+.mistral-wrapper .navbar-brand{
+ padding: 6px 10px;
+}
+
+.boolfield{
+ font-style: italic;
+}
+
+.boolfield i{
+ padding-right: .2em;
+}
+
+.boolfield i.green{
+ color: green;
+}
+
+.boolfield i.red{
+ color: red;
+}
+
+.line-space{
+ margin: .3em 0;
+}
+
+.line-space dd{
+ display:inline-block;
+ margin-left: 1.5em;
+}
+
+
+
diff --git a/mistraldashboard/tasks/tables.py b/mistraldashboard/tasks/tables.py
index c6e61d3..7d978f5 100644
--- a/mistraldashboard/tasks/tables.py
+++ b/mistraldashboard/tasks/tables.py
@@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from django.core.urlresolvers import reverse
+from django.template.defaultfilters import title
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
@@ -42,6 +44,21 @@ class UpdateRow(tables.Row):
return instance
+class TypeColumn(tables.Column):
+ def get_link_url(self, datum):
+ obj_id = datum.id
+ url = ""
+ action_execution_url = "horizon:mistral:action_executions:task"
+ if datum.type == "ACTION":
+ url = action_execution_url
+ # todo: add missing link to workflow execution
+ # once available in python mistral client API
+ # elif datum.type = "WORKFLOW":
+ # url= "horizon:mistral:workflow:task"
+
+ return reverse(url, args=[obj_id])
+
+
class TaskTable(tables.DataTable):
def getHoverHelp(data):
@@ -70,6 +87,12 @@ class TaskTable(tables.DataTable):
verbose_name=_("Workflow Execution ID"),
link="horizon:mistral:executions:detail_task_id"
)
+ type = TypeColumn(
+ "type",
+ verbose_name=_("Type"),
+ filters=[title],
+ link=True
+ )
result = tables.Column(
"",
verbose_name=_("Result"),
diff --git a/mistraldashboard/tasks/templates/tasks/detail.html b/mistraldashboard/tasks/templates/tasks/detail.html
index 08f9c28..a06682b 100644
--- a/mistraldashboard/tasks/templates/tasks/detail.html
+++ b/mistraldashboard/tasks/templates/tasks/detail.html
@@ -1,4 +1,5 @@
-{% extends 'base.html' %}
+
+{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Task Details" %}{% endblock %}
@@ -18,12 +19,21 @@
- {{ task.name }}
- {% trans "ID" %}
- {{ task.id }}
+ - {% trans "Type" %}
+ -
+
+ {{ task.type |title }}
+
+
{% if task.state_info %}
- {% trans "State Info" %}
- {{ task.state_info }}
{% endif %}
- - {% trans "State" %}
- - {{ task.state }}
+
+
- {% trans "State" %}
+ - {{ task.state }}
+
- {% trans "Creation Date" %}
- {{ task.created_at|parse_isotime }}
@@ -63,3 +73,4 @@
{% endblock %}
+
\ No newline at end of file
diff --git a/mistraldashboard/tasks/views.py b/mistraldashboard/tasks/views.py
index 14e468a..fcda8c7 100644
--- a/mistraldashboard/tasks/views.py
+++ b/mistraldashboard/tasks/views.py
@@ -25,7 +25,8 @@ from horizon import forms
from horizon import tables
from mistraldashboard import api
-from mistraldashboard.default.utils import prettyprint
+from mistraldashboard.default import utils
+
from mistraldashboard import forms as mistral_forms
from mistraldashboard.tasks import tables as mistral_tables
@@ -63,6 +64,7 @@ class OverviewView(generic.TemplateView):
page_title = _("Task Details")
workflow_url = 'horizon:mistral:workflows:detail'
execution_url = 'horizon:mistral:executions:detail'
+ action_execution_url = 'horizon:mistral:action_executions:task'
def get_context_data(self, **kwargs):
context = super(OverviewView, self).get_context_data(**kwargs)
@@ -71,8 +73,18 @@ class OverviewView(generic.TemplateView):
args=[task.workflow_name])
task.execution_url = reverse(self.execution_url,
args=[task.workflow_execution_id])
- task.result = prettyprint(task.result)
- task.published = prettyprint(task.published)
+ task.result = utils.prettyprint(task.result)
+ task.published = utils.prettyprint(task.published)
+ task.state = utils.label(task.state)
+ if task.type and task.type == "ACTION":
+ task.type_url = reverse(self.action_execution_url, args=[task.id])
+
+ breadcrumb = [(task.id, reverse(
+ 'horizon:mistral:tasks:detail',
+ args=[task.id]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
context['task'] = task
return context
@@ -95,10 +107,10 @@ class CodeView(forms.ModalFormView):
if column == 'result':
io['name'] = _('Result')
- io['value'] = task.result = prettyprint(task.result)
+ io['value'] = task.result = utils.prettyprint(task.result)
elif column == 'published':
io['name'] = _('Published')
- io['value'] = task.published = prettyprint(task.published)
+ io['value'] = task.published = utils.prettyprint(task.published)
context['io'] = io
diff --git a/mistraldashboard/workbooks/templates/workbooks/detail.html b/mistraldashboard/workbooks/templates/workbooks/detail.html
index fea69ec..93dcef6 100644
--- a/mistraldashboard/workbooks/templates/workbooks/detail.html
+++ b/mistraldashboard/workbooks/templates/workbooks/detail.html
@@ -1,4 +1,5 @@
-{% extends 'base.html' %}
+
+{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Workbook Definition" %}{% endblock %}
@@ -11,3 +12,4 @@
{{ definition }}
{% endblock %}
+
diff --git a/mistraldashboard/workbooks/views.py b/mistraldashboard/workbooks/views.py
index 273e829..835b54f 100644
--- a/mistraldashboard/workbooks/views.py
+++ b/mistraldashboard/workbooks/views.py
@@ -43,6 +43,12 @@ class DetailView(generic.TemplateView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
workbook = self.get_data(self.request, **kwargs)
+ breadcrumb = [(workbook.name, reverse(
+ 'horizon:mistral:workbooks:detail',
+ args=[workbook.name]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
context['definition'] = workbook.definition
return context
diff --git a/mistraldashboard/workflows/templates/workflows/detail.html b/mistraldashboard/workflows/templates/workflows/detail.html
index 28f8e4c..4f15646 100644
--- a/mistraldashboard/workflows/templates/workflows/detail.html
+++ b/mistraldashboard/workflows/templates/workflows/detail.html
@@ -1,4 +1,5 @@
-{% extends 'base.html' %}
+
+{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Workflow Definition" %}{% endblock %}
@@ -11,3 +12,4 @@
{{ definition }}
{% endblock %}
+
\ No newline at end of file
diff --git a/mistraldashboard/workflows/views.py b/mistraldashboard/workflows/views.py
index ea9b0c1..4b979cf 100644
--- a/mistraldashboard/workflows/views.py
+++ b/mistraldashboard/workflows/views.py
@@ -43,10 +43,17 @@ class DetailView(generic.TemplateView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
workflow = self.get_data(self.request, **kwargs)
+ breadcrumb = [(workflow.name, reverse(
+ 'horizon:mistral:workflows:detail',
+ args=[workflow.name]
+ ))]
+
+ context["custom_breadcrumb"] = breadcrumb
context['definition'] = (
workflow.definition or
'This workflow was created as part of workbook %s'
% workflow.name.split('.')[0])
+
return context
def get_data(self, request, **kwargs):