Task List Panel
Main page allows viewing of all active, approved, completed and canceled tasks can click through to view full task details. Un-approved tasks can be resubmitted with new data, or approved, tasks can be cancelled. Change-Id: I35306fdf8e774d39b34c8c6fa50af5c70e010420
This commit is contained in:
parent
4feb401f9b
commit
113124e14b
|
@ -3,5 +3,6 @@ include setup.py
|
|||
recursive-include stacktask_ui/content/default/templates *
|
||||
recursive-include stacktask_ui/content/forgotpassword/templates *
|
||||
recursive-include stacktask_ui/content/project_users/templates *
|
||||
recursive-include stacktask_ui/content/tasks/templates *
|
||||
recursive-include stacktask_ui/content/token/templates *
|
||||
recursive-include stacktask_ui/static *
|
||||
|
|
|
@ -20,6 +20,8 @@ from six.moves.urllib.parse import urljoin
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
from horizon.utils import functions as utils
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -29,6 +31,12 @@ USER = collections.namedtuple('User',
|
|||
TOKEN = collections.namedtuple('Token',
|
||||
['action'])
|
||||
|
||||
TASK = collections.namedtuple('Task',
|
||||
['id', 'task_type', 'valid',
|
||||
'request_by', 'request_project',
|
||||
'created_on', 'approved_on', 'page',
|
||||
'completed_on', 'actions', 'status'])
|
||||
|
||||
|
||||
def _get_endpoint_url(request):
|
||||
# If the request is made by an anonymous user, this endpoint request fails.
|
||||
|
@ -230,3 +238,90 @@ def forgotpassword_submit(request, data):
|
|||
except Exception as e:
|
||||
LOG.error(e)
|
||||
raise
|
||||
|
||||
|
||||
def task_list(request, filters={}, page=1):
|
||||
tasks_per_page = utils.get_page_size(request)
|
||||
tasklist = []
|
||||
prev = more = False
|
||||
try:
|
||||
headers = {"Content-Type": "application/json",
|
||||
'X-Auth-Token': request.user.token.id}
|
||||
params = {
|
||||
"filters": json.dumps(filters),
|
||||
"page": page,
|
||||
"tasks_per_page": tasks_per_page
|
||||
}
|
||||
resp = get(request, "tasks", params=params, data=json.dumps({}),
|
||||
headers=headers).json()
|
||||
prev = resp['has_prev']
|
||||
more = resp['has_more']
|
||||
for task in resp['tasks']:
|
||||
tasklist.append(task_obj_get(request, task=task, page=page))
|
||||
except Exception as e:
|
||||
LOG.error(e)
|
||||
raise
|
||||
|
||||
return tasklist, prev, more
|
||||
|
||||
|
||||
def task_get(request, task_id):
|
||||
# Get a single task
|
||||
headers = {"Content-Type": "application/json",
|
||||
'X-Auth-Token': request.user.token.id}
|
||||
|
||||
return get(request, "tasks/%s" % task_id,
|
||||
headers=headers)
|
||||
|
||||
|
||||
def task_obj_get(request, task_id=None, task=None, page=0):
|
||||
if not task:
|
||||
task = task_get(request, task_id)
|
||||
|
||||
status = "Awaiting Approval"
|
||||
if task['cancelled']:
|
||||
status = "Cancelled"
|
||||
elif task['completed_on']:
|
||||
status = "Completed"
|
||||
elif task['approved_on']:
|
||||
status = "Approved; Incomplete"
|
||||
|
||||
valid = False not in [action['valid'] for
|
||||
action in task['actions']]
|
||||
return TASK(
|
||||
id=task['uuid'],
|
||||
task_type=task['task_type'],
|
||||
valid=valid,
|
||||
request_by=task['keystone_user'].get('username'),
|
||||
request_project=task['keystone_user'].get('project_name'),
|
||||
status=status,
|
||||
created_on=task['created_on'],
|
||||
approved_on=task['approved_on'],
|
||||
completed_on=task['completed_on'],
|
||||
actions=task['actions'],
|
||||
page=page
|
||||
)
|
||||
|
||||
|
||||
def task_cancel(request, task_id):
|
||||
headers = {"Content-Type": "application/json",
|
||||
'X-Auth-Token': request.user.token.id}
|
||||
|
||||
return delete(request, "tasks/%s" % task_id,
|
||||
headers=headers)
|
||||
|
||||
|
||||
def task_approve(request, task_id):
|
||||
headers = {"Content-Type": "application/json",
|
||||
'X-Auth-Token': request.user.token.id}
|
||||
|
||||
return post(request, "tasks/%s" % task_id,
|
||||
data=json.dumps({"approved": True}), headers=headers)
|
||||
|
||||
|
||||
def task_update(request, task_id, new_data):
|
||||
headers = {"Content-Type": "application/json",
|
||||
'X-Auth-Token': request.user.token.id}
|
||||
|
||||
return put(request, "tasks/%s" % task_id,
|
||||
data=new_data, headers=headers)
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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 horizon import messages
|
||||
|
||||
import json
|
||||
|
||||
from stacktask_ui.api import stacktask
|
||||
|
||||
|
||||
class UpdateTaskForm(forms.SelfHandlingForm):
|
||||
task_id = forms.CharField(widget=forms.HiddenInput())
|
||||
task_type = forms.CharField(widget=forms.TextInput(
|
||||
attrs={'readonly': True}))
|
||||
task_data = forms.CharField(widget=forms.Textarea)
|
||||
|
||||
def clean_task_data(self):
|
||||
taskdata = self.cleaned_data['task_data']
|
||||
try:
|
||||
json.loads(taskdata)
|
||||
except ValueError:
|
||||
raise forms.ValidationError(
|
||||
"Invalid non-JSON data in Task Data field")
|
||||
|
||||
return taskdata
|
||||
|
||||
def handle(self, request, data):
|
||||
task_id = self.cleaned_data.pop('task_id')
|
||||
try:
|
||||
response = stacktask.task_update(
|
||||
request, task_id, data['task_data'])
|
||||
if response.status_code == 200:
|
||||
messages.success(request, _('Updated task successfully.'))
|
||||
elif response.status_code == 400:
|
||||
messages.error(request, _(response.text))
|
||||
else:
|
||||
messages.error(request, _('Failed to update task.'))
|
||||
return True
|
||||
except Exception:
|
||||
messages.error(request, _('Failed to update task.'))
|
||||
return False
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
class TaskList(horizon.Panel):
|
||||
name = _('Tasks')
|
||||
slug = 'tasks'
|
||||
policy_rules = (("identity", "role:admin"),)
|
|
@ -0,0 +1,184 @@
|
|||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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 django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
|
||||
from stacktask_ui.api import stacktask
|
||||
|
||||
|
||||
class CancelTask(tables.DeleteAction):
|
||||
help_text = _("This will cancel all selected tasks.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Cancel Task",
|
||||
u"Cancel Tasks",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Cancelled Task",
|
||||
u"Cancelled Tasks",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
result = stacktask.task_cancel(request, obj_id)
|
||||
if not result or result.status_code != 200:
|
||||
exception = exceptions.NotAvailable()
|
||||
exception._safe_message = False
|
||||
raise exception
|
||||
|
||||
def allowed(self, request, task=None):
|
||||
if task:
|
||||
return not(
|
||||
task.status == "Completed" or task.status == "Cancelled")
|
||||
return True
|
||||
|
||||
|
||||
class ApproveTask(tables.BatchAction):
|
||||
name = "approve"
|
||||
help_text = _("This will approve all of the selected tasks.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Approve Task",
|
||||
u"Approve Tasks",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Approved Task",
|
||||
u"Approved Tasks",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
result = stacktask.task_approve(request, obj_id)
|
||||
if not result or result.status_code != 200:
|
||||
exception = exceptions.NotAvailable()
|
||||
exception._safe_message = False
|
||||
raise exception
|
||||
|
||||
def allowed(self, request, task=None):
|
||||
if task:
|
||||
return not(
|
||||
task.status == "Completed" or task.status == "Cancelled")
|
||||
return True
|
||||
|
||||
|
||||
class ReapproveTask(ApproveTask):
|
||||
name = "approve"
|
||||
help_text = _("This will approve all of the selected tasks.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Reapprove Task",
|
||||
u"Repprove Tasks",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Reapproved Task",
|
||||
u"Reapproved Tasks",
|
||||
count
|
||||
)
|
||||
|
||||
|
||||
class UpdateTask(tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Update Task")
|
||||
url = "horizon:management:tasks:update"
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
def allowed(self, request, task=None):
|
||||
if task:
|
||||
return task.status == 'Awaiting Approval'
|
||||
return True
|
||||
|
||||
|
||||
def TaskTypeDisplayFilter(task_type):
|
||||
return task_type.replace("_", " ").title()
|
||||
|
||||
|
||||
class TaskTable(tables.DataTable):
|
||||
uuid = tables.Column('id', verbose_name=_('Task ID'),
|
||||
hidden=True)
|
||||
task_type = tables.Column('task_type', verbose_name=_('Task Type'),
|
||||
filters=[TaskTypeDisplayFilter],
|
||||
link="horizon:management:tasks:detail")
|
||||
status = tables.Column('status', verbose_name=_('Status'))
|
||||
request_by = tables.Column('request_by', verbose_name=_('Requestee'))
|
||||
request_project = tables.Column('request_project',
|
||||
verbose_name=_('Request Project'))
|
||||
valid = tables.Column('valid', verbose_name=_("Actions Valid"))
|
||||
created_on = tables.Column('created_on',
|
||||
verbose_name=_('Request Date'))
|
||||
page = tables.Column('page', hidden=True)
|
||||
|
||||
class Meta(object):
|
||||
name = 'task_table'
|
||||
verbose_name = _('Tasks')
|
||||
table_actions = (CancelTask, ApproveTask)
|
||||
row_actions = (ApproveTask, UpdateTask, CancelTask)
|
||||
prev_pagination_param = pagination_param = 'task_page'
|
||||
|
||||
def get_prev_marker(self):
|
||||
return str(int(self.data[0].page) - 1) if self.data else ''
|
||||
|
||||
def get_marker(self):
|
||||
return str(int(self.data[0].page) + 1) if self.data else ''
|
||||
|
||||
def get_object_display(self, obj):
|
||||
task_type = obj.task_type.replace("_", " ").title()
|
||||
return "%s (%s)" % (task_type, obj.id)
|
||||
|
||||
|
||||
class ApprovedTaskTable(TaskTable):
|
||||
|
||||
class Meta(object):
|
||||
name = 'approved_table'
|
||||
verbose_name = _('Tasks')
|
||||
table_actions = (CancelTask, ReapproveTask)
|
||||
row_actions = (CancelTask, ReapproveTask)
|
||||
prev_pagination_param = pagination_param = 'approved_page'
|
||||
|
||||
|
||||
class CompletedTaskTable(TaskTable):
|
||||
|
||||
class Meta(object):
|
||||
name = 'completed_table'
|
||||
verbose_name = _('Tasks')
|
||||
prev_pagination_param = pagination_param = 'completed_page'
|
||||
|
||||
|
||||
class CancelledTaskTable(TaskTable):
|
||||
|
||||
class Meta(object):
|
||||
name = 'cancelled_table'
|
||||
verbose_name = _('Tasks')
|
||||
prev_pagination_param = pagination_param = 'cancelled_page'
|
|
@ -0,0 +1,134 @@
|
|||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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 exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from stacktask_ui.content.tasks import tables as task_tables
|
||||
from stacktask_ui.api import stacktask
|
||||
|
||||
|
||||
class ActiveTaskListTab(tabs.TableTab):
|
||||
table_classes = (task_tables.TaskTable,)
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
page_title = _("Active Tasks")
|
||||
name = _('Active')
|
||||
slug = 'active'
|
||||
filters = {'cancelled': {'exact': False},
|
||||
'approved': {'exact': False}}
|
||||
_prev = False
|
||||
_more = False
|
||||
|
||||
def get_task_table_data(self):
|
||||
tasks = []
|
||||
marker = self.request.GET.get(
|
||||
self.table_classes[0]._meta.pagination_param, 1)
|
||||
try:
|
||||
tasks, self._prev, self._more = stacktask.task_list(
|
||||
self.request, filters=self.filters, page=marker)
|
||||
except Exception:
|
||||
exceptions.handle(self.request, _('Failed to list tasks.'))
|
||||
return tasks
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return self._prev
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._more
|
||||
|
||||
|
||||
class ApprovedTaskListTab(ActiveTaskListTab):
|
||||
table_classes = (task_tables.ApprovedTaskTable,)
|
||||
page_title = _("Approved Tasks")
|
||||
name = _('Approved')
|
||||
slug = 'approved'
|
||||
filters = {'cancelled': {'exact': False},
|
||||
'approved': {'exact': True},
|
||||
'completed': {'exact': False}}
|
||||
|
||||
def get_approved_table_data(self):
|
||||
return super(ApprovedTaskListTab, self).get_task_table_data()
|
||||
|
||||
|
||||
class CompletedTaskListTab(ActiveTaskListTab):
|
||||
table_classes = (task_tables.CompletedTaskTable,)
|
||||
page_title = _("Completed Tasks")
|
||||
name = _('Completed')
|
||||
slug = 'completed'
|
||||
filters = {'completed': {'exact': True}}
|
||||
|
||||
def get_completed_table_data(self):
|
||||
return super(CompletedTaskListTab, self).get_task_table_data()
|
||||
|
||||
|
||||
class CancelledTaskListTab(ActiveTaskListTab):
|
||||
table_classes = (task_tables.CancelledTaskTable,)
|
||||
name = _('Cancelled')
|
||||
slug = 'cancelled'
|
||||
filters = {'cancelled': {'exact': True}}
|
||||
|
||||
def get_cancelled_table_data(self):
|
||||
return super(CancelledTaskListTab, self).get_task_table_data()
|
||||
|
||||
|
||||
class TaskTabs(tabs.TabGroup):
|
||||
slug = "tasks"
|
||||
tabs = (ActiveTaskListTab, ApprovedTaskListTab, CompletedTaskListTab,
|
||||
CancelledTaskListTab)
|
||||
sticky = True
|
||||
|
||||
def get_selected_tab(self):
|
||||
super(TaskTabs, self).get_selected_tab()
|
||||
if not self._selected:
|
||||
for tab in self.tabs:
|
||||
param = tab.table_classes[0]._meta.pagination_param
|
||||
if self.request.GET.get(param):
|
||||
self._selected = self.get_tab(tab.slug)
|
||||
return self._selected
|
||||
|
||||
|
||||
class TaskOverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
template_name = 'management/tasks/_task_detail_overview.html'
|
||||
|
||||
def get_context_data(self, request):
|
||||
print self.tab_group
|
||||
print self.tab_group.kwargs
|
||||
return {"task": self.tab_group.kwargs['task']}
|
||||
|
||||
|
||||
class TaskActionsTab(tabs.Tab):
|
||||
name = _("Actions")
|
||||
slug = "actions"
|
||||
template_name = 'management/tasks/_task_detail_actions.html'
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"task": self.tab_group.kwargs['task']}
|
||||
|
||||
|
||||
class TaskNotesTab(tabs.Tab):
|
||||
name = _("Action Notes")
|
||||
slug = "notes"
|
||||
template_name = 'management/tasks/_task_detail_notes.html'
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"task": self.tab_group.kwargs['task']}
|
||||
|
||||
|
||||
class TaskDetailTabs(tabs.DetailTabsGroup):
|
||||
slug = "task_details"
|
||||
tabs = (TaskOverviewTab, TaskActionsTab, TaskNotesTab)
|
|
@ -0,0 +1,17 @@
|
|||
{% load i18n sizeformat %}
|
||||
{% load task_filters %}
|
||||
|
||||
<div class="detail">
|
||||
<h4>{% trans "Actions" %}</h4>
|
||||
<hr class="header_rule">
|
||||
{% for action in task.actions %}
|
||||
<h5>{{ action.action_name }}</h5>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Valid" %}</dt>
|
||||
<dd>{{ action.valid }}</dd>
|
||||
<dt>{% trans "Data" %}</dt>
|
||||
<dd><pre>{{ action.data|pretty_json }}</pre></dd>
|
||||
</dl>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
{% load i18n sizeformat %}
|
||||
{% load task_filters %}
|
||||
|
||||
<div class="detail">
|
||||
<h4>{% trans "Action Notes" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
{% for action, notes in task.action_notes.iteritems %}
|
||||
<dt>{{ action }}</dt>
|
||||
{% for note in notes %}
|
||||
<dd>{{ note }}</dd>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</dl>
|
||||
</div>
|
|
@ -0,0 +1,45 @@
|
|||
{% load i18n sizeformat %}
|
||||
{% load task_filters %}
|
||||
|
||||
<div class="detail">
|
||||
<h4>{% trans "Task Details" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Task Type" %}</dt>
|
||||
<dd>{{ task.task_type }}</dd>
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ task.uuid }}</dd>
|
||||
<dt>{% trans "Cancelled" %}</dt>
|
||||
<dd>{{ task.cancelled }}</dd>
|
||||
<dt>{% trans "Approved" %}</dt>
|
||||
<dd>{{ task.approved }}</dd>
|
||||
<dt>{% trans "Completed" %}</dt>
|
||||
<dd>{{ task.completed }}</dd>
|
||||
{% if task.approved_by %}
|
||||
<dt>{% trans "Approved By" %}</dt>
|
||||
<dd>{{ task.approved_by.username }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Requested on" %}</dt>
|
||||
<dd>{{ task.created_on }}</dd>
|
||||
<dt>{% trans "Approved on" %}</dt>
|
||||
<dd>{% if task.approved_on %}{{ task.approved_on}}{% else %}-{% endif %}</dd>
|
||||
<dt>{% trans "Completed on" %}</dt>
|
||||
<dd>{% if task.completed_on %}{{ task.completed_on}}{% else %}-{% endif %}</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
{% if task.keystone_user %}
|
||||
<h4>{% trans "Requestee" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Username" %}</dt>
|
||||
<dd>{{ task.keystone_user.username }}</dd>
|
||||
<dt>{% trans "Project" %}</dt>
|
||||
<dd>{{ task.keystone_user.project_name }}</dd>
|
||||
<dt>{% trans "Roles" %}</dt>
|
||||
<dd>{{ task.keystone_user.roles|pretty_list }}</dd>
|
||||
<dt>{% trans "IP Address" %}</dt>
|
||||
<dd>{{ task.ip_address }}</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal_id %}update_user_form{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Tasks" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,6 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Update User" %}{% endblock %}
|
||||
{% block main %}
|
||||
{% include 'management/tasks/_update.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,18 @@
|
|||
from django import template
|
||||
import json
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def pretty_json(value):
|
||||
return json.dumps(value, indent=4)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def pretty_list(value):
|
||||
return ', '.join(value)
|
||||
|
||||
|
||||
register.filter('pretty_json', pretty_json)
|
||||
register.filter('pretty_list', pretty_list)
|
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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
|
||||
from django.conf.urls import url
|
||||
|
||||
from stacktask_ui.content.tasks import views
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^(?P<task_id>[^/]+)/$',
|
||||
views.TaskDetailView.as_view(),
|
||||
name='detail'),
|
||||
url(r'^(?P<task_id>[^/]+)/update/$',
|
||||
views.UpdateTaskView.as_view(), name='update'),
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
)
|
|
@ -0,0 +1,108 @@
|
|||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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 horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
|
||||
from horizon.utils import memoized
|
||||
|
||||
from stacktask_ui.content.tasks import tables as task_tables
|
||||
from stacktask_ui.content.tasks import forms as task_forms
|
||||
from stacktask_ui.content.tasks import tabs as task_tabs
|
||||
from stacktask_ui.api import stacktask
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = task_tabs.TaskTabs
|
||||
template_name = 'management/tasks/index.html'
|
||||
redirect_url = 'horizon:management:tasks:index'
|
||||
page_title = _("Tasks")
|
||||
|
||||
|
||||
class TaskDetailView(tabs.TabView):
|
||||
tab_group_class = task_tabs.TaskDetailTabs
|
||||
template_name = 'horizon/common/_detail.html'
|
||||
redirect_url = 'horizon:management:tasks:index'
|
||||
page_title = "{{ task.task_type }}"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TaskDetailView, self).get_context_data(**kwargs)
|
||||
task = self.get_data()
|
||||
context["task"] = task
|
||||
context["url"] = reverse(self.redirect_url)
|
||||
context["actions"] = self._get_actions(
|
||||
stacktask.task_obj_get(self.request, task=task))
|
||||
return context
|
||||
|
||||
def _get_actions(self, task):
|
||||
table = task_tables.TaskTable(self.request)
|
||||
return table.render_row_actions(task)
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
return stacktask.task_get(self.request, self.kwargs['task_id']).json()
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
task = self.get_data()
|
||||
return self.tab_group_class(request, task=task, **kwargs)
|
||||
|
||||
|
||||
class UpdateTaskView(forms.ModalFormView):
|
||||
form_class = task_forms.UpdateTaskForm
|
||||
form_id = "update_user_form"
|
||||
modal_header = _("Update Task")
|
||||
submit_label = _("Update")
|
||||
submit_url = 'horizon:management:tasks:update'
|
||||
template_name = 'management/tasks/update.html'
|
||||
context_object_name = 'project_users'
|
||||
success_url = "horizon:management:tasks:detail"
|
||||
page_title = _("Update Task")
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(self.success_url,
|
||||
args=(self.kwargs['task_id'],))
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
return stacktask.task_get(self.request,
|
||||
self.kwargs['task_id']).json()
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve user.')
|
||||
url = reverse('horizon:management:tasks:index')
|
||||
exceptions.handle(self.request, msg, redirect=url)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateTaskView, self).get_context_data(**kwargs)
|
||||
context['task'] = self.get_object()
|
||||
args = (self.kwargs['task_id'],)
|
||||
context['submit_url'] = reverse(self.submit_url, args=args)
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
task = self.get_object()
|
||||
task_data = {}
|
||||
for data in [action['data'] for action in task['actions']]:
|
||||
task_data.update(data)
|
||||
data = {'task_id': self.kwargs['task_id'],
|
||||
'task_type': task['task_type'],
|
||||
'task_data': json.dumps(task_data, indent=4),
|
||||
}
|
||||
return data
|
|
@ -0,0 +1,13 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'tasks'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'management'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'default'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'stacktask_ui.content.tasks.panel.TaskList'
|
||||
|
||||
ADD_INSTALLED_APPS = [
|
||||
'stacktask_ui.content.tasks'
|
||||
]
|
Loading…
Reference in New Issue