Author: eallen
Date: 2009-07-01 13:30:11 -0400 (Wed, 01 Jul 2009)
New Revision: 3472
Added:
mgmt/trunk/cumin/python/cumin/OpenFlashChart.py
Modified:
mgmt/trunk/cumin/python/cumin/__init__.py
mgmt/trunk/cumin/python/cumin/grid/pool.py
mgmt/trunk/cumin/python/cumin/grid/scheduler.py
mgmt/trunk/cumin/python/cumin/grid/slot.py
mgmt/trunk/cumin/python/cumin/grid/submitter.py
mgmt/trunk/cumin/python/cumin/inventory/system.py
mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py
mgmt/trunk/cumin/python/cumin/messaging/connection.py
mgmt/trunk/cumin/python/cumin/messaging/exchange.py
mgmt/trunk/cumin/python/cumin/messaging/queue.py
mgmt/trunk/cumin/python/cumin/model.py
mgmt/trunk/cumin/python/cumin/stat.py
mgmt/trunk/cumin/python/cumin/stat.strings
mgmt/trunk/cumin/resources/app.js
Log:
First working version of Flash based charts. Implements line charts and displaying charts
in new full-screen window.
Added: mgmt/trunk/cumin/python/cumin/OpenFlashChart.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/OpenFlashChart.py (rev 0)
+++ mgmt/trunk/cumin/python/cumin/OpenFlashChart.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -0,0 +1,74 @@
+# 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; version 2 of the License.
+#
+# 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.
+#
+# Author: Eugene Kin Chee Yip
+# Date: 14 June 2009
+
+# Modified to not use json or cjson by Ernie
+import copy
+
+class Chart(dict):
+ # Dictionary for replacing attribute names
+ replaceKeyDictionary = {
+ "on_show": "on-show", "on_click":
"on-click",
+ "start_angle": "start-angle",
+
+ "threeD": "3d", "tick_height":
"tick-height",
+ "grid_colour": "grid-colour", "tick_length":
"tick-length",
+
+ "dot_style": "dot-style", "dot_size":
"dot-size",
+ "halo_size": "halo-size",
+
+ "line_style": "line-style", "outline_colour":
"outline-colour",
+ "fill_alpha": "fill-alpha", "gradient_fill":
"gradient-fill",
+ "visible_steps": "visible-steps",
+ }
+
+ # Redefine to allow for nested attributes.
+ # E.g. when calling the leaf attribute, text, in chart.title.text
+ # without previously defining the branch attribute, title.
+ def __getattribute__(self, key):
+ try:
+ return dict.__getattribute__(self, key)
+ except AttributeError:
+ self.__dict__[key] = Chart()
+ return dict.__getattribute__(self, key)
+
+ # This copy function is called when we want to get all the attributes of the
+ # chart instance so we can pass it off to cjson to create the JSON string.
+ # Recursive trick to get leaf attributes. Have to be careful of list types.
+ # Also, replace certain underscored keys.
+ # E.g. getting the leaf attribute, text, from the parent Chart instance where a
+ # previous assignment was to chart.title.text
+ def __copy__(self):
+ attributes = dict()
+ for key, value in self.__dict__.items():
+ if isinstance(value, list):
+ attributes[self.replaceKey(key)] = [copy.copy(item) for item in value]
+ else:
+ attributes[self.replaceKey(key)] = copy.copy(value)
+ return attributes
+
+ # If key has an underscore, replace with a dash.
+ # Python does not allow dash in object names.
+ def replaceKey(self, key):
+ if (key in self.replaceKeyDictionary):
+ return self.replaceKeyDictionary[key]
+ else:
+ return key
+
+ # Encode the chart attributes as JSON
+ def create(self):
+ attributes = copy.copy(self)
+ # the json parser in open flash chart .swf requires double quotes
+ # this assumes we don't have any single quotes in the line names
+ return str(attributes).replace("'", '"')
+
+class Element(Chart):
+ pass
\ No newline at end of file
Modified: mgmt/trunk/cumin/python/cumin/__init__.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/__init__.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/__init__.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -15,7 +15,7 @@
from model import CuminModel, CallPage
from demo import DemoData
from page import MainPage
-from stat import StatChartPage, StatStackedPage, SlotMapPage
+from stat import StatChartPage, StatStackedPage, SlotMapPage, StatFlashPage,
FlashFullPage
from account.user import UserSession, UserSessionExpireThread
from account import LoginPage, AccountPage
from datetime import timedelta
@@ -64,6 +64,8 @@
self.add_page(StatChartPage(self, "stats.png"))
self.add_page(StatStackedPage(self, "stacked.png"))
self.add_page(SlotMapPage(self, "slots.png"))
+ self.add_page(StatFlashPage(self, "chart.json"))
+ self.add_page(FlashFullPage(self, "flashpage.html"))
unprotected = set()
Modified: mgmt/trunk/cumin/python/cumin/grid/pool.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/pool.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/grid/pool.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -333,14 +333,14 @@
if grid.count() > 0:
return super(PoolStats.GridStats, self).render(session)
- class GridJobChart(StatValueChart):
+ class GridJobChart(StatFlashChart):
def render_title(self, session, object):
return "Grid Jobs"
def get_args(self, session):
return self.parent.get_grid_args(session)
- class GridSubmitChart(StatValueChart):
+ class GridSubmitChart(StatFlashChart):
def render_title(self, session, object):
return "Grid Submits"
Modified: mgmt/trunk/cumin/python/cumin/grid/scheduler.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/scheduler.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/grid/scheduler.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -140,7 +140,7 @@
def render_title(self, session):
return "Statistics"
- class UsersChart(StatValueChart):
+ class UsersChart(StatFlashChart):
def __init__(self, app, name):
super(SchedulerStats.UsersChart, self).__init__(app, name)
@@ -149,7 +149,7 @@
def render_title(self, session, sched):
return "Users"
- class JobsChart(StatValueChart):
+ class JobsChart(StatFlashChart):
def __init__(self, app, name):
super(SchedulerStats.JobsChart, self).__init__ \
(app, name)
Modified: mgmt/trunk/cumin/python/cumin/grid/slot.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/slot.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/grid/slot.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -141,7 +141,7 @@
def render_title(self, session):
return "Statistics"
- class LoadChart(StatValueChart):
+ class LoadChart(StatFlashChart):
def __init__(self, app, name):
super(SlotStats.LoadChart, self).__init__(app, name)
Modified: mgmt/trunk/cumin/python/cumin/grid/submitter.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/submitter.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/grid/submitter.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -83,7 +83,7 @@
def render_title(self, session):
return "Statistics"
- class JobsChart(StatValueChart):
+ class JobsChart(StatFlashChart):
def __init__(self, app, name):
super(SubmitterStats.JobsChart, self).__init__ \
(app, name)
Modified: mgmt/trunk/cumin/python/cumin/inventory/system.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/inventory/system.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/inventory/system.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -136,11 +136,11 @@
self.add_child(StatSet(app, "stats", "general"))
- chart = StatValueChart(app, "freemem")
+ chart = StatFlashChart(app, "freemem")
chart.stats = ("memFree",)
self.add_child(chart)
- chart = StatValueChart(app, "loadavg")
+ chart = StatFlashChart(app, "loadavg")
chart.stats = ("loadAverage1Min",)
self.add_child(chart)
Modified: mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py 2009-07-01 17:26:43 UTC (rev
3471)
+++ mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py 2009-07-01 17:30:11 UTC (rev
3472)
@@ -235,14 +235,14 @@
chart = self.ReceiveRouteDropRateChart(app, "recvroutedrop")
self.add_child(chart)
- chart = StatValueChart(app, "producers")
+ chart = StatFlashChart(app, "producers")
chart.stats = ("producerCount",)
self.add_child(chart)
def render_title(self, session):
return "Statistics"
- class ReceiveRouteDropRateChart(StatValueChart):
+ class ReceiveRouteDropRateChart(StatFlashChart):
def __init__(self, app, name):
super(LinkStats.ReceiveRouteDropRateChart, self).__init__ \
(app, name)
Modified: mgmt/trunk/cumin/python/cumin/messaging/connection.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/connection.py 2009-07-01 17:26:43 UTC (rev
3471)
+++ mgmt/trunk/cumin/python/cumin/messaging/connection.py 2009-07-01 17:30:11 UTC (rev
3472)
@@ -187,7 +187,7 @@
def render_title(self, session):
return "Statistics"
- class SendReceiveRateChart(StatValueChart):
+ class SendReceiveRateChart(StatFlashChart):
def __init__(self, app, name):
super(ConnectionStats.SendReceiveRateChart, self).__init__(app, name)
Modified: mgmt/trunk/cumin/python/cumin/messaging/exchange.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/exchange.py 2009-07-01 17:26:43 UTC (rev
3471)
+++ mgmt/trunk/cumin/python/cumin/messaging/exchange.py 2009-07-01 17:30:11 UTC (rev
3472)
@@ -401,14 +401,14 @@
chart = self.ReceiveRouteDropRateChart(app, "recvroutedrop")
self.add_child(chart)
- chart = StatValueChart(app, "producers")
+ chart = StatFlashChart(app, "producers")
chart.stats = ("producerCount",)
self.add_child(chart)
def render_title(self, session):
return "Statistics"
- class ReceiveRouteDropRateChart(StatValueChart):
+ class ReceiveRouteDropRateChart(StatFlashChart):
def __init__(self, app, name):
super(ExchangeStats.ReceiveRouteDropRateChart, self).__init__ \
(app, name)
Modified: mgmt/trunk/cumin/python/cumin/messaging/queue.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/queue.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/messaging/queue.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -678,14 +678,14 @@
chart = self.DepthChart(app, "depth")
self.add_child(chart)
- chart = StatValueChart(app, "consumers")
+ chart = StatFlashChart(app, "consumers")
chart.stats = ("consumerCount",)
self.add_child(chart)
def render_title(self, session):
return "General"
- class EnqueueDequeueRateChart(StatValueChart):
+ class EnqueueDequeueRateChart(StatFlashChart):
def __init__(self, app, name):
super(QueueStatsGeneral.EnqueueDequeueRateChart,
self).__init__(app, name)
@@ -696,7 +696,7 @@
def render_title(self, session, queue):
return "Messages Enqueued and Dequeued"
- class DepthChart(StatValueChart):
+ class DepthChart(StatFlashChart):
def __init__(self, app, name):
super(QueueStatsGeneral.DepthChart, self).__init__(app, name)
@@ -718,7 +718,7 @@
def render_title(self, session):
return "Durability"
- class EnqueueDequeueRateChart(StatValueChart):
+ class EnqueueDequeueRateChart(StatFlashChart):
def __init__(self, app, name):
super(QueueStatsDurability.EnqueueDequeueRateChart,
self).__init__(app, name)
@@ -768,7 +768,7 @@
def render_title(self, session):
return "Transactions"
- class EnqueueTransactionRateChart(StatValueChart):
+ class EnqueueTransactionRateChart(StatFlashChart):
def __init__(self, app, name):
super(QueueStatsTransactions.EnqueueTransactionRateChart,
self).__init__(app, name)
@@ -780,7 +780,7 @@
def render_title(self, session, queue):
return "Enqueue Transaction Operations per Second"
- class DequeueTransactionRateChart(StatValueChart):
+ class DequeueTransactionRateChart(StatFlashChart):
def __init__(self, app, name):
super(QueueStatsTransactions.DequeueTransactionRateChart,
self).__init__(app, name)
@@ -792,7 +792,7 @@
def render_title(self, session, queue):
return "Dequeue Transaction Operations per Second"
- class EnqueueDequeueRateChart(StatValueChart):
+ class EnqueueDequeueRateChart(StatFlashChart):
def __init__(self, app, name):
super(QueueStatsTransactions.EnqueueDequeueRateChart,
self).__init__(app, name)
Modified: mgmt/trunk/cumin/python/cumin/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/model.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/model.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -402,7 +402,7 @@
def avg_samples(self, object, secs, interval):
conn = self.get_connection()
- when = "(qmf_update_time >= now() - interval '%i seconds')"
% int(secs * 1.5)
+ when = "(qmf_update_time >= now() - interval '%i seconds')"
% int(secs + 12)
where = "%s and %s" % (object.stats.clause, when)
table_name = self.cumin_class.mint_stats_class.q.tableName
Modified: mgmt/trunk/cumin/python/cumin/stat.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/stat.py 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/stat.py 2009-07-01 17:30:11 UTC (rev 3472)
@@ -11,6 +11,9 @@
#import tempfile
import cStringIO
from datetime import timedelta, datetime
+
+from OpenFlashChart import Chart, Element
+
from visualizations import CuminVisualization
strings = StringCatalog(__file__)
@@ -109,23 +112,24 @@
def get_args(self, session):
return self.frame.get_args(session)
- def render_href(self, session, object):
+ def get_href_params(self, session, object):
params = list()
cls = self.app.model.get_class_by_object(object).cumin_name
params.append("class=%s" % cls)
-
params.append("id=%i" % object.id)
for stat in self.stats:
params.append("stat=%s" % stat)
- params.append("duration=%s" % self.duration.get(session))
-
if self.mode:
params.append("mode=%s" % self.mode)
+ return params
+
+ def render_href(self, session, object):
+ params = self.get_href_params(session, object)
return "%s?" % self.get_chart_name(session) +
";".join(params)
def get_chart_name(self, session):
@@ -184,6 +188,33 @@
def render_height(self, session, object):
return 200
+class StatFlashChart(StatValueChart):
+ def get_flash_name(self, session):
+ return "chart.json"
+
+ def render_img_href(self, session, object):
+ return super(StatFlashChart, self).render_href(session, object)
+
+ def render_img_width(self, session, object):
+ return super(StatFlashChart, self).render_width(session, object)
+
+ def render_img_height(self, session, object):
+ return super(StatFlashChart, self).render_height(session, object)
+
+ def render_href(self, session, object):
+ params = self.get_href_params(session, object)
+ return escape_entity("%s?" % self.get_flash_name(session) +
";".join(params))
+
+ def render_fullpage_href(self, session, object):
+ params = self.get_href_params(session, object)
+ return "flashpage.html?" + ";".join(params)
+
+ def render_width(self, session, object):
+ return 360
+
+ def render_height(self, session, object):
+ return 120
+
class ImageCache(object):
def __init__(self):
self.__files = dict() # {name: {"time": time_created, "file":
file object, "cookie": (cookie values)}}
@@ -381,7 +412,7 @@
def get_cache_control(self, session):
return "no-cache"
- def get_args(self, session):
+ def get_object(self, session):
cls = self.class_.get(session).mint_class
id = self.id.get(session)
return (cls.get(id),)
@@ -435,7 +466,8 @@
rets[stat.name] = ret
return str(rets)
- def do_render(self, session, object):
+ def do_render(self, session):
+ object = self.get_object(session)[0]
colors = ((1,0,0), (0,0,1), (0,1,0))
cls = self.class_.get(session)
@@ -542,8 +574,330 @@
chart.write(writer)
return writer.to_string()
+class FlashFullPage(HtmlPage):
+ def __init__(self, app, name):
+ super(FlashFullPage, self).__init__(app, name)
+
+ self.flash_chart = self.GenericChart(app, "chart")
+ self.add_child(self.flash_chart)
+
+ self.updater = Widget(app, "updater")
+ self.updater.update_enabled = True
+ self.add_child(self.updater)
+
+ self.class_ = CuminClassParameter(app, "class")
+ self.add_parameter(self.class_)
+
+ self.id = IntegerParameter(app, "id")
+ self.add_parameter(self.id)
+
+ param = Parameter(app, "param")
+ self.add_parameter(param)
+
+ self.stats = ListParameter(app, "stat", param)
+ self.add_parameter(self.stats)
+
+ self.container_width = IntegerParameter(app, "width")
+ self.container_width.default = 360
+ self.add_parameter(self.container_width)
+
+ self.container_height = IntegerParameter(app, "height")
+ self.container_height.default = 100
+ self.add_parameter(self.container_height)
+
+ self.mode = Parameter(app, "mode")
+ self.add_parameter(self.mode)
+
+ def render_content(self, session):
+ self.flash_chart.stats = self.stats.get(session)
+ self.flash_chart.mode = self.mode.get(session)
+ return self.flash_chart.render(session)
+
+ class GenericChart(StatFlashChart):
+ def get_args(self, session):
+ cls = self.parent.class_.get(session).mint_class
+ id = self.parent.id.get(session)
+ return (cls.get(id),)
+
+ def render_width(self, session, object):
+ return self.parent.container_width.get(session)
+
+ def render_height(self, session, object):
+ return self.parent.container_height.get(session)
+
+ def render_img_href(self, session, object):
+ params = self.get_href_params(session, object)
+ params.append("width=%i" % self.render_width(session, object))
+ params.append("height=%i" % self.render_height(session, object))
+ return "%s?" % self.get_chart_name(session) +
";".join(params)
+
+class StatFlashPage(StatChartPage):
+ def __init__(self, app, name):
+ super(StatFlashPage, self).__init__(app, name)
+
+ self.elapsed = Parameter(app, "elapsed")
+ self.elapsed.default = None
+ self.add_parameter(self.elapsed)
+
+ self.axis_max = IntegerParameter(app, "amax")
+ self.axis_max.default = 0;
+ self.add_parameter(self.axis_max)
+
+ self.vals_max = IntegerParameter(app, "vmax")
+ self.vals_max.default = 0;
+ self.add_parameter(self.vals_max)
+
+ def get_content_type(self, session):
+ return "text/plain"
+
+ def get_duration(self, session):
+ duration = self.duration.get(session)
+ elapsed = self.elapsed.get(session)
+ if elapsed:
+ js_milliseconds = long(elapsed)
+ seconds = js_milliseconds / 1000
+ elapsed = int(seconds + 1)
+ if elapsed <= 24 * 60 * 60:
+ duration = elapsed
+ return duration
+
+ def get_elapsed(self, session):
+ elapsed = self.elapsed.get(session)
+ if elapsed:
+ js_milliseconds = long(elapsed)
+ seconds = js_milliseconds / 1000
+ milliseconds = (js_milliseconds % 1000.0) / 1000.0
+ return seconds + milliseconds
+ return 0
+
+ def get_delta(self, session):
+ """ if we get an elapsed parameter, we want
+ to send only the new values, that is
+ unless the elapsed value is too large
+ in which case we want to get the entire sample """
+
+ elapsed = self.elapsed.get(session)
+ if elapsed:
+ js_milliseconds = long(elapsed)
+ seconds = js_milliseconds / 1000
+ elapsed = int(seconds)
+ if elapsed <= 24 * 60 * 60:
+ return True
+ return False
+
+ def do_render(self, session):
+ object = self.get_object(session)[0]
+ colors = ('#FF0000', '#0000FF', '#00FF00')
+ samples = dict()
+ values = dict()
+ cls = self.class_.get(session)
+ stats = [getattr(cls, x) for x in self.stats.get(session)]
+
+ width = self.container_width.get(session)
+ height = self.container_height.get(session)
+ mode = self.mode.get(session)
+ duration = self.get_duration(session)
+ time_period = self.duration.get(session)
+ delta = self.get_delta(session)
+ interval = self.get_interval(session, duration, width)
+ #interval = 1
+ method = self.method.get(session)
+
+ def fetch_samples(object, dur, interval, method, mode, delta):
+ if mode == "rate":
+ method = None # don't do averaging
+ if delta: # need more than 1 point for calculating rate
+ dur *= 2;
+ for stat in stats:
+ os = stat.samples(object, dur, interval, method)
+ ns = list()
+ prev = None
+
+ for sample in reversed(os):
+ if prev is not None:
+ rate = calc_rate(sample[1], prev[1],
+ secs(sample[0]), secs(prev[0]))
+
+ ns.insert(0, (sample[0], rate, None))
+
+ prev = sample
+
+ samples[stat] = ns
+ else:
+ for stat in stats:
+ samples[stat] = stat.samples(object, dur, interval, method)
+
+ def get_max_min():
+ # take stddev into account for max and min y values
+ max_value = 0
+ min_value = 0
+ try:
+ deviated_values = [[(nvl(x[1],0) + float(nvl(x[2],0))/2,
+ nvl(x[1],0) - float(nvl(x[2],0))/2)
+ for x in samples[stat]] for stat in stats]
+ for deviated_list in deviated_values:
+ if len(deviated_list):
+ max_list = max(max(deviated_list)) # list of tuples
+ max_value = max(max_list, max_value)
+ min_list = min(min(deviated_list))
+ min_value = min(min_list, min_value)
+
+ max_value = round(max_value * 1.1 + 1)
+
+ if min_value < 0:
+ min_value = round(min_value * 1.1 - 1)
+
+ except:
+ pass
+ return max_value, min_value
+
+ #print "fetching duration=%i time_period=%i" % (duration, time_period)
+ fetch_samples(object, duration, interval, method, mode, delta)
+ max_value, min_value = get_max_min()
+
+ append = False
+ if delta:
+ axis_max = self.axis_max.get(session)
+ vals_max = self.vals_max.get(session)
+ axis_for_vals = round(float(vals_max) * 1.1 + 1)
+ max_of_axiis = max(max_value, axis_for_vals)
+ # the most recent value(s) changed the y-axis range
+ if axis_max != max_of_axiis:
+ #print "change in axis: axis_max=%s vals_max=%s max_new_values=%s
vals_axis=%s desired_axis=%s" % (str(axis_max), str(vals_max), str(max_value),
str(axis_for_vals), str(max_of_axiis))
+ duration = time_period
+ fetch_samples(object, duration, interval, method, mode, False)
+ max_value, min_value = get_max_min()
+ else:
+ append = True
+
+ chart = Chart()
+ chart.elements = list()
+ tnow = time()
+ for stat, color in zip(stats, colors):
+ line = Element()
+ line.type = "scatter_line"
+ #line.dot_size = 3
+ #line.dot_style = {"type": "solid-dot",
+ # "dot-size": 5,
+ # "halo-size": 0}
+ line.colour = color
+ line.width = 1
+ line.text = mode == "rate" and "%s / sec" % stat.title or
stat.title
+
+ vals = [{"x":int(time_period -(tnow - secs(dt))),
+ "y":value,
+ "uid": dt.strftime("%m%d%Y%H%M%S"),
+ "tip": "<br>%s: #val#<br>Time: %s" %
(line.text, dt.strftime("%m/%d/%Y %H:%M:%S"))}
+ for dt, value, dev in reversed(samples[stat])
+ if value is not None]
+ line.values = vals
+ chart.elements.append(line)
+
+ if append:
+ chart.append = self.get_elapsed(session)
+ #print "append: time_period=%i duration=%i elapsed=%f" %
(time_period, duration, float(self.get_elapsed(session)))
+ return chart.create()
+
+ chart.title.text = ""
+ chart.title.style = "{text-align: left; font-weight: bold; font-size:
14px;}"
+ chart.tooltip = {"colour": "#000033",
+ "background": "#FFFFCC",
+ "stroke": 1,
+ "title": "{background-color: #000022; color:
#FFFFFF; font-size: 1em;}",
+ "body": "{font-size: 10px; color:
#000000;}"
+ }
+ x_intervals = 8
+ x_steps = 2
+ if duration == 600:
+ x_intervals = 10
+ steps = int(duration / x_intervals)
+
+ chart.x_axis.colour = "#CCCCCC"
+ chart.x_axis.grid_colour = "#DDDDDD"
+ chart.x_axis.stroke = 1
+ chart.x_axis.steps = steps
+ xlbls = Element()
+ #xlbls.steps = steps
+ xlbls.size = 12
+ xlbls.colour = "#999999"
+ xlbls.align = "auto"
+ xlbls.rotate = 0.0001
+ lbls = self.get_x_labels(duration, x_intervals, x_steps)
+ xlbls.labels = lbls
+ chart.x_axis.labels = xlbls
+
+ y_intervals = 6
+ absy = max_value - min_value
+ y_steps = int(absy / y_intervals)
+
+ if int(max_value) < 3:
+ y_steps = 3
+
+ # .swf won't show grid lines with a y_axis on the left
+ chart.y_axis = {
+ "min": int(min_value),
+ "max": int(max_value),
+ "steps": y_steps,
+ "stroke": 0,
+ "tick-length": 0,
+ "colour": "#CCCCCC",
+ "grid-colour": "#DDDDDD",
+ "labels": {"labels": []}}
+ chart.y_axis_right = {
+ "min": int(min_value),
+ "max": int(max_value),
+ "tick-length": 0,
+ "stroke": 1,
+ "colour": "#BBBBBB",
+ "labels": {"colour": "#AAAAAA",
"labels": self.get_y_labels(absy, y_intervals, 2)}}
+ chart.bg_colour = "#FFFFFF"
+ #print "sending entire sample set with y_axis.max=%i" %
chart.y_axis["max"]
+ return chart.create()
+
+ def get_x_labels(self, duration, intervals, step):
+ x_step = duration / intervals
+ labels = list()
+ for i in range(0, intervals + 1):
+ label = dict()
+ if i % step == 0:
+ value = duration - i * x_step
+ text = fmt_duration_brief(value)
+
+ label["text"] = text
+ else:
+ label["text"] = ""
+ label["x"] = i * x_step
+
+ labels.append(label)
+ return labels
+
+ def get_y_labels(self, absy, intervals, step):
+ y_step = absy / intervals
+ labels = list()
+ for i in range(0, intervals + 1):
+ label = dict()
+
+ if i % step == 0:
+ value = int(round(i * y_step, 0))
+
+ if value >= 10000000:
+ svalue = "%im" % int(round(value / 1000000.0, -1))
+ elif value >= 10000:
+ svalue = "%ik" % int(round(value / 1000.0, -1))
+ else:
+ svalue = str(value)
+
+ label["text"] = svalue
+ else:
+ label["text"] = ""
+ label["y"] = int(i * y_step)
+
+ labels.append(label)
+ return labels
+
class StatStackedPage(StatChartPage):
- def do_render(self, session, object):
+ def do_render(self, session):
+ object = self.get_object(session)[0]
cls = self.class_.get(session)
stats = [getattr(cls, x) for x in self.stats.get(session)]
@@ -623,7 +977,7 @@
chart.plot_legend(reversed(titles), reversed(colors[:len(titles)]))
- self.cache_it(session, chart, recent)
+ self.cache_it(session, chart, {'recent': recent, 'samples': None,
"title_xy": None})
writer = Writer()
chart.write(writer)
Modified: mgmt/trunk/cumin/python/cumin/stat.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/stat.strings 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/python/cumin/stat.strings 2009-07-01 17:30:11 UTC (rev 3472)
@@ -110,7 +110,7 @@
var container = $(id);
var loading = container.getElement(".loading");
var img = container.getElement("img");
- if (loading.loading) {
+ if (loading && (loading.loading)) {
loading.style.visibility = "hidden";
loading.style.display = "block";
var left = (img.getSize().x - loading.getSize().x) / 2;
@@ -125,19 +125,25 @@
}
}
function hideLoading(id) {
- $(id).getElement(".loading").setStyles({
+ var loading = $(id).getElement(".loading");
+ if (loading)
+ loading.setStyles({
display: 'none',
visibility: 'hidden'
});
}
-function changeDuration(state, a, id) {
+function changeDuration(state, a, id, attempt) {
li = a.parentNode;
ul = li.parentNode;
as = ul.getElementsByTagName('a');
for (var i=0; i < as.length; i++) {
as[i].className = (as[i] == a) ? "selected" : "";
}
+ var hash = wooly.session.hash();
+ hash[id] = state;
+ wooly.session.setHash(hash);
+
var oImg = document.images[id];
if (oImg) {
var src = oImg.src;
@@ -150,39 +156,44 @@
$(oImg).onerror = $(oImg).onload = function () {this.className = ""};
oImg.className = "Loading";
- var hash = wooly.session.hash();
- hash[id] = state;
- wooly.session.setHash(hash);
+ var loading = $(id).getElement(".loading");
+ if (loading) {
+ loading.loading = true;
+ setTimeout("showLoading('"+id+"')", 1000);
+ setTimeout("hideLoading('"+id+"')", 1000 * 60);
+ }
+
+ wooly.cancelIntervalUpdate();
+ wooly.resumeIntervalUpdate();
+ cumin.expireIntervalUpdate();
+ return false;
+ } else {
+ var chart = cumin.getFlashChart(id);
+ if (chart == null) {
+ if (typeof attempt == "undefined")
+ attempt = 1;
+ if (attempt < 2)
+ setTimeout(function () {changeDuration(state, a, id, attempt+1);},
1000);
+ return false;
+ }
+ chart.geturl("got_chart_url", id, state);
}
- var loading = $(id).getElement(".loading");
- loading.loading = true;
- setTimeout("showLoading('"+id+"')", 1000);
- setTimeout("hideLoading('"+id+"')", 1000 * 60);
-
- wooly.cancelIntervalUpdate();
- wooly.resumeIntervalUpdate();
- cumin.expireIntervalUpdate();
return false;
}
+// called by open-flash-chart.swf in response to chart.geturl()
+function got_chart_url(id, url, state) {
+ var chart = cumin.getFlashChart(id);
+ if (chart == null)
+ return false;
-window.addEvent('domready',function () {
- var hash = wooly.session.hash();
- for (var key in hash) {
- if (!(hash[key] instanceof Function)) {
- var oChart = $(key);
- if (oChart) {
- var links = oChart.getElements('a');
- var state = hash[key];
+ var branch = wooly.session.branch(url);
+ var now = new Date().getTime();
+ branch['elapsed'] = now; // force an entire update
+ branch['duration'] = state;
+ url = branch.marshal();
- $each(links, function (item) {
- if (item.get('state') == state) {
- changeDuration(state, item, key);
- }
- });
- }
- }
- }
-});
+ chart.reload(url, true);
+}
function gotChartPoints(text, oImg) {
var mImg = $(oImg);
@@ -224,3 +235,64 @@
<span class="ph" statname="{stat_name}"
statmode="{mode}">{stat_value}</span>
</li>
+[StatFlashChart.html]
+<div>
+ <div class="StatValueChart{fullpageable}" id="{id}">
+ <h2>{title}</h2>
+ <div class="duration">{duration}</div>
+ <div id="{id}_chart">
+ <img id="{id}" src="{img_href}"
height="{img_height}" width="{img_width}" alt="stats" />
+ <div class="loading"
style="display:none;"><span>Loading...</span></div>
+ </div>
+ </div>
+</div>
+<script type="text/javascript">
+//<![CDATA[
+ var flashversion = swfobject.getFlashPlayerVersion();
+ if (flashversion.major < 9) {
+ wooly.addPageUpdateListener(function () {if
(document.images["{id}"].className == "Loading") return;
cumin.updateChart("{id}");});
+ cumin.setupChart('{id}', '{fullpage_href}');
+ } else {
+ swfobject.embedSWF("resource?name=open-flash-chart.swf",
"{id}_chart", "{width}", "{height}", "9.0.0",
"",
+ {"data-file":"{href}"});
+
+ wooly.addPageUpdateListener(function () { cumin.updateFlashChart('{id}');
});
+ window.addEvent('domready',function () {
+ cumin.setFullpageHandler('{id}', '{fullpage_href}');
+ });
+ }
+//]]>
+</script>
+
+[GenericChart.css]
+div.FullpageChart {
+ margin: 1.5em;
+}
+
+[GenericChart.html]
+ <div class="FullpageChart" id="{id}">
+ <h2>{title}</h2>
+ <div id="{id}_chart">
+ <img id="{id}" src="{img_href}"
height="{height}" width="{width}" alt="stats" />
+ <div class="loading"
style="display:none;"><span>Loading...</span></div>
+ </div>
+ </div>
+<script type="text/javascript">
+//<![CDATA[
+ var flashversion = swfobject.getFlashPlayerVersion();
+ if (flashversion.major < 9) {
+ wooly.addPageUpdateListener(function () {if
(document.images["{id}"].className == "Loading") return;
cumin.updateChart("{id}");});
+ cumin.setupChart('{id}', '{fullpage_href}');
+ } else {
+ swfobject.embedSWF("resource?name=open-flash-chart.swf",
"{id}_chart", "96%", "{height}", "9.0.0",
"",
+ {"data-file":"{href}"});
+ wooly.addPageUpdateListener(function () { cumin.updateFlashChart('{id}');
});
+ window.addEvent('resize', function () {
+ var chart = cumin.getFlashChart('{id}');
+ if (chart) {
+ chart.height = window.getSize().y - 170;
+ }
+ });
+ }
+//]]>
+</script>
Modified: mgmt/trunk/cumin/resources/app.js
===================================================================
--- mgmt/trunk/cumin/resources/app.js 2009-07-01 17:26:43 UTC (rev 3471)
+++ mgmt/trunk/cumin/resources/app.js 2009-07-01 17:30:11 UTC (rev 3472)
@@ -87,10 +87,16 @@
}
}
- this.setupChart = function (id, width) {
+ this.setFullpageHandler = function (id, fullpage_url) {
var chart = $(id);
- chart.onfullpage = function (width, height) { cumin.chartNotify(true, width,
height, id); };
- chart.onrestore = function () { cumin.chartNotify(false, width, 100, id); };
+ if (chart) {
+ chart.preFullPage = function () { cumin.fullpageChart(fullpage_url);
return false;};
+ }
+ }
+
+ this.setupChart = function (id, fullpage_url) {
+ cumin.setFullpageHandler(id, fullpage_url);
+ var chart = $(id);
var oImg = document.images[id];
var mImg = $(oImg);
mImg.addEvent('load', function () {
@@ -116,17 +122,18 @@
event.stop();
return false;
}
- var oHighlight =
document.body.getElement(".imgHighlight_red");
+ var mBody = $(document.body);
+ var oHighlight = mBody.getElement(".imgHighlight_red");
if (oHighlight) oHighlight.style.display = "none";
- oHighlight = document.body.getElement(".imgHighlight_green");
+ oHighlight = mBody.getElement(".imgHighlight_green");
if (oHighlight) oHighlight.style.display = "none";
- oHighlight = document.body.getElement(".imgHighlight_blue");
+ oHighlight = mBody.getElement(".imgHighlight_blue");
if (oHighlight) oHighlight.style.display = "none";
- var oValue = document.body.getElement(".imgValues_red");
+ var oValue = mBody.getElement(".imgValues_red");
if (oValue) oValue.style.display = "none";
- oValue = document.body.getElement(".imgValues_green");
+ oValue = mBody.getElement(".imgValues_green");
if (oValue) oValue.style.display = "none";
- oValue = document.body.getElement(".imgValues_blue");
+ oValue = mBody.getElement(".imgValues_blue");
if (oValue) oValue.style.display = "none";
mImg.store("over", false);
});
@@ -151,13 +158,14 @@
return closest;
}
highlight = function (stat, point, xy) {
- var oHighlight =
document.body.getElement(".imgHighlight_"+stat);
- var oValues = document.body.getElement(".imgValues_"+stat);
+ var mBody = $(document.body);
+ var oHighlight = mBody.getElement(".imgHighlight_"+stat);
+ var oValues = mBody.getElement(".imgValues_"+stat);
if (!oHighlight) {
oHighlight = new Element('div', { 'class':
'imgHighlight_'+stat });
oValues = new Element('div', { 'class':
'imgValues_'+stat });
- document.body.appendChild(oHighlight);
- document.body.appendChild(oValues);
+ mBody.appendChild(oHighlight);
+ mBody.appendChild(oValues);
}
var pos = mImg.getPosition();
oHighlight.style.display = "block";
@@ -184,13 +192,16 @@
}
}
+ // called when a chart widget is maximized or restored
this.chartNotify = function (full, width, height, id) {
+ var bWidth = Math.max(width - 100, 360);
+ var bHeight = Math.max(height - 100, 100);
var oImg = document.images[id];
if (oImg) {
var src = oImg.src;
var branch = wooly.session.branch(src);
- branch.width = Math.max(width - 100, 360);
- branch.height = Math.max(height - 100, 100);
+ branch.width = bWidth;
+ branch.height = bHeight;
src = branch.marshal();
src = cumin.refreshTime(src);
@@ -206,6 +217,7 @@
setTimeout("showLoading('"+id+"')", 1000);
setTimeout("hideLoading('"+id+"')", 1000 *
60);
}
+ return true;
}
this.makeFullPageable = function (element) {
@@ -231,9 +243,26 @@
}
});
}
+
+ this.fullpageChart = function (fullpage_url) {
+ var params = 'width='+screen.width;
+ params += ', height='+screen.height;
+ params += ', top=0, left=0'
+ params += ', fullscreen=yes';
+ var branch = wooly.session.branch(fullpage_url);
+ branch['width'] = screen.width - 40;
+ branch['height'] = screen.height - 300;
+
+ var newwin = window.open(branch.marshal(), 'cuminflash', params);
+ if (window.focus) {
+ newwin.focus();
+ }
+ }
+
}
}())
+
/* surround all elements that have class 'fullpageable' with
supporting divs and add event behaviors */
window.addEvent('domready', function () {
@@ -251,6 +280,12 @@
function fullpage(oIcon) {
var oFullPage = oIcon.getParent();
+ // short circuit to open in a new window
+ if (oFullPage.preFullPage) {
+ if (!oFullPage.preFullPage())
+ return false;
+ }
+
var oBack = $("fullpageBack");
if (!oBack) {
oBack = new Element('div', {'class': 'fullpageBack',
'id': 'fullpageBack', 'events': {'click': fullpage}});
@@ -271,19 +306,25 @@
window.scrollTo(oFullPage.origScroll.x, oFullPage.origScroll.y);
} else {
// make it full page
+
oBack.style.display = "block";
oBack.style.height = Math.max($(document.body).getCoordinates().height,
window.getScrollSize().y) + "px";
+ document.body.style.height = "100%";
- document.body.style.height = "100%";
document.body.appendChild(oFullPage);
+
oFullPage.removeClass('fullpageable');
oFullPage.addClass('fullpaged');
+
oIcon.setProperty("title", "Restore");
setFullpageParam(true, oFullPage);
+
var onfullpage = oFullPage.onfullpage;
var coords = $(oFullPage).getCoordinates();
var height = Math.min(Math.max(window.getSize().y - 100, 200), 600);
- if (onfullpage) onfullpage(coords.width, height);
+ if (onfullpage)
+ onfullpage(coords.width, height);
+
var curScroll = window.getScroll();
oFullPage.origScroll = curScroll;
window.scrollTo(0,0);
@@ -305,4 +346,70 @@
wooly.restartIntervalUpdate(branch.marshal());
cumin.expireIntervalUpdate();
}
-}
\ No newline at end of file
+}
+
+cumin.getFlashChart = function (id) {
+ var chart = $(id+"_chart");
+ if (chart) {
+ if (typeof chart.reload != "undefined")
+ return chart;
+ var pchart = $(id);
+ var chart2 = pchart.getChildren(id+"_chart");
+ if (navigator.appName.indexOf("Microsoft") != -1) {
+ return window[id+"_chart"];
+ } else {
+ return document[id+"_chart"];
+ }
+ }
+ return null;
+}
+
+cumin.updateFlashChart = function (id) {
+ var chart = cumin.getFlashChart(id);
+ if (chart == null)
+ return false;
+ chart.get_ymax(id)
+}
+
+/* called from .swf in response to chart.get_ymax() */
+function got_ymax(id, href, axis_max, vals_max) {
+ var chart = cumin.getFlashChart(id);
+ if (chart == null)
+ return false;
+
+ var branch = wooly.session.branch(href);
+
+ var now = new Date().getTime();
+ var then = 0;
+ if (typeof chart.last_time != "undefined") {
+ then = chart.last_time;
+ }
+ chart.last_time = now;
+ var elapsed = now - then;
+ branch['elapsed'] = elapsed; // milliseconds since last update
+ branch['amax'] = axis_max;
+ branch['vmax'] = vals_max;
+
+ href = branch.marshal();
+ chart.reload(href, false);
+}
+
+window.addEvent('domready',function () {
+ var hash = wooly.session.hash();
+ for (var key in hash) {
+ if (!(hash[key] instanceof Function)) {
+ var oChart = $(key);
+ if (oChart) {
+ var links = oChart.getElements('a');
+ var state = hash[key];
+
+ $each(links, function (item) {
+ if (item.get('state') == state) {
+ setTimeout(function() {changeDuration(state, item, key);}, 0);
+ }
+ });
+ }
+ }
+ }
+});
+