diff --git a/tailbone/static/css/forms.css b/tailbone/static/css/forms.css index 950ca82d..dc85c46b 100644 --- a/tailbone/static/css/forms.css +++ b/tailbone/static/css/forms.css @@ -24,6 +24,23 @@ div.form-wrapper ul.context-menu li { } +/****************************** + * "object helper" panel + ******************************/ + +.object-helper { + border: 1px solid black; + float: right; + margin-top: 1em; + padding: 1em; + width: 20em; +} + +.object-helper-content { + margin-top: 1em; +} + + /****************************** * Forms ******************************/ diff --git a/tailbone/templates/batch/view.mako b/tailbone/templates/batch/view.mako index f535ce4a..4874d7ef 100644 --- a/tailbone/templates/batch/view.mako +++ b/tailbone/templates/batch/view.mako @@ -40,18 +40,6 @@ display: inline; } - .batch-helper { - border: 1px solid black; - float: right; - margin-top: 1em; - padding: 1em; - width: 20em; - } - - .batch-helper-content { - margin-top: 1em; - } - @@ -92,9 +80,9 @@ % if status_breakdown is not Undefined: -
+

Row Status Breakdown

-
+
% if status_breakdown:
diff --git a/tailbone/templates/tempmon/clients/view.mako b/tailbone/templates/tempmon/clients/view.mako index 2a508f73..f77b0663 100644 --- a/tailbone/templates/tempmon/clients/view.mako +++ b/tailbone/templates/tempmon/clients/view.mako @@ -1,4 +1,4 @@ -## -*- coding: utf-8 -*- +## -*- coding: utf-8; -*- <%inherit file="/master/view.mako" /> <%def name="head_tags()"> @@ -13,10 +13,23 @@ -${parent.body()} +
    + ${self.context_menu_items()} +
-% if instance.enabled and master.restartable_client(instance) and request.has_perm('tempmon.clients.restart'): -
- +% if instance.enabled and master.restartable_client(instance) and request.has_perm('{}.restart'.format(route_prefix)): +
+

Client Tools

