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()
cumin-developers@lists.fedorahosted.org