westferry_ui_PYTHON = \
src/westferry/ui/__init__.py \
src/westferry/ui/base.py \
+ src/westferry/ui/graphs.py \
src/westferry/ui/menu.py \
src/westferry/ui/utils.py
templates_modulesdir = $(templatesdir)/modules
+dist_templates_modules_DATA =
+
+templates_modules_graphsdir = $(templates_modulesdir)/graphs
+
+dist_templates_modules_graphs_DATA = \
+ src/templates/modules/graphs/box.html \
+ src/templates/modules/graphs/preview.html
+
templates_modules_menudir = $(templates_modulesdir)/menu
dist_templates_modules_menu_DATA = \
--- /dev/null
+{% extends "base.html" %}
+
+{% block main %}
+ {% module GraphBox(graph) %}
+{% end block %}
--- /dev/null
+{% extends "base.html" %}
+
+{% block main %}
+ {% for g in graphs %}
+ {% module GraphBoxPreview(g) %}
+ {% end %}
+{% end block %}
--- /dev/null
+<div class="page-header">
+ <h3>{{ graph.title }}</h3>
+</div>
+
+<img class="img-responsive img-thumbnail" src="{{ graph.make_image_url(width="1130") }}"
+ alt="{{ graph.title }}">
+
+<ul class="nav nav-pills">
+ {% for interval in ("hour", "day", "week", "month", "year") %}
+ <li {% if graph.interval == interval %}class="active"{% end %}>
+ <a href="{{ graph.make_url(interval=interval) }}">
+ {% if interval == "hour" %}
+ {{ _("Hour") }}
+ {% elif interval == "day" %}
+ {{ _("Day") }}
+ {% elif interval == "week" %}
+ {{ _("Week") }}
+ {% elif interval == "month" %}
+ {{ _("Month") }}
+ {% elif interval == "year" %}
+ {{ _("Year") }}
+ {% else %}
+ {{ interval }}
+ {% end %}
+ </a>
+ </li>
+ {% end %}
+</ul>
+
+<a class="btn btn-default btn-sm" href="{{ graph.make_image_url(format="pdf") }}">
+ {{ _("Download") }}
+</a>
--- /dev/null
+<div class="page-header">
+ <h3>
+ <a href="{{ graph.make_url() }}">{{ graph.title }}</a>
+ </h3>
+</div>
+
+<a href="{{ graph.make_url(interval="week") }}">
+ <img class="img-responsive img-thumbnail" src="{{ graph.make_image_url() }}"
+ alt="{{ graph.title }}">
+</a>
return self._collecty
- def generate_graph(self, template_name, **kwargs):
- assert self.template_exists(template_name)
-
+ @staticmethod
+ def _remove_defaults(kwargs):
# Remove all keys with value None
# dbus cannot marshall None and when None is set, we assume
# that the default is selected, so we can simply remove the
if kwargs[key] is None:
del kwargs[key]
+ return kwargs
+
+ def generate_graph(self, template_name, **kwargs):
+ assert self.template_exists(template_name)
+ kwargs = self._remove_defaults(kwargs)
+
return self.collecty.generate_graph(template_name, **kwargs)
+ def graph_info(self, template_name, **kwargs):
+ assert self.template_exists(template_name)
+ kwargs = self._remove_defaults(kwargs)
+
+ return self.collecty.graph_info(template_name, **kwargs)
+
def template_exists(self, template_name):
"""
Returns True if a template with the given name exists
return m
+ def render_graphs(self, graphs):
+ self.render("graphs.html", graphs=graphs)
+
class AnalyticsOverviewHandler(AnalyticsBaseHandler):
url = r"/analytics"
m.add_handler(AnalyticsSystemOverviewHandler, title=_("Overview"))
m.add_divider()
- # Others
+ # Most interesting items
m.add_handler(AnalyticsSystemProcessorsHandler)
m.add_handler(AnalyticsSystemMemoryHandler)
+ m.add_handler(AnalyticsSystemTemperaturesHandler)
+
+ # Others
+ s = m.add_submenu(_("More"))
+ s.add_handler(AnalyticsSystemEntropyHandler)
return m
url = r"/analytics/system"
title = N_("System")
+ def get(self):
+ self.render("base.html")
+
class AnalyticsSystemProcessorsHandler(AnalyticsSystemBaseHandler):
url = r"/analytics/system/processors"
title = N_("Processors")
+ def get(self):
+ _ = self.locale.translate
+
+ graphs = [
+ ui.graphs.Graph(self, "processor"),
+ ui.graphs.Graph(self, "processor-temperature"),
+ ui.graphs.Graph(self, "cpufreq"),
+ ui.graphs.Graph(self, "loadavg"),
+ ]
+
+ self.render_graphs(graphs)
+
class AnalyticsSystemMemoryHandler(AnalyticsSystemBaseHandler):
url = r"/analytics/system/memory"
title = N_("Memory")
+ def get(self):
+ _ = self.locale.translate
+
+ graphs = [
+ ui.graphs.Graph(self, "memory"),
+ ]
+
+ self.render_graphs(graphs)
+
+
+class AnalyticsSystemTemperaturesHandler(AnalyticsSystemBaseHandler):
+ url = r"/analytics/system/temperatures"
+ title = N_("Temperatures")
+
+ def get(self):
+ _ = self.locale.translate
+
+ graphs = [
+ ui.graphs.Graph(self, "sensors-temperature"),
+ ui.graphs.Graph(self, "processor-temperature"),
+ ]
+
+ self.render_graphs(graphs)
+
+
+class AnalyticsSystemEntropyHandler(AnalyticsSystemBaseHandler):
+ url = r"/analytics/system/entropy"
+ title = N_("Entropy")
+
+ def get(self):
+ _ = self.locale.translate
+
+ graphs = [
+ ui.graphs.Graph(self, "entropy"),
+ ]
+
+ self.render_graphs(graphs)
+
class GraphExportHandler(base.BaseHandler):
VALID_INTERVALS = ("hour", "day", "month", "week", "year")
SUPPORTED_FORMATS = ("pdf", "png", "svg")
- url = r"/graph/([\w\-]+)(?:/([\w\d]+))?.(%s)" % "|".join(SUPPORTED_FORMATS)
+ url = r"/graph/([\w\-]+)(?:/([\w\d\.]+))?\.(%s)" % "|".join(SUPPORTED_FORMATS)
def get(self, template_name, object_id, format):
# Get the requested dimensions of the image
if interval and not interval in self.VALID_INTERVALS:
raise tornado.web.HTTPError(400, _("Invalid interval: %s") % interval)
+ # Create the graph object
+ g = ui.graphs.Graph(self, template_name, object_id=object_id)
+
# Generate the graph image
- g = self.backend.graphs.generate_graph(template_name, object_id=object_id,
- format=format.upper(), height=height, width=width, interval=interval)
+ image = g.generate_graph(format=format.upper(), interval=interval,
+ height=height, width=width)
# Set the HTTP headers
self._make_headers(format, template_name, object_id)
# Deliver the content
- self.finish(g)
+ self.finish(image.get("image"))
def _make_headers(self, extension, template_name, object_id):
# Determine the mime type
self.set_header("Content-Disposition", "attachment; filename=%s" % filename)
else:
self.set_header("Content-Disposition", "inline; filename=%s" % filename)
+
+
+class GraphHandler(base.BaseHandler):
+ url = r"/graph/([\w\-]+)(?:/([\w\d\.]+))?"
+
+ def get(self, template, object_id):
+ graph = ui.graphs.Graph(self, template, object_id=object_id)
+
+ self.render("graph.html", graph=graph)
--- /dev/null
+#!/usr/bin/python3
+###############################################################################
+# #
+# Westferry - The IPFire web user interface #
+# Copyright (C) 2015 IPFire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+N_ = lambda x: x
###############################################################################
from . import base
+from . import graphs
from . import menu
from . import utils
--- /dev/null
+#!/usr/bin/python3
+###############################################################################
+# #
+# Westferry - The IPFire web user interface #
+# Copyright (C) 2015 IPFire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import urllib.parse
+
+from . import base
+
+DEFAULT_INTERVAL = "day"
+DEFAULT_FORMAT = "svg"
+
+
+class Graph(object):
+ def __init__(self, handler, template, object_id=None, title=None):
+ self.handler = handler
+ self._title = title
+
+ # Graph identifier
+ self.template = template
+ self.object_id = object_id
+
+ # Set the default interval
+ self.interval = handler.get_argument("interval", DEFAULT_INTERVAL)
+ self.format = handler.get_argument("format", DEFAULT_FORMAT)
+
+ @property
+ def backend(self):
+ """
+ Shortcut to the backend.
+ """
+ return self.handler.backend
+
+ @property
+ def locale(self):
+ """
+ Shortcut to the locale
+ """
+ return self.handler.locale
+
+ @property
+ def graph_info(self):
+ if not hasattr(self, "_graph_info"):
+ self._graph_info = self.backend.graphs.graph_info(self.template,
+ object_id=self.object_id)
+
+ return self._graph_info
+
+ @property
+ def title(self):
+ return self._title or self.graph_info.get("title")
+
+ # Format
+
+ def get_format(self):
+ return self._format
+
+ def set_format(self, format):
+ # TODO check for valid inputs
+ self._format = format
+
+ # Interval
+
+ def get_interval(self):
+ return self._interval
+
+ def set_interval(self, interval):
+ # TODO check for valid inputs
+ self._interval = interval
+
+ interval = property(get_interval, set_interval)
+
+ def make_namespace(self, **kwargs):
+ ns = {
+ "format" : self.format,
+ "interval" : self.interval,
+ }
+ ns.update(kwargs)
+
+ return ns
+
+ def _make_url(self, url, format=None, **kwargs):
+ ns = self.make_namespace(**kwargs)
+
+ url = url % {
+ "format" : format or self.format or DEFAULT_FORMAT,
+ "object_id" : self.object_id,
+ "template" : self.template,
+ }
+
+ query_string = urllib.parse.urlencode(ns)
+ if query_string:
+ url += "?%s" % query_string
+
+ return url
+
+ def make_image_url(self, **kwargs):
+ if self.object_id:
+ url = "/graph/%(template)s/%(object_id)s.%(format)s"
+ else:
+ url = "/graph/%(template)s.%(format)s"
+
+ return self._make_url(url, **kwargs)
+
+ def make_url(self, **kwargs):
+ if self.object_id:
+ url = "/graph/%(template)s/%(object_id)s"
+ else:
+ url = "/graph/%(template)s"
+
+ return self._make_url(url, **kwargs)
+
+ def generate_graph(self, **kwargs):
+ return self.backend.graphs.generate_graph(self.template,
+ object_id=self.object_id, **kwargs)
+
+
+class GraphBoxModule(base.BaseUIModule):
+ def render(self, graph):
+ return self.render_string("modules/graphs/box.html", graph=graph)
+
+
+class GraphBoxPreviewModule(base.BaseUIModule):
+ def render(self, graph):
+ return self.render_string("modules/graphs/preview.html", graph=graph)
"""
return self.menu.locale
+ @property
+ def handler(self):
+ """
+ Shortcut to the handler of the main menu
+ """
+ return self.menu.handler
+
class SidebarMenuModule(base.BaseUIModule):
def render(self, menu):