+
+ +
% endif + +
+ ${form.render()|n} +
+ +% if master.has_rows: + ${rows_grid|n} +% endif diff --git a/tailbone/views/tempmon/clients.py b/tailbone/views/tempmon/clients.py index 3fd41723..1b9fd37d 100644 --- a/tailbone/views/tempmon/clients.py +++ b/tailbone/views/tempmon/clients.py @@ -35,6 +35,7 @@ from rattail_tempmon.db import model as tempmon import colander from webhelpers2.html import HTML, tags +from tailbone import grids from tailbone.views.tempmon import MasterView @@ -48,6 +49,9 @@ class TempmonClientView(MasterView): route_prefix = 'tempmon.clients' url_prefix = '/tempmon/clients' + has_rows = True + model_row_class = tempmon.Reading + grid_columns = [ 'config_key', 'hostname', @@ -71,6 +75,12 @@ class TempmonClientView(MasterView): 'archived', ] + row_grid_columns = [ + 'probe', + 'taken', + 'degrees_f', + ] + def configure_grid(self, g): super(TempmonClientView, self).configure_grid(g) @@ -106,15 +116,21 @@ class TempmonClientView(MasterView): f.set_enum('disk_type', self.enum.TEMPMON_DISK_TYPE) f.widgets['disk_type'].values.insert(0, ('', "(unknown)")) + # delay + f.set_helptext('delay', tempmon.Client.delay.__doc__) + # probes - f.set_renderer('probes', self.render_probes) + if self.viewing: + f.set_renderer('probes', self.render_probes) + else: + f.remove_field('probes') # notes f.set_type('notes', 'text') + # online if self.creating or self.editing: - f.remove_fields('probes', - 'online') + f.remove_field('online') def unique_config_key(self, node, value): query = self.Session.query(tempmon.Client)\ @@ -126,15 +142,43 @@ class TempmonClientView(MasterView): raise colander.Invalid(node, "Config key must be unique") def render_probes(self, client, field): - probes = client.probes - if not probes: + if not client.probes: return "" - items = [] - for probe in probes: - text = six.text_type(probe) - url = self.request.route_url('tempmon.probes.view', uuid=probe.uuid) - items.append(HTML.tag('li', c=[tags.link_to(text, url)])) - return HTML.tag('ul', c=items) + + route_prefix = self.get_route_prefix() + view_url = lambda p, i: self.request.route_url('tempmon.probes.view', uuid=p.uuid) + actions = [ + grids.GridAction('view', icon='zoomin', url=view_url), + ] + if self.request.has_perm('tempmon.probes.edit'): + url = lambda p, i: self.request.route_url('tempmon.probes.edit', uuid=p.uuid) + actions.append(grids.GridAction('edit', icon='pencil', url=url)) + + g = grids.Grid( + key='{}.probes'.format(route_prefix), + data=client.probes, + columns=[ + 'description', + 'critical_temp_min', + 'good_temp_min', + 'good_temp_max', + 'critical_temp_max', + 'status', + 'enabled', + ], + labels={ + 'critical_temp_min': "Crit. Min", + 'good_temp_min': "Good Min", + 'good_temp_max': "Good Max", + 'critical_temp_max': "Crit. Max", + }, + url=lambda p: self.request.route_url('tempmon.probes.view', uuid=p.uuid), + linked_columns=['description'], + main_actions=actions, + ) + g.set_enum('status', self.enum.TEMPMON_PROBE_STATUS) + g.set_type('enabled', 'boolean') + return HTML.literal(g.render_grid()) def delete_instance(self, client): # bulk-delete all readings first @@ -149,6 +193,24 @@ class TempmonClientView(MasterView): self.Session.delete(client) self.Session.flush() + def get_row_data(self, client): + query = self.Session.query(tempmon.Reading)\ + .join(tempmon.Probe)\ + .filter(tempmon.Reading.client == client) + return query + + def get_parent(self, reading): + return reading.client + + def configure_row_grid(self, g): + super(TempmonClientView, self).configure_row_grid(g) + + # probe + g.set_filter('probe', tempmon.Probe.description) + g.set_sorter('probe', tempmon.Probe.description) + + g.set_sort_defaults('taken', 'desc') + def restartable_client(self, client): return True diff --git a/tailbone/views/tempmon/probes.py b/tailbone/views/tempmon/probes.py index dda7192f..2320fa34 100644 --- a/tailbone/views/tempmon/probes.py +++ b/tailbone/views/tempmon/probes.py @@ -34,6 +34,7 @@ import colander from deform import widget as dfwidget from webhelpers2.html import tags +from tailbone import grids from tailbone.views.tempmon import MasterView @@ -47,6 +48,9 @@ class TempmonProbeView(MasterView): route_prefix = 'tempmon.probes' url_prefix = '/tempmon/probes' + has_rows = True + model_row_class = tempmon.Reading + grid_columns = [ 'client', 'config_key', @@ -74,6 +78,11 @@ class TempmonProbeView(MasterView): 'status', ] + row_grid_columns = [ + 'taken', + 'degrees_f', + ] + def configure_grid(self, g): super(TempmonProbeView, self).configure_grid(g) @@ -103,8 +112,10 @@ class TempmonProbeView(MasterView): f.set_label('client', "Tempmon Client") if self.creating or self.editing: f.replace('client', 'client_uuid') - clients = self.Session.query(tempmon.Client)\ - .order_by(tempmon.Client.config_key) + clients = self.Session.query(tempmon.Client) + if self.creating: + clients = clients.filter(tempmon.Client.archived == False) + clients = clients.order_by(tempmon.Client.config_key) client_values = [(client.uuid, "{} ({})".format(client.config_key, client.hostname)) for client in clients] f.set_widget('client_uuid', dfwidget.SelectWidget(values=client_values)) @@ -151,6 +162,23 @@ class TempmonProbeView(MasterView): self.Session.delete(probe) self.Session.flush() + def get_row_data(self, probe): + query = self.Session.query(tempmon.Reading)\ + .filter(tempmon.Reading.probe == probe) + return query + + def get_parent(self, reading): + return reading.client + + def configure_row_grid(self, g): + super(TempmonProbeView, self).configure_row_grid(g) + + # # probe + # g.set_filter('probe', tempmon.Probe.description) + # g.set_sorter('probe', tempmon.Probe.description) + + g.set_sort_defaults('taken', 'desc') + def includeme(config): TempmonProbeView.defaults(config)