r5144 - branches/noflash/cumin/resources
by croberts@fedoraproject.org
Author: croberts
Date: 2011-11-21 21:18:18 +0000 (Mon, 21 Nov 2011)
New Revision: 5144
Modified:
branches/noflash/cumin/resources/app.js
Log:
Fixing the time-series chart popup so that the chart gets drawn after the ajax fetch for StickWin. We are now overriding the default handleResponse function for StickyWin. Essentially, we are using the original code, but now we are adding a call to drawAllCharts() after the content is fetched and displayed.
Modified: branches/noflash/cumin/resources/app.js
===================================================================
--- branches/noflash/cumin/resources/app.js 2011-11-21 20:11:08 UTC (rev 5143)
+++ branches/noflash/cumin/resources/app.js 2011-11-21 21:18:18 UTC (rev 5144)
@@ -408,11 +408,20 @@
useIframeShim: false,
allowMultiple: false,
closeOnEsc: true,
- uiOptions: {width: 420},
+ uiOptions: {width: 450},
edge: false,
position: 'center',
offset: {x:0,y:0},
- relativeTo: document.body
+ relativeTo: document.body,
+ handleResponse: function(response){
+ var responseScript = "";
+ this.Request.response.text.stripScripts(function(script){ responseScript += script; });
+ if (this.options.wrapWithUi) response = StickyWin.ui(this.options.caption, response, this.options.uiOptions);
+ this.setContent(response);
+ this.show();
+ if (this.evalScripts) $exec(responseScript);
+ drawAllCharts();
+ }
}).update();
}
return false;
12 years, 5 months
r5143 - branches/noflash/cumin/python/cumin
by croberts@fedoraproject.org
Author: croberts
Date: 2011-11-21 20:11:08 +0000 (Mon, 21 Nov 2011)
New Revision: 5143
Modified:
branches/noflash/cumin/python/cumin/stat.py
Log:
Reverting to fix the "show time series chart link" from grid::overview.
Modified: branches/noflash/cumin/python/cumin/stat.py
===================================================================
--- branches/noflash/cumin/python/cumin/stat.py 2011-11-21 20:09:23 UTC (rev 5142)
+++ branches/noflash/cumin/python/cumin/stat.py 2011-11-21 20:11:08 UTC (rev 5143)
@@ -14,6 +14,7 @@
from cumin.model import CuminStatistic, SamplesSqlAdapter
from cumin.widgets import StateSwitch
from wooly.template import WidgetTemplate
+from cumin.charts import CuminPieChart, TimeSeriesChart, StackedValueChart
from cumin.util import calc_rate, secs, nvl
from cumin.OpenFlashChart import Element, Chart
@@ -275,7 +276,8 @@
self.__files[name] = {"time": datetime.now(), "file": file, "cookie": args}
return file
-class PieChartPage(object):
+class PieChartPage(Page):
+ # handles pie.png request
PAGE_NAME = "pie.png"
RAINBOW = "rainbow"
GREENS = "greens"
@@ -293,6 +295,51 @@
(0,1,0), (.6,1,.6)],
"blank": [(1,1,1), (1,1,1), (1,1,1), (1,1,1)]}
+ def __init__(self, app, name):
+ super(PieChartPage, self).__init__(app, name)
+
+ param = IntegerParameter(app, "param")
+ self.slices = ListParameter(app, "slice", param)
+ self.add_parameter(self.slices)
+
+ self.color_scheme = Parameter(app, "cs")
+ self.add_parameter(self.color_scheme)
+
+ self.radius = IntegerParameter(app, "r")
+ self.add_parameter(self.radius)
+
+ # used to keep all the parameter names and the page name in one class
+ @classmethod
+ def get_href(cls, items, scheme, radius):
+ page = cls.PAGE_NAME
+
+ tokens = ["slice=%d" % x for x in items]
+
+ tokens.append("r=%s" % radius)
+
+ assert scheme in cls.color_schemes.keys()
+ tokens.append("cs=%s" % scheme)
+
+ src = "%s?%s" % (page, ";".join(tokens))
+ return src
+
+ def do_render(self, session):
+ radius = self.radius.get(session)
+
+ slices = self.slices.get(session)
+ color_scheme = self.color_scheme.get(session)
+ colors = self.color_schemes[color_scheme]
+
+ pie = CuminPieChart(radius)
+ surface = pie.plot_pie(slices, colors)
+
+ writer = Writer()
+ surface.write_to_png(writer)
+ return writer.to_string()
+
+ def get_content_type(self, session):
+ return "image/png"
+
class StatChartPage(Page):
# handles stats.png request
def __init__(self, app, name):
@@ -418,6 +465,151 @@
max_samples = int(width * 1.5)
return max(int(duration / max_samples), 1)
+ def render_samples(self, session, recent):
+ c = {(1,0,0): "red", (0,0,1): "blue", (0,1,0): "green"}
+ cached_samples, title_xy = self.get_cached_samples(session, recent)
+ if cached_samples:
+ rets = dict()
+ for stat in cached_samples:
+ ret = dict()
+ ret["color"] = c[cached_samples[stat][1]]
+ ret["points"] = cached_samples[stat][0]
+ ret["xy"] = title_xy[cached_samples[stat][1]]
+ rets[stat.name] = ret
+ return str(rets)
+
+ def do_render(self, session):
+ adapter, stats = self.get_adapter_stats(session)
+ colors = ((1,0,0), (0,0,1), (0,1,0))
+
+ recent = None
+ if len(stats):
+ recent = adapter.recent()
+ else:
+ return
+
+ # mouseover event wants the points returned in a array
+ if self.samples.get(session):
+ return self.render_samples(session, recent)
+
+ #if recent:
+ # cached_png = self.get_cached(session, recent)
+ # if cached_png:
+ # return cached_png
+
+ samples = dict()
+ values = dict()
+
+ width = self.container_width.get(session)
+ height = self.container_height.get(session)
+ mode = self.mode.get(session)
+ duration = self.duration.get(session)
+ interval = self.get_interval(session, duration, width)
+
+ chart = TimeSeriesChart(width, height, interval=interval)
+
+ if mode == "rate":
+ #method = None # don't do averaging
+ method = self.method.get(session)
+ for stat in stats:
+ os = adapter.samples(stat, duration, interval, method)
+ ns = list()
+ prev = None
+
+ for sample in reversed(os):
+ if prev is not None:
+ rate = calc_rate(float(sample[1]), float(prev[1]),
+ secs(sample[0]), secs(prev[0]))
+
+ ns.insert(0, (sample[0], rate, None))
+
+ prev = sample
+
+ samples[stat] = ns
+ else:
+ method = self.method.get(session)
+ for stat in stats:
+ samples[stat] = adapter.samples(stat, duration, interval, method)
+
+ type = self.type.get(session)
+ if type == "percent":
+ total_property = self.total_property.get(session)
+ total = self.get_object_property(session, total_property)
+ total = total and float(total) or 1.0
+
+ for stat in stats:
+ percents = [(x[0], (float(x[1]) / total) * 100.0, x[2])
+ for x in samples[stat] if x[1] is not None]
+ samples[stat] = percents
+
+ # take stddev into account for max and min y values
+ deviated_values = list()
+ for stat in stats:
+ for x in samples[stat]:
+ d1 = float(nvl(x[1], 0))
+ d2 = float(nvl(x[2], 0) / 2)
+ dv = (d1 + d2, d1 - d2)
+ deviated_values.append(dv)
+
+ max_value = deviated_values and max(max(deviated_values)) or 1
+ min_value = deviated_values and min(min(deviated_values)) or 0
+
+ # convert from decimal to float to avoid exceptions
+ max_value = float(max_value)
+ min_value = float(min_value)
+
+ max_value = round(max_value * 1.1 + 1)
+ if type == "percent" and max_value > 100:
+ # since none of our percentage graphs go above 100%
+ # we limit it here to match the flash graph
+ max_value = 100
+
+ if min_value < 0:
+ min_value = round(min_value * 1.1 - 1)
+
+ chart.x_max = duration
+ chart.x_min = 0
+ chart.y_max = max_value
+ chart.y_min = min_value
+
+ x_intervals = 8
+ x_step = 2
+
+ if chart.x_max == 600:
+ x_intervals = 10
+
+ y_intervals = 6
+ y_step = 2
+
+ if chart.y_max < 3:
+ y_step = 3
+
+ chart.plot_x_axis(x_intervals, x_step)
+ chart.plot_y_axis(y_intervals, y_step)
+
+ points = dict()
+ for stat, color in zip(stats, colors):
+ points[stat] = (chart.plot_values(samples[stat], color=color), color)
+
+ for stat, color in zip(stats, colors):
+ chart.plot_ticks(samples[stat], color=color)
+
+ chart.plot_frame()
+ chart.plot_interval(interval)
+
+ if mode == "rate":
+ titles = ["%s / sec" % x.title for x in stats]
+ else:
+ titles = ["%s%s" % ((type == "percent") and "Percent " or "", x.short_title and x.short_title or x.title) for x in stats]
+
+ title_xy = chart.plot_legend(titles, colors)
+
+ self.cache_it(session, chart, {'recent': recent, 'samples': points, "title_xy": title_xy})
+
+ writer = Writer()
+ chart.write(writer)
+ return writer.to_string()
+
class ModifiedAgentIdParameter(StringParameter):
def get(self, session):
agent = super(ModifiedAgentIdParameter, self).get(session)
@@ -1126,6 +1318,140 @@
def chart_factory(self, chart_type):
if chart_type == "area":
chart_obj = AreaChart(self.app, chart_type, self)
+ elif chart_type == "stacked":
+ chart_obj = StackedAreaChart(self.app, chart_type, self)
+ #chart_obj = StackedChart(self.app, chart_type, self)
+ elif chart_type == "percent":
+ chart_obj = PercentAreaChart(self.app, chart_type, self)
elif chart_type == "pie":
chart_obj = FlashPieChart(self.app, chart_type, self)
+
return chart_obj
+
+class PercentAreaChart(AreaChart):
+ def get_max_min(self, session, stats, samples, time_span, end_seconds_ago):
+ max_val, min_val = super(PercentAreaChart, self).get_max_min(session, stats, samples, time_span, end_seconds_ago)
+
+ percent = self.page.percent_property.get(session)
+ total = self.page.get_object_property(session, percent)
+ total = total and float(total) or 1.0
+
+ max_val = (max_val / total) * 100.0
+ min_val = (min_val / total) * 100.0
+
+ max_val = round(max_val * 1.1 + 1)
+ # cap at 100
+ max_val = min(max_val, 100.0)
+
+ if min_val < 0:
+ min_val = round(min_val * 1.1 - 1)
+
+ return max_val, min_val
+
+ def get_vals(self, session, samples, stat, text, duration, end_secs):
+ tnow = time()
+ vals = list()
+ percent = self.page.percent_property.get(session)
+ total = self.page.get_object_property(session, percent)
+
+ min_dt = tnow - duration - end_secs
+ for dt, value, dev in samples[stat]:
+ if value is not None:
+ if total is None or total == 0.0:
+ vals.append({"dt": secs(dt), "y": 0.0})
+ else:
+ vals.append({"dt": secs(dt), "y": (float(value) / float(total)) * 100.0})
+ if secs(dt) < min_dt:
+ break
+
+ vals.reverse()
+ return vals
+
+ def get_line_title(self, stat):
+ return "Percent %s" % stat.title
+
+class StatStackedPage(StatChartPage):
+ # handles stacked.png requests
+ def do_render(self, session):
+ adapter, stats = self.get_adapter_stats(session)
+
+ if len(stats):
+ recent = adapter.recent()
+ if recent:
+ cached_png = self.get_cached(session, recent)
+ if cached_png:
+ return cached_png
+
+ legend_height = len(stats) * 16 + 16
+
+ width = self.container_width.get(session)
+ height = self.container_height.get(session)
+ chart = StackedValueChart(width, height, legend_height)
+
+ duration = self.duration.get(session)
+ method = self.method.get(session)
+ interval = self.get_interval(session, duration, width)
+
+ max_value = 1
+ min_value = 999999
+
+ points = dict()
+ samples = dict()
+ collapsed = dict()
+ values = dict()
+ for stat in stats:
+ samples[stat] = adapter.samples(stat, duration, interval, method)
+ # we sometimes get multiple samples for the same qmf_update_time
+ for sample in samples[stat]:
+ collapsed[sample[0]] = sample[1]
+
+ for t in collapsed:
+ if not t in points:
+ points[t] = list()
+ values[t] = 0
+ points[t].append(collapsed[t])
+ values[t] = values[t] + collapsed[t]
+ max_value = max(values[t], max_value)
+ min_value = min(values[t], min_value)
+
+ max_value = round(max_value * 1.1 + 1)
+
+ if min_value < 0:
+ min_value = round(min_value * 1.1 - 1)
+
+ chart.x_max = duration
+ chart.x_min = 0
+ chart.y_max = max_value
+ chart.y_min = min_value
+
+ x_intervals = 8
+ x_step = 2
+
+ if chart.x_max == 600:
+ x_intervals = 10
+
+ y_intervals = 6
+ y_step = 2
+
+ if chart.y_max < 3:
+ y_step = 3
+
+ chart.plot_x_axis(x_intervals, x_step)
+ chart.plot_y_axis(y_intervals, y_step)
+
+ colors = ((1,0,0), (0,0,1), (0,1,0), (1,0,1), (1,1,0), (0,1,1), (0,0,0))
+
+ for t in points:
+ chart.plot_values(t, points[t], colors)
+
+ chart.plot_frame()
+
+ titles = [x.title for x in stats]
+
+ chart.plot_legend(reversed(titles), reversed(colors[:len(titles)]))
+
+ self.cache_it(session, chart, {'recent': recent, 'samples': None, "title_xy": None})
+
+ writer = Writer()
+ chart.write(writer)
+ return writer.to_string()
12 years, 5 months
r5142 - branches/noflash/cumin/python/cumin
by croberts@fedoraproject.org
Author: croberts
Date: 2011-11-21 20:09:23 +0000 (Mon, 21 Nov 2011)
New Revision: 5142
Added:
branches/noflash/cumin/python/cumin/charts.py
Log:
Got a little whack happy with previous commit. Reverting to include charts.py for now.
Added: branches/noflash/cumin/python/cumin/charts.py
===================================================================
--- branches/noflash/cumin/python/cumin/charts.py (rev 0)
+++ branches/noflash/cumin/python/cumin/charts.py 2011-11-21 20:09:23 UTC (rev 5142)
@@ -0,0 +1,439 @@
+import logging
+
+from cairo import *
+from random import random
+from time import time
+from math import sqrt, pi, ceil
+
+from formats import *
+from util import *
+
+log = logging.getLogger("cumin.chart")
+
+class CuminPieChart(object):
+ def __init__(self, radius=400):
+ self.width = radius * 2
+ self.height = self.width
+ self.centerx = radius
+ self.centery = radius
+ self.radius = radius
+
+ def plot_pie(self, slices, colors):
+ surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height))
+ cr = Context(surface)
+ cr.set_line_width(2)
+
+ total = float(sum(slices))
+
+ if not total:
+ # draw a grey placeholder circle
+ cr.new_path()
+ cr.arc(self.centerx, self.centery,
+ self.radius, 0, 2 * pi)
+ cr.close_path()
+ cr.set_source_rgba(0, 0, 0, 0.1)
+ cr.stroke()
+ return surface
+
+ wedge = cum_wedges = end = 0.0
+
+ # are we going to draw more than one slice
+ draw_border = sum([1 for x in slices if x > 0]) > 1
+
+ for slice, color in zip(slices, colors):
+ if slice > 0:
+ wedge = slice / total
+
+ start = end
+ end = (cum_wedges + wedge) * 2 * pi
+ cum_wedges += wedge
+
+ # draw the wedge
+ self.draw_slice_path(cr, self.centerx, self.centery,
+ self.radius, start, end)
+ cr.set_source_rgb(*color)
+ cr.fill()
+
+ # draw a white border
+ if draw_border:
+ self.draw_slice_path(cr, self.centerx, self.centery,
+ self.radius, start, end)
+ cr.set_source_rgb(1,1,1)
+ cr.stroke()
+
+ return surface
+
+ def draw_slice_path(self, cr, x, y, radius, start, end):
+ cr.new_path()
+ cr.move_to(x, y)
+ cr.arc(x, y, radius, start, end)
+ cr.close_path()
+
+class HeatMapChart(object):
+ def __init__(self, width=400, height=400):
+ self.width = width # final width
+ self.height = height
+ self.max_width = width # max width
+ self.max_height = height
+ self.cols = 0 # columns
+ self.rows = 0
+ self.max_size = 28 # size of each slot
+ self.surface = None
+ self.gap = 3
+
+ def plot_colored_shape(self, interior, shape, width, height):
+ surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
+ cr = Context(surface)
+ cr.set_line_width(1)
+
+ cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
+ cr.rectangle(0, 0, width - 1, height - 1)
+ cr.fill()
+
+ cr.set_line_width(2)
+ self._plot_shape(cr, interior, shape, 0, 0, width, height)
+ return surface
+
+ def plot_colored_rect(self, interior, width, height):
+ surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
+ cr = Context(surface)
+ cr.set_line_width(1)
+
+ cr.set_source_rgb(*interior)
+ cr.rectangle(0, 0, width - 1, height - 1)
+ cr.fill()
+ return surface
+
+ def plot_state(self, state, width, height):
+ surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
+ cr = Context(surface)
+ cr.set_line_width(2)
+
+ self._plot_square(cr, (0.7, 0.7, 0.7), state, 0, 0, width, height)
+ return surface
+
+ def _plot_square(self, cr, colors, state, x, y, width, height):
+ if state == "Unclaimed":
+ shape = "box"
+ elif state == "Claimed":
+ shape = "solid"
+ elif state in ("Owner", "Unavailable"):
+ shape = "diagonal"
+ elif state in ("Matched", "Preempting", "Preempting/Matched"):
+ shape = "filled_triangle"
+ else:
+ shape = "box"
+
+ self._plot_shape(cr, colors, shape, x, y, width, height)
+
+ def _plot_shape(self, cr, colors, shape, x, y, width, height):
+ cr.set_source_rgb(*colors)
+ #cr.move_to(x, y)
+ cr.rectangle(x, y, width - self.gap, height - self.gap)
+
+ if shape == "box": # leave empty
+ cr.stroke()
+ elif shape == "solid": # solid fill
+ cr.fill()
+ elif shape == "diagonal": # diagonal line
+ cr.move_to(x, y)
+ cr.line_to(x + width - self.gap, y + height - self.gap)
+ cr.stroke()
+ elif shape == "filled_triangle": # triangle
+ cr.stroke()
+ cr.move_to(x + width - self.gap, y)
+ cr.line_to(x, y + height - self.gap)
+ cr.line_to(x + width - self.gap, y + height - self.gap)
+ cr.close_path()
+ cr.fill()
+
+ def plot_shapes(self, shape_info, zl):
+ count = len(shape_info)
+ slot_size = self.slot_size(count, zl)
+ x = 0
+ y = 0
+ col = 0
+ self.rows = 1
+ # the width and height depend on the number of slots
+ self.surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height))
+ cr = Context(self.surface)
+
+ cr.set_line_width( zl == 1 and 1 or 2 )
+
+ for slot in shape_info:
+ interior = slot["color"]
+ shape = slot["shape"]
+
+ if col >= self.cols:
+ x = 0
+ y = y + slot_size
+ col = 0
+ self.rows = self.rows + 1
+
+ self._plot_shape(cr, interior, shape, x, y, slot_size, slot_size)
+
+ x = x + slot_size
+ col = col + 1
+ return slot_size
+
+ def slot_size(self, count, zoom=1):
+ sq = sqrt(count)
+ cols = int(sq)
+ if sq > cols:
+ cols = cols + 1
+ size = int(self.width * zoom / cols)
+ if size < 2:
+ size = 2
+ if size > self.max_size * zoom:
+ size = self.max_size * zoom
+
+ self.width = int((size * cols) + 1)
+ self.height = int((ceil(count * 1.0 / cols) * size) + 1)
+ self.cols = cols
+ return size
+
+ def write(self, writer):
+ self.surface.write_to_png(writer)
+
+class TimeSeriesChart(object):
+ def __init__(self, width, height, interval=0):
+ self.width = width - 40
+ self.height = height - 20
+ real_height = height + ((interval > 10) and 12 or 0)
+ self.surface = ImageSurface(FORMAT_ARGB32, int(width), int(real_height))
+ self.surface.set_device_offset(1.5, 5.5)
+ self.x_max = 1
+ self.x_min = 0
+ self.y_max = 1
+ self.y_min = 0
+
+ def plot_values(self, samples, color=(0, 0, 0)):
+ cr = Context(self.surface)
+ cr.set_line_width(1.5)
+ cr.set_source_rgba(color[0], color[1], color[2], 0.66)
+
+ tnow = time()
+ points = list()
+
+ for dt, value, dev in samples:
+ if value is None:
+ continue
+
+ t = secs(dt)
+
+ # prevent line from drawing off right side of grid
+ if tnow - t < 0:
+ t = tnow
+
+ x = self.width - ((tnow - t) / float(self.x_max)) * self.width
+ y = self.height - (value / float(self.y_max)) * self.height
+ cr.line_to(x, y)
+ if x < 0:
+ break
+ points.append([int(x), int(y), "%.2f" % value])
+
+ cr.stroke()
+ return points
+
+ def plot_ticks(self, samples, color=(0, 0, 0)):
+ cr = Context(self.surface)
+ cr.set_line_width(1.5)
+
+ tnow = time()
+ dot_size = max(int(self.height / 100), 2)
+ half_dot = int(dot_size / 2)
+
+ for dt, value, dev in samples:
+ if value is None:
+ continue
+
+ t = secs(dt)
+
+ # prevent line from drawing off right side of grid
+ if tnow - t < 0:
+ t = tnow
+
+ cr.set_source_rgba(color[0], color[1], color[2], 0.66)
+ x = self.width - ((tnow - t) / float(self.x_max)) * self.width
+ y = self.height - (value / float(self.y_max)) * self.height
+ cr.move_to(x, y)
+ cr.rectangle(x - half_dot, y - half_dot, dot_size, dot_size)
+ cr.fill()
+
+ if dev:
+ cr.set_source_rgba(0, 0, 0, 0.1)
+ half_dev = int(((float(dev) / float(self.y_max)) * self.height) / 2)
+ cr.move_to(x - dot_size, y - half_dev)
+ cr.line_to(x + dot_size, y - half_dev)
+
+ cr.move_to(x, y - half_dev)
+ cr.line_to(x, y + half_dev)
+
+ cr.move_to(x - dot_size, y + half_dev)
+ cr.line_to(x + dot_size, y + half_dev)
+
+ cr.stroke()
+
+ def plot_legend(self, titles, colors):
+ cr = Context(self.surface)
+ cr.set_line_width(1.5)
+ title_xy = dict()
+
+ for i, item in enumerate(zip(titles, colors)):
+ title, color = item
+ y = 16 + i * 16
+
+ cr.set_source_rgba(1, 1, 1, 0.75)
+ cr.rectangle(4, y - 12, 16 + 6.5 * len(title), 16)
+ cr.fill()
+ cr.stroke()
+
+ cr.set_source_rgb(*color)
+ cr.rectangle(8, y, 8, -8)
+ cr.fill()
+ cr.stroke()
+
+ cr.move_to(20, y)
+ cr.set_source_rgb(0, 0, 0)
+ cr.show_text(title)
+ cr.stroke()
+
+ width, height = cr.text_extents(title)[2:4]
+ title_xy[color] = [width, y]
+ return title_xy
+
+ def plot_frame(self, color=(0.8, 0.8, 0.8)):
+ cr = Context(self.surface)
+ cr.set_line_width(1)
+ cr.set_source_rgb(*color)
+
+ cr.move_to(self.width, 0)
+ cr.line_to(self.width, self.height)
+ cr.line_to(0, self.height)
+
+ cr.stroke()
+
+ def plot_interval(self, interval):
+ if interval > 10:
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.2, 0.2, 0.2)
+
+ cr.move_to(2, self.height + 24)
+ cr.show_text("* Samples averaged over %i second interval" % interval)
+ cr.stroke()
+
+ def plot_x_axis(self, intervals, step):
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.6, 0.6, 0.6)
+
+ interval = self.width / float(intervals)
+ absmax = self.x_max - self.x_min
+
+ for i in range(0, intervals + 1):
+ x = self.width - (i * interval)
+
+ cr.move_to(x, 0)
+ cr.line_to(x, self.height + 12)
+ cr.move_to(x + 2, self.height + 12)
+
+ if i % step == 0:
+ value = absmax - (absmax * x / float(self.width))
+ text = fmt_duration_brief(value)
+ if text:
+ cr.show_text(text)
+
+ cr.stroke()
+
+ def plot_y_axis(self, intervals, step):
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.6, 0.6, 0.6)
+
+ interval = self.height / float(intervals)
+
+ for i in range(0, intervals + 1):
+ y = self.height - (i * interval)
+
+ cr.move_to(0, y)
+ cr.line_to(self.width + 3, y)
+ cr.move_to(self.width + 4, y + 3)
+
+ if i % step == 0:
+ fraction = (self.height - y) / float(self.height)
+ value = fraction * self.y_max - self.y_min
+ value = int(round(value + self.y_min))
+
+ if value >= 1000000:
+ svalue = "%.2fM" % (round(value / 1000000.0, 2))
+ elif value >= 10000:
+ svalue = "%ik" % int(round(value / 1000.0, -1))
+ else:
+ svalue = str(value)
+
+ cr.show_text(svalue)
+
+ cr.stroke()
+
+ def write(self, writer):
+ self.surface.write_to_png(writer)
+
+class StackedValueChart(TimeSeriesChart):
+ def __init__(self, width, height, legend_height = 0):
+ super(StackedValueChart, self).__init__(width, height)
+
+ if legend_height:
+ self.surface = ImageSurface(FORMAT_ARGB32, int(width), int(height + legend_height))
+ self.surface.set_device_offset(1.5, 5.5)
+
+ self.legend_height = legend_height
+ self.bar_width = 2
+
+ def plot_values(self, dt, samples, colors):
+ cr = Context(self.surface)
+ cr.set_line_width(self.bar_width)
+
+ tnow = time()
+ y = self.height
+ v = 0
+ for value, color in zip(samples, colors):
+ if value is None:
+ continue
+
+ t = secs(dt)
+
+ if tnow - t < 0:
+ t = tnow
+
+ x = self.width - ((tnow - t) / float(self.x_max)) * self.width
+ cr.move_to(x, y)
+ cr.set_source_rgba(color[0], color[1], color[2], 0.66)
+
+ v = v + value
+ y = self.height - (v / float(self.y_max)) * self.height
+ cr.line_to(x, y)
+ cr.stroke()
+
+ def plot_legend(self, titles, colors):
+ cr = Context(self.surface)
+
+ for i, item in enumerate(zip(titles, colors)):
+ title, color = item
+ y = self.height + 26 + i * 16
+
+ cr.set_source_rgba(1, 1, 1, 0.75)
+ cr.rectangle(4, y - 12, 16 + 6.5 * len(title), 16)
+ cr.fill()
+ cr.stroke()
+
+ cr.set_source_rgb(*color)
+ cr.rectangle(8, y, 8, -8)
+ cr.fill()
+ cr.stroke()
+
+ cr.move_to(20, y)
+ cr.set_source_rgb(0, 0, 0)
+ cr.show_text(title)
+ cr.stroke()
+
12 years, 5 months
r5141 - branches/noflash/cumin/resources
by croberts@fedoraproject.org
Author: croberts
Date: 2011-11-14 14:45:16 +0000 (Mon, 14 Nov 2011)
New Revision: 5141
Modified:
branches/noflash/cumin/resources/app.js
Log:
More jqplot memory leak fixes. Also, fixing the console exceptions when mousing over pie charts that are drawn *after* a line chart had been drawn. This was because $j.jqplot.config.enablePlugins gets set to true for line charts. The fix is to set $j.jqplot.config.enablePlugins = false when drawing a pie chart (that hopefully doesn't require any plugins).
Modified: branches/noflash/cumin/resources/app.js
===================================================================
--- branches/noflash/cumin/resources/app.js 2011-11-11 22:23:08 UTC (rev 5140)
+++ branches/noflash/cumin/resources/app.js 2011-11-14 14:45:16 UTC (rev 5141)
@@ -603,7 +603,17 @@
if(allCharts[divName]) {
var parent = $(divName).getParent();
var newDiv = $(divName);
- allCharts[divName].destroy();
+ allCharts[divName].canvasManager.freeAllCanvases();
+ if (allCharts[divName].eventCanvas) {
+ allCharts[divName].eventCanvas._elem.unbind();
+ }
+ // Couple of posts on Stack Overflow indicate that empty() doesn't
+ // always cear up the dom and release memory. Sometimes setting
+ // innerHTML property to null is needed. Particularly on IE, may
+ // have to directly set it to null, bypassing jQuery.
+ allCharts[divName].target.empty();
+ allCharts[divName].target[0].innerHTML = '';
+ allCharts[divName].destroy();
newDiv.inject(parent);
allCharts[divName] = $j.jqplot(holder, dataContainer['y_coordinate_values'],chartOptionsObject);
} else {
@@ -647,6 +657,7 @@
}
updatePieChart = function (piediv, vals, colors) {
+ $j.jqplot.config.enablePlugins = false;
var data = vals;
if(allCharts[piediv]) {
allCharts[piediv].destroy();
@@ -675,8 +686,9 @@
highlightMouseOver: false
}
},
- legend: { show:false},
- seriesColors: colors
+ legend: {show:false},
+ seriesColors: colors,
+ cursor: {show:false},
}
);
}
12 years, 5 months
r5140 - trunk/cumin/etc
by tmckay@fedoraproject.org
Author: tmckay
Date: 2011-11-11 22:23:08 +0000 (Fri, 11 Nov 2011)
New Revision: 5140
Modified:
trunk/cumin/etc/sysvinit-cumin
Log:
Let improved scripts give advice instead of the init script.
Modified: trunk/cumin/etc/sysvinit-cumin
===================================================================
--- trunk/cumin/etc/sysvinit-cumin 2011-11-11 21:47:46 UTC (rev 5139)
+++ trunk/cumin/etc/sysvinit-cumin 2011-11-11 22:23:08 UTC (rev 5140)
@@ -58,18 +58,14 @@
cumin-database check &> /dev/null || {
# Run again with output left on for user.
echo_failure; echo
- echo "Cumin's database is not yet installed"
- echo "Run 'cumin-database install' as root"
-
echo "(detailed output from cumin-database check:)"
- cumin-database check noadvice
+ cumin-database check
exit 1
}
cumin-admin check-schema &> /dev/null || {
# Run again with output left on for user.
echo_failure; echo
- echo "Run 'cumin-admin upgrade-schema' for available upgrades"
echo "(detailed output from cumin-admin check-schema:)"
cumin-admin check-schema
exit 1
12 years, 5 months
r5139 - tags
by tmckay@fedoraproject.org
Author: tmckay
Date: 2011-11-11 21:47:46 +0000 (Fri, 11 Nov 2011)
New Revision: 5139
Added:
tags/sesame-1.1/
Log:
Add a tag for 1.1
12 years, 5 months
r5138 - trunk/sesame/cpp/etc
by tmckay@fedoraproject.org
Author: tmckay
Date: 2011-11-11 21:08:33 +0000 (Fri, 11 Nov 2011)
New Revision: 5138
Added:
trunk/sesame/cpp/etc/sesame.service
Log:
Add sesame.service file for systemd (F15+)
Added: trunk/sesame/cpp/etc/sesame.service
===================================================================
--- trunk/sesame/cpp/etc/sesame.service (rev 0)
+++ trunk/sesame/cpp/etc/sesame.service 2011-11-11 21:08:33 UTC (rev 5138)
@@ -0,0 +1,9 @@
+[Unit]
+Description=MRG Management system agent
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/sesame
+
+[Install]
+WantedBy=multi-user.target
12 years, 5 months
r5137 - trunk/cumin/etc
by tmckay@fedoraproject.org
Author: tmckay
Date: 2011-11-11 19:49:40 +0000 (Fri, 11 Nov 2011)
New Revision: 5137
Modified:
trunk/cumin/etc/cumin.service
Log:
Add --syslog to ExecStart
Modified: trunk/cumin/etc/cumin.service
===================================================================
--- trunk/cumin/etc/cumin.service 2011-11-11 19:42:20 UTC (rev 5136)
+++ trunk/cumin/etc/cumin.service 2011-11-11 19:49:40 UTC (rev 5137)
@@ -4,7 +4,7 @@
[Service]
Type=simple
-ExecStart=/usr/bin/cumin
+ExecStart=/usr/bin/cumin --syslog
[Install]
WantedBy=multi-user.target
12 years, 5 months
r5136 - trunk/cumin/bin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2011-11-11 19:42:20 +0000 (Fri, 11 Nov 2011)
New Revision: 5136
Modified:
trunk/cumin/bin/cumin
Log:
Path wrong in syslog entry
Modified: trunk/cumin/bin/cumin
===================================================================
--- trunk/cumin/bin/cumin 2011-11-11 19:36:09 UTC (rev 5135)
+++ trunk/cumin/bin/cumin 2011-11-11 19:42:20 UTC (rev 5136)
@@ -49,7 +49,7 @@
def log(cls):
if _syslog.enabled:
home = os.environ.get("CUMIN_HOME", os.path.normpath("/usr/share/cumin"))
- syslog.syslog("cumin: master script exited with errors, see %s/master.log" % home)
+ syslog.syslog("cumin: master script exited with errors, see %s/log/master.log" % home)
def main():
12 years, 5 months
r5135 - trunk/cumin/bin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2011-11-11 19:36:09 +0000 (Fri, 11 Nov 2011)
New Revision: 5135
Modified:
trunk/cumin/bin/cumin
Log:
Move import of errors after path change
Modified: trunk/cumin/bin/cumin
===================================================================
--- trunk/cumin/bin/cumin 2011-11-11 19:28:53 UTC (rev 5134)
+++ trunk/cumin/bin/cumin 2011-11-11 19:36:09 UTC (rev 5135)
@@ -8,12 +8,12 @@
import traceback
from datetime import datetime, timedelta
import syslog
-from cumin.errors import CuminErrors
home = os.environ.get("CUMIN_HOME", os.path.normpath("/usr/share/cumin"))
sys.path.append(os.path.join(home, "python"))
from cumin.config import *
+from cumin.errors import CuminErrors
from parsley.loggingex import *
log = logging.getLogger("cumin.master")
12 years, 5 months