Steve Linabery
2008-Nov-20 04:12 UTC
[Ovirt-devel] [PATCH server] Add time range and data type selection to flexchart.
Also -change color scheme to match tallen prototype pallette -change SingleBar elements to dynamically resized Canvas objects instead of HBox objects -add new route to accept second-resolution timestamps for time range selection --- src/app/controllers/graph_controller.rb | 59 ++++- src/app/util/stats/StatsTypes.rb | 8 +- src/app/views/graph/history_graphs.rhtml | 2 +- src/config/routes.rb | 1 + src/flexchart/flexchart.mxml | 12 +- src/flexchart/org/ovirt/ApplicationBus.as | 39 ++++ src/flexchart/org/ovirt/Constants.as | 2 +- src/flexchart/org/ovirt/DataSource.as | 7 +- src/flexchart/org/ovirt/charts/BarChart.as | 287 ++++++++++++++++++------ src/flexchart/org/ovirt/charts/Chart.as | 38 +++- src/flexchart/org/ovirt/data/DataPoint.as | 7 +- src/flexchart/org/ovirt/data/DataSeries.as | 12 +- src/flexchart/org/ovirt/elements/SingleBar.as | 51 ++++- src/flexchart/org/ovirt/elements/XAxisLabel.as | 66 ++++++ src/flexchart/org/ovirt/elements/YAxisLabel.as | 17 +-- 15 files changed, 476 insertions(+), 132 deletions(-) create mode 100644 src/flexchart/org/ovirt/ApplicationBus.as create mode 100644 src/flexchart/org/ovirt/elements/XAxisLabel.as diff --git a/src/app/controllers/graph_controller.rb b/src/app/controllers/graph_controller.rb index 0105ea6..d636544 100644 --- a/src/app/controllers/graph_controller.rb +++ b/src/app/controllers/graph_controller.rb @@ -4,20 +4,53 @@ class GraphController < ApplicationController layout nil def flexchart_data - - #FIXME: use the stats package aggregation (when it's available) - #instead of the old method - graph_obj = history_graph_data_object - - #FIXME: for this release, the flexchart shows only peak values, - # and only shows a default of the last 40 data points in rrd. - graph_data = { :labels => graph_obj[:timepoints].last(40), - :values => graph_obj[:dataset][2][:values].last(40) } - my_data = graph_data[:labels].zip(graph_data[:values]) - graph = { :vectors => my_data, - :max_value => graph_obj[:total_peak], - :description => params[:target] - } + @id = params[:id] + target = params[:target] + startTime = params[:startTime].to_i + endTime = params[:endTime].to_i + duration = endTime - startTime + + #the maximum number of data points we want in any chart + maxPoints = 100 + resolution + case + when duration / RRDResolution::Minimum < maxPoints + RRDResolution::Minimum + when duration / RRDResolution::Short < maxPoints + RRDResolution::Short + when duration / RRDResolution::Medium < maxPoints + RRDResolution::Medium + when duration / RRDResolution::Long < maxPoints + RRDResolution::Long + else + RRDResolution::Maximum + end + devclass = DEV_KEY_CLASSES[target] + counter = DEV_KEY_COUNTERS[target] + + #FIXME: until stats aggregation is pushed, we just get stats data for + # the first host in the pool. If no host in pool, the chart will be + # empty. + pool = Pool.find(@id) + hosts = pool.hosts + host = pool.hosts[0] + requestList = [ ] + requestList.push StatsRequest.new(host.hostname, devclass, 0, counter, startTime, duration, resolution, DataFunction::Peak) + statsList = getStatsData?(requestList) + + #The aggregated (summed) stats will come back as a single stats list + stat = statsList[0] + vectors = [ ] + data = stat.get_data? + data.each{ |datum| + val = datum.get_value? + val = 0 if val.nan? + vectors.push [datum.get_timestamp?.to_i, val] + } + graph = { :vectors => vectors, + :max_value => stat.get_max_value?, + :description => target + } render :json => graph end diff --git a/src/app/util/stats/StatsTypes.rb b/src/app/util/stats/StatsTypes.rb index 41c2977..4896bb3 100644 --- a/src/app/util/stats/StatsTypes.rb +++ b/src/app/util/stats/StatsTypes.rb @@ -324,10 +324,12 @@ class RRDResolution end # Set up the resolutions for our rrd + RRDResolution.add_item :Minimum, 10 # Ten secs + RRDResolution.add_item :Short, 70 # Seventy secs + RRDResolution.add_item :Medium, 500 # 500 secs ( 8minute, 20 sec) + RRDResolution.add_item :Long, 2230 + RRDResolution.add_item :Maximum, 26350 RRDResolution.add_item :Default, 10 # Ten secs - RRDResolution.add_item :Short, 500 # 500 secs ( 8minute, 20 sec) - RRDResolution.add_item :Medium, 2230 - RRDResolution.add_item :Long, 26350 end diff --git a/src/app/views/graph/history_graphs.rhtml b/src/app/views/graph/history_graphs.rhtml index bebe5d9..97ed67f 100644 --- a/src/app/views/graph/history_graphs.rhtml +++ b/src/app/views/graph/history_graphs.rhtml @@ -10,7 +10,7 @@ $('#flex_history_chart').flash( height: 300, wmode: 'transparent', menu: false, - flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/1" } + flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/<%= Time.now.to_i - 60 * 60 * 24 %>/<%= Time.now.to_i %>" } }, { version: 9 } ); diff --git a/src/config/routes.rb b/src/config/routes.rb index 8d538cb..0dbe0d6 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -41,6 +41,7 @@ ActionController::Routing::Routes.draw do |map| map.connect ':controller/service.wsdl', :action => 'wsdl' # Install the default route as the lowest priority. + map.connect 'graph/flexchart_data/:id/:target/:startTime/:endTime', :controller => 'graph', :action => 'flexchart_data' map.connect 'graph/flexchart_data/:id/:target/:days', :controller => 'graph', :action => 'flexchart_data' map.connect ':controller/:action/:id.:format' map.connect ':controller/:action/:id' diff --git a/src/flexchart/flexchart.mxml b/src/flexchart/flexchart.mxml index 35fa9a6..c4a089f 100644 --- a/src/flexchart/flexchart.mxml +++ b/src/flexchart/flexchart.mxml @@ -6,6 +6,7 @@ import mx.containers.VBox; import mx.effects.Resize; import org.ovirt.Constants; + import org.ovirt.ApplicationBus; import org.ovirt.charts.Chart; import org.ovirt.charts.BarChart; @@ -21,6 +22,7 @@ private var contractResources:Resize = new Resize(); private function myInit():void { + mainChart.height = Constants.height; mainChart.width = Constants.width; hostsChart.width = Constants.width; @@ -42,6 +44,9 @@ contractResources.heightTo = 0; contractResources.target = hostsChart; contractResources.duration = 500; + + ApplicationBus.instance().barClickAction = zoomIntoSeries; + } private function zoomOutSeries(e:Event):void { @@ -61,7 +66,8 @@ </mx:Script> - <mx:VBox id="mainChart" click="zoomIntoSeries(event)" /> - <mx:VBox id="hostsChart" visible="false" opaqueBackground="0x00ff00" click="zoomOutSeries(event)"/> - + <mx:VBox id="mainChart" /> + <mx:VBox id="hostsChart" visible="false" opaqueBackground="0x00ff00" click="zoomOutSeries(event)"> + <mx:Text text="This Space Reserved for Drill-down Hosts Chart" /> + </mx:VBox> </mx:Application> diff --git a/src/flexchart/org/ovirt/ApplicationBus.as b/src/flexchart/org/ovirt/ApplicationBus.as new file mode 100644 index 0000000..7d6d431 --- /dev/null +++ b/src/flexchart/org/ovirt/ApplicationBus.as @@ -0,0 +1,39 @@ +/* + Copyright (C) 2008 Red Hat, Inc. + Written by Steve Linabery <slinabery at redhat.com> + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. A copy of the GNU General Public License is + also available at http://www.gnu.org/copyleft/gpl.html. +*/ + +//A way to expose some functions that are defined in flexchart.mxml to +//our ActionScript classes without needing to access Application directly + +package org.ovirt { + public class ApplicationBus { + + private static var _instance:ApplicationBus; + + public static function instance():ApplicationBus { + if (_instance == null) { + _instance = new ApplicationBus(); + } + return _instance; + } + + public var barClickAction:Function; + + } +} diff --git a/src/flexchart/org/ovirt/Constants.as b/src/flexchart/org/ovirt/Constants.as index 996a31e..14f6465 100644 --- a/src/flexchart/org/ovirt/Constants.as +++ b/src/flexchart/org/ovirt/Constants.as @@ -23,6 +23,6 @@ package org.ovirt { public static var width:int = 722; public static var height:int = 297; public static var barSpacing:int = 2; - public static var labelHeight:int = 40; + public static var labelHeight:int = 20; } } diff --git a/src/flexchart/org/ovirt/DataSource.as b/src/flexchart/org/ovirt/DataSource.as index 44e482d..4438900 100644 --- a/src/flexchart/org/ovirt/DataSource.as +++ b/src/flexchart/org/ovirt/DataSource.as @@ -20,13 +20,13 @@ package org.ovirt { - import flash.net.URLLoader; - import flash.net.URLRequest; import com.adobe.serialization.json.JSON; import flash.events.Event; import flash.events.IOErrorEvent; - import org.ovirt.data.DataSeries; + import flash.net.URLLoader; + import flash.net.URLRequest; import org.ovirt.charts.Chart; + import org.ovirt.data.DataSeries; public class DataSource { @@ -52,6 +52,7 @@ package org.ovirt { } private function ioError( e:IOErrorEvent ):void { + trace("ioError"); //FIXME: //do something useful with this error } diff --git a/src/flexchart/org/ovirt/charts/BarChart.as b/src/flexchart/org/ovirt/charts/BarChart.as index 83cf0bb..88a75ca 100644 --- a/src/flexchart/org/ovirt/charts/BarChart.as +++ b/src/flexchart/org/ovirt/charts/BarChart.as @@ -19,40 +19,127 @@ */ package org.ovirt.charts { - + import flash.events.Event; + import flash.events.MouseEvent; + import mx.collections.ArrayCollection; import mx.containers.Box; import mx.containers.HBox; import mx.containers.VBox; - import mx.controls.Text; import mx.containers.Canvas; + import mx.controls.TextInput; + import mx.controls.DateField; + import mx.controls.Button; + import mx.controls.PopUpMenuButton; + import mx.controls.Text; + import mx.events.MenuEvent; + import mx.formatters.DateFormatter; import org.ovirt.data.*; import org.ovirt.elements.*; import org.ovirt.Constants; + import org.ovirt.ApplicationBus; public class BarChart extends Chart { - private var chartArea:HBox; - private var labelArea:Canvas; + private var chartArea:Canvas; + private var XAxisLabelArea:Canvas; + private var startDateField:DateField; + private var endDateField:DateField; + private var startTimeField:TextInput; + private var endTimeField:TextInput; + private var button:Button; + private var menu:PopUpMenuButton; + private var dateBar:Box; + private var datePattern:RegExp; + public function BarChart(container:Box, datasourceUrl:String) { super(container,datasourceUrl); - chartArea = new HBox(); - chartArea.setStyle("horizontalGap",Constants.barSpacing); - chartArea.setStyle("verticalAlign","bottom"); + container.setStyle("verticalGap","2"); + datePattern = /^(\d+):(\d+)$/; + } + + + private function timeRangeAdjusted(event:Event):void { + var t1:Number = startDateField.selectedDate.getTime() + + (parseHour(startTimeField.text) * 3600 * 1000) + + (parseMinute(startTimeField.text) * 60 * 1000); + setStartTime(Math.floor(t1 / 1000)); + var t2:Number = endDateField.selectedDate.getTime() + + (parseHour(endTimeField.text) * 3600 * 1000) + + (parseMinute(endTimeField.text) * 60 * 1000); + setEndTime(Math.floor(t2 / 1000)); + load(); + } + + private function typeSelected(event:MenuEvent):void { + target = event.label; + load(); + } + + + private function pad(input:int):String { + if (input < 10) { + return "0" + input; + } else { + return "" + input; + } + } + + private function parseHour(input:String):int { + var answer:int = 0; + try { + var obj:Object = datePattern.exec(input); + if (obj != null) { + answer = int(obj[1].toString()); + } + } catch (e:Error) {} + return answer; + } + + private function parseMinute(input:String):int { + var answer:int = 0; + try { + var obj:Object = datePattern.exec(input); + if (obj != null) { + answer = int(obj[2].toString()); + } + } catch (e:Error) {} + return answer; + } + + override public function addData(dataSeries:DataSeries):void { + container.removeAllChildren(); + + var dateFormat:DateFormatter = new DateFormatter(); + + //since we're reusing objects, we need to get rid of stale + //EventListener references + if (chartArea != null) { + var kids:Array = chartArea.getChildren(); + var i:int; + for (i = 0; i < kids.length; i++) { + (kids[i] as SingleBar).destroy(); + } + } + + chartArea = new Canvas(); chartArea.percentHeight = 80; chartArea.percentWidth = 100; + chartArea.setStyle("backgroundColor","0xbbccdd"); this.container.addChild(chartArea); - labelArea = new Canvas(); - labelArea.height = Constants.labelHeight; - labelArea.minHeight = Constants.labelHeight; - labelArea.percentWidth = 100; - this.container.addChild(labelArea); - } + XAxisLabelArea = new Canvas(); + XAxisLabelArea.height = Constants.labelHeight; + XAxisLabelArea.minHeight = Constants.labelHeight; + XAxisLabelArea.percentWidth = 100; + this.container.addChild(XAxisLabelArea); - override public function addData(dataSeries:DataSeries):void { try { + + dateBar = new HBox(); + dateBar.setVisible(true); + this.container.addChild(dateBar); var dataPoints:Array = dataSeries.getDataPoints(); var maxValue:Number = dataSeries.getMaxValue(); var scale:Number = maxValue; @@ -65,74 +152,72 @@ package org.ovirt.charts { throw new Error("No data points in range"); } - //have to iterate through datapoint.timestamp strings, - //create a TextLiberation object with them, and add them to - //a parent container before we can tell how wide they are in pixels. - var labelWidth:Number = 0; - for (var i:int = 0; i < size; i++) { - var dataPoint:DataPoint = dataPoints[i] as DataPoint; - var textTemp:TextLiberation - new TextLiberation(dataPoint.getTimestamp()); - textTemp.setVisible(false); - chartArea.addChild(textTemp); - var tempLabelWidth:Number = textTemp.getTextWidth(); - if (! isNaN(tempLabelWidth)) { - labelWidth = Math.max(labelWidth, tempLabelWidth); - } - } - //now we have to remove all the children we just added, since we don't - //really want them to be part of the chart. - chartArea.removeAllChildren(); - - //we always want an odd number of y-axis labels, and we'll - //determine this by using the labelWidth we just determined - var labelCount:int = Math.floor(Constants.width / labelWidth); - if (labelCount > 3 && labelCount % 2 == 1) { - labelCount--; - } - //the distance between left edges of adjacent bars - var gridWidth:Number = Constants.width / size; + var gridWidth:Number = Math.floor(Constants.width / size); //the width of each SingleBar (does not including padding between bars) var barWidth:Number = gridWidth - Constants.barSpacing; - //use this to center y-axis labels on the bars - var labelOffset:Number = barWidth / 2; + //due to the discrete number of pixels, there may be space at the + //right side of the graph that needs to be made up by padding + //bars here and there + var shortfall:Number = Constants.width - (gridWidth * size); + var makeup:Number = Math.round(size / shortfall); + var madeup:Number = 0; + + //variable to hold the numbered day of the month of the last + //XAxisLabel added to the label area + var lastDate:Number; - //distance between first and last label - var labelSpace:Number = Constants.width - gridWidth; - var labelSpacing:Number = labelSpace / labelCount; + //variable to hold the x-coordinate of the next bar to be added to + //the chart + var currentBarPosition:int = 0; //add the bars & labels to the chart var labelCounter:int = 0; for (i = 0; i < size; i++) { - dataPoint = dataPoints[i] as DataPoint; + + var dataPoint:DataPoint = dataPoints[i] as DataPoint; + if (i == 0) { + lastDate = dataPoint.getTimestamp().date; + } + + //show long date format for first & last XAxisLabels, + //as well as whenever the date changes + if (i == 0 || i == size - 1 + || dataPoint.getTimestamp().date != lastDate) { + dateFormat.formatString = "DD-MMM-YYYY JJ:NN"; + } else { + dateFormat.formatString = "JJ:NN"; + } + var value:Number = dataPoint.getValue(); - var bar:SingleBar = new SingleBar(dataPoint); - bar.percentHeight = ((value / scale) * 80); - bar.width = barWidth; - bar.setVisible(true); + var bar:SingleBar = new SingleBar(dataPoint,scale); chartArea.addChild(bar); - var currentLabelPosition:int = labelCounter * labelSpacing + - labelOffset; - - if (currentLabelPosition >= i * gridWidth && - currentLabelPosition < (i + 1) * gridWidth) { - var label:YAxisLabel = new YAxisLabel(dataPoint.getTimestamp()); - label.setVisible(false); - label.y = ((labelCounter + 1) % 2) * 13 + 4; - labelArea.addChild(label); - //make sure the label is fully within the chart width - label.x = Math.max(0, - Math.min((i) * gridWidth - - (label.labelText.getTextWidth() / 2) + - labelOffset, - Constants.width - - label.labelText.getTextWidth() - 6) - ); + bar.width = barWidth; + bar.addEventListener(MouseEvent.CLICK, + ApplicationBus.instance().barClickAction); + bar.x = currentBarPosition; + if (makeup > 0 && i % makeup == 0 && madeup < shortfall) { + bar.width = bar.width + 1; + madeup++; + } + + //add XAxisLabels at the endpoints of the time range, + //as well as the center if there are more than 6 points + //and two more if there are more than 14 points + if ((size > 6 && i == Math.floor(size / 2)) + || (size > 14 + && (i == Math.floor(size / 4) + || i == Math.floor(size * 3 / 4))) + || i == 0 + || i == size - 1) { + var label:XAxisLabel + new XAxisLabel(dateFormat.format(dataPoint.getTimestamp())); + label.setCenter(currentBarPosition + bar.width / 2); label.setVisible(true); - labelCounter++; + label.y = 6; + XAxisLabelArea.addChild(label); //add a 'tick' in the center of the bar to which this label //corresponds @@ -140,13 +225,73 @@ package org.ovirt.charts { ind.opaqueBackground = 0x000000; ind.width=1; ind.height=3; - ind.x = (i) * gridWidth + labelOffset; + ind.x = label.getCenter(); ind.y = 0; ind.setVisible(true); ind.setStyle("backgroundColor","0x000000"); - labelArea.addChild(ind); + XAxisLabelArea.addChild(ind); + lastDate = dataPoint.getTimestamp().date; } + currentBarPosition += (bar.width + Constants.barSpacing); + } + + //fill in the time range selection bar + var t:Date; + var f1:Text = new Text(); + f1.text = "View data between"; + dateBar.addChild(f1); + t = new Date(dataPoints[0].getTimestamp().getTime()); + startDateField = new DateField(); + startDateField.selectedDate = t; + startDateField.editable = true; + startTimeField = new TextInput(); + startTimeField.minWidth = 50; + startTimeField.maxWidth = 50; + startTimeField.text = pad(t.hours) + ":" + pad(t.minutes); + dateBar.addChild(startTimeField); + dateBar.addChild(startDateField); + var f2:Text = new Text(); + f2.text = "and"; + dateBar.addChild(f2); + + t = new Date(dataPoints[size - 1].getTimestamp().getTime()); + endDateField = new DateField(); + endDateField.selectedDate = t; + endDateField.editable = true; + endTimeField = new TextInput(); + endTimeField.minWidth = 50; + endTimeField.maxWidth = 50; + endTimeField.text = pad(t.hours) + ":" + pad(t.minutes); + dateBar.addChild(endTimeField); + dateBar.addChild(endDateField); + + button = new Button(); + button.label = "go"; + button.addEventListener(MouseEvent.CLICK,timeRangeAdjusted); + dateBar.addChild(button); + + //FIXME: these should be fetched from the graph controller so + //that different types can be added (or restricted) dynamically + var menuItems:ArrayCollection + new ArrayCollection( [{label: "memory"}, + {label: "cpu"}, + {label: "load"}, + {label: "netin"}, + {label: "netout"}, + {label: "disk"} + ]); + + + if (menu != null) { + menu.removeEventListener(MenuEvent.ITEM_CLICK,typeSelected); } + + menu = new PopUpMenuButton(); + menu.label = "Select Data Type"; + menu.dataProvider = menuItems; + menu.addEventListener(MenuEvent.ITEM_CLICK,typeSelected); + dateBar.addChild(menu); + } catch (e:Error) { var err:Text = new Text(); err.text = e.message; diff --git a/src/flexchart/org/ovirt/charts/Chart.as b/src/flexchart/org/ovirt/charts/Chart.as index 26c8d02..f2faf33 100644 --- a/src/flexchart/org/ovirt/charts/Chart.as +++ b/src/flexchart/org/ovirt/charts/Chart.as @@ -19,19 +19,32 @@ */ package org.ovirt.charts { - - public class Chart { - import org.ovirt.DataSource; import mx.containers.Box; import org.ovirt.data.DataSeries; + public class Chart { + protected var container:Box; protected var datasourceUrl:String; + protected var startTime:Number; + protected var endTime:Number; + protected var target:String; + protected var id:int; + public function Chart(container:Box, datasourceUrl:String) { this.container = container; this.datasourceUrl = datasourceUrl; + if (datasourceUrl != null) { + var results:Array = datasourceUrl.split("/"); + if (results != null && results.length > 7) { + setId(new int(results[4])); + setTarget(results[5] as String); + setStartTime(new int(results[6])); + setEndTime(new int(results[7])); + } + } } public function addData(dataSeries:DataSeries):void { @@ -40,7 +53,24 @@ package org.ovirt.charts { public function load():void { var dataSource:DataSource = new DataSource(this); - dataSource.retrieveData(datasourceUrl); + var myString:String = "/ovirt/graph/flexchart_data/" + id + "/" + target + "/" + startTime + "/" + endTime; + dataSource.retrieveData(myString); + } + + public function setStartTime(startTime:Number):void { + this.startTime = startTime; + } + + public function setEndTime(endTime:Number):void { + this.endTime = endTime; + } + + public function setTarget(target:String):void { + this.target = target; + } + + public function setId(id:int):void { + this.id = id; } } } diff --git a/src/flexchart/org/ovirt/data/DataPoint.as b/src/flexchart/org/ovirt/data/DataPoint.as index 00cd0a4..5d59de9 100644 --- a/src/flexchart/org/ovirt/data/DataPoint.as +++ b/src/flexchart/org/ovirt/data/DataPoint.as @@ -22,17 +22,18 @@ package org.ovirt.data { public class DataPoint { - private var timestamp:String; + private var timestamp:Date; private var value:Number; private var description:String; - public function DataPoint (timestamp:String, value:Number, description:String) { + public function DataPoint (timestamp:Date, value:Number, + description:String) { this.timestamp = timestamp; this.value = value; this.description = description; } - public function getTimestamp():String { + public function getTimestamp():Date { return timestamp; } diff --git a/src/flexchart/org/ovirt/data/DataSeries.as b/src/flexchart/org/ovirt/data/DataSeries.as index 764fd34..eb653e9 100644 --- a/src/flexchart/org/ovirt/data/DataSeries.as +++ b/src/flexchart/org/ovirt/data/DataSeries.as @@ -36,9 +36,15 @@ package org.ovirt.data { dataPoints = new Array(); var inDataPoints:Array = object["vectors"] as Array; for (var i:int = 0; i < inDataPoints.length; i++) { - dataPoints.push(new DataPoint((inDataPoints[i] as Array)[0] as String, - (inDataPoints[i] as Array)[1] as Number, - description)); + var value:Number = 0; + var valuea:Number = (inDataPoints[i] as Array)[1] as Number; + if (!isNaN(valuea)) { + value = (inDataPoints[i] as Array)[1] as Number; + } + var seconds:int = int((inDataPoints[i] as Array)[0]) * 1000; + dataPoints.push(new DataPoint(new Date(seconds), + value, + description)); } maxValue = object["max_value"] as Number; } diff --git a/src/flexchart/org/ovirt/elements/SingleBar.as b/src/flexchart/org/ovirt/elements/SingleBar.as index 6e09bff..e7caf93 100644 --- a/src/flexchart/org/ovirt/elements/SingleBar.as +++ b/src/flexchart/org/ovirt/elements/SingleBar.as @@ -20,39 +20,66 @@ package org.ovirt.elements { - import mx.containers.Box; - import mx.controls.ToolTip; - import mx.managers.ToolTipManager; + import flash.display.DisplayObject; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Rectangle; - import flash.display.DisplayObject; + import mx.containers.Canvas; + import mx.controls.ToolTip; + import mx.events.FlexEvent; + import mx.events.ResizeEvent; + import mx.formatters.DateFormatter; + import mx.managers.ToolTipManager; import org.ovirt.data.DataPoint; - public class SingleBar extends Box { + public class SingleBar extends Canvas { private var tip:ToolTip; private var dataPoint:DataPoint; + private var scale:Number; + private var dateFormat:DateFormatter = new DateFormatter(); - public function SingleBar(dataPoint:DataPoint) { + public function SingleBar(dataPoint:DataPoint,scale:Number) { super(); this.dataPoint = dataPoint; + this.scale = scale; addEventListener(MouseEvent.MOUSE_OVER,showTip); addEventListener(MouseEvent.MOUSE_OUT,destroyTip); - this.setStyle("backgroundColor","0x0000FF"); - this.setStyle("left","1"); - this.setStyle("right","1"); + addEventListener(ResizeEvent.RESIZE,myResize); + addEventListener(FlexEvent.CREATION_COMPLETE,myResize); + addEventListener(Event.RENDER,myResize); + this.setStyle("backgroundColor","0x2875c1"); + dateFormat.formatString = "DD-MMM-YYYY JJ:NN"; + } + + public function destroy():void { + removeEventListener(MouseEvent.MOUSE_OVER,showTip); + removeEventListener(MouseEvent.MOUSE_OUT,destroyTip); + removeEventListener(ResizeEvent.RESIZE,myResize); + removeEventListener(FlexEvent.CREATION_COMPLETE,myResize); + removeEventListener(FlexEvent.UPDATE_COMPLETE,myResize); + removeEventListener(Event.RENDER,myResize); + } + + + private function myResize(event:Event):void { + trace(event.type); + this.height = (dataPoint.getValue() / scale) * parent.height * .9 * -1; + this.y = parent.height; } private function showTip(event:Event):void { + var w:Number = this.stage.width; var target:DisplayObject = event.currentTarget as DisplayObject; var pt:Rectangle = this.stage.getBounds(target); var yPos:Number = pt.y * -1; var xPos:Number = pt.x * -1; - tip = ToolTipManager.createToolTip(dataPoint.getDescription() + "\n" + - dataPoint.getTimestamp() + "\n" + - dataPoint.getValue(), + tip = ToolTipManager.createToolTip(dataPoint.getDescription() + + "\n" + + dateFormat.format(dataPoint.getTimestamp()) + + "\n" + + dataPoint.getValue(), xPos,yPos) as ToolTip; tip.x = Math.min(tip.x, w - tip.width); diff --git a/src/flexchart/org/ovirt/elements/XAxisLabel.as b/src/flexchart/org/ovirt/elements/XAxisLabel.as new file mode 100644 index 0000000..9bf4c26 --- /dev/null +++ b/src/flexchart/org/ovirt/elements/XAxisLabel.as @@ -0,0 +1,66 @@ +/* + Copyright (C) 2008 Red Hat, Inc. + Written by Steve Linabery <slinabery at redhat.com> + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. A copy of the GNU General Public License is + also available at http://www.gnu.org/copyleft/gpl.html. +*/ + +package org.ovirt.elements { + + import mx.containers.Box; + import mx.core.ScrollPolicy; + import flash.events.*; + import flash.events.MouseEvent; + import mx.events.*; + import mx.events.FlexEvent; + + public class XAxisLabel extends Box { + + public var labelText:TextLiberation; + private var center:int; + + public function XAxisLabel(text:String) { + super(); + labelText = new TextLiberation(text); + labelText.setVisible(true); + this.addChild(labelText); + this.horizontalScrollPolicy = ScrollPolicy.OFF; + this.verticalScrollPolicy = ScrollPolicy.OFF; + this.setStyle("paddingLeft","0"); + this.setStyle("paddingRight","0"); + addEventListener(FlexEvent.CREATION_COMPLETE,centerLabel); + } + + public function setCenter(center:int):void { + this.center = center; + } + + public function getCenter():int { + return center; + } + + private function centerLabel(event:Event):void { + this.x = center - labelText.getTextWidth() / 2; + if (parent != null) { + if (this.x < 0) { + this.x = 0; + } else if (this.x > parent.width - labelText.getTextWidth() - 5) { + this.x = parent.width - labelText.getTextWidth() - 5; + } + } + } + } +} diff --git a/src/flexchart/org/ovirt/elements/YAxisLabel.as b/src/flexchart/org/ovirt/elements/YAxisLabel.as index 0e93b97..73e1239 100644 --- a/src/flexchart/org/ovirt/elements/YAxisLabel.as +++ b/src/flexchart/org/ovirt/elements/YAxisLabel.as @@ -18,24 +18,11 @@ also available at http://www.gnu.org/copyleft/gpl.html. */ -package org.ovirt.elements { +//class for labeling the scale of the y-axis of a chart - import mx.containers.Box; - import mx.core.ScrollPolicy; +package org.ovirt.elements { public class YAxisLabel extends Box { - public var labelText:TextLiberation; - - public function YAxisLabel(text:String) { - super(); - labelText = new TextLiberation(text); - labelText.setVisible(true); - this.addChild(labelText); - this.horizontalScrollPolicy = ScrollPolicy.OFF; - this.verticalScrollPolicy = ScrollPolicy.OFF; - this.setStyle("paddingLeft","0"); - this.setStyle("paddingRight","0"); - } } } -- 1.5.6.5
Steve Linabery
2008-Nov-20 05:10 UTC
[Ovirt-devel] Re: [PATCH server] Add time range and data type selection to flexchart.
Gaah! Just noticed that I broke the date display somehow while editing out whitespace, etc. Revision 1st thing in AM. Thanks, Steve On Wed, Nov 19, 2008 at 10:12:04PM -0600, Steve Linabery wrote:> Also > -change color scheme to match tallen prototype pallette > -change SingleBar elements to dynamically resized Canvas objects instead of HBox objects > -add new route to accept second-resolution timestamps for time range selection > --- > src/app/controllers/graph_controller.rb | 59 ++++- > src/app/util/stats/StatsTypes.rb | 8 +- > src/app/views/graph/history_graphs.rhtml | 2 +- > src/config/routes.rb | 1 + > src/flexchart/flexchart.mxml | 12 +- > src/flexchart/org/ovirt/ApplicationBus.as | 39 ++++ > src/flexchart/org/ovirt/Constants.as | 2 +- > src/flexchart/org/ovirt/DataSource.as | 7 +- > src/flexchart/org/ovirt/charts/BarChart.as | 287 ++++++++++++++++++------ > src/flexchart/org/ovirt/charts/Chart.as | 38 +++- > src/flexchart/org/ovirt/data/DataPoint.as | 7 +- > src/flexchart/org/ovirt/data/DataSeries.as | 12 +- > src/flexchart/org/ovirt/elements/SingleBar.as | 51 ++++- > src/flexchart/org/ovirt/elements/XAxisLabel.as | 66 ++++++ > src/flexchart/org/ovirt/elements/YAxisLabel.as | 17 +-- > 15 files changed, 476 insertions(+), 132 deletions(-) > create mode 100644 src/flexchart/org/ovirt/ApplicationBus.as > create mode 100644 src/flexchart/org/ovirt/elements/XAxisLabel.as > > diff --git a/src/app/controllers/graph_controller.rb b/src/app/controllers/graph_controller.rb > index 0105ea6..d636544 100644 > --- a/src/app/controllers/graph_controller.rb > +++ b/src/app/controllers/graph_controller.rb > @@ -4,20 +4,53 @@ class GraphController < ApplicationController > layout nil > > def flexchart_data > - > - #FIXME: use the stats package aggregation (when it's available) > - #instead of the old method > - graph_obj = history_graph_data_object > - > - #FIXME: for this release, the flexchart shows only peak values, > - # and only shows a default of the last 40 data points in rrd. > - graph_data = { :labels => graph_obj[:timepoints].last(40), > - :values => graph_obj[:dataset][2][:values].last(40) } > - my_data = graph_data[:labels].zip(graph_data[:values]) > - graph = { :vectors => my_data, > - :max_value => graph_obj[:total_peak], > - :description => params[:target] > - } > + @id = params[:id] > + target = params[:target] > + startTime = params[:startTime].to_i > + endTime = params[:endTime].to_i > + duration = endTime - startTime > + > + #the maximum number of data points we want in any chart > + maxPoints = 100 > + resolution > + case > + when duration / RRDResolution::Minimum < maxPoints > + RRDResolution::Minimum > + when duration / RRDResolution::Short < maxPoints > + RRDResolution::Short > + when duration / RRDResolution::Medium < maxPoints > + RRDResolution::Medium > + when duration / RRDResolution::Long < maxPoints > + RRDResolution::Long > + else > + RRDResolution::Maximum > + end > + devclass = DEV_KEY_CLASSES[target] > + counter = DEV_KEY_COUNTERS[target] > + > + #FIXME: until stats aggregation is pushed, we just get stats data for > + # the first host in the pool. If no host in pool, the chart will be > + # empty. > + pool = Pool.find(@id) > + hosts = pool.hosts > + host = pool.hosts[0] > + requestList = [ ] > + requestList.push StatsRequest.new(host.hostname, devclass, 0, counter, startTime, duration, resolution, DataFunction::Peak) > + statsList = getStatsData?(requestList) > + > + #The aggregated (summed) stats will come back as a single stats list > + stat = statsList[0] > + vectors = [ ] > + data = stat.get_data? > + data.each{ |datum| > + val = datum.get_value? > + val = 0 if val.nan? > + vectors.push [datum.get_timestamp?.to_i, val] > + } > + graph = { :vectors => vectors, > + :max_value => stat.get_max_value?, > + :description => target > + } > render :json => graph > end > > diff --git a/src/app/util/stats/StatsTypes.rb b/src/app/util/stats/StatsTypes.rb > index 41c2977..4896bb3 100644 > --- a/src/app/util/stats/StatsTypes.rb > +++ b/src/app/util/stats/StatsTypes.rb > @@ -324,10 +324,12 @@ class RRDResolution > end > > # Set up the resolutions for our rrd > + RRDResolution.add_item :Minimum, 10 # Ten secs > + RRDResolution.add_item :Short, 70 # Seventy secs > + RRDResolution.add_item :Medium, 500 # 500 secs ( 8minute, 20 sec) > + RRDResolution.add_item :Long, 2230 > + RRDResolution.add_item :Maximum, 26350 > RRDResolution.add_item :Default, 10 # Ten secs > - RRDResolution.add_item :Short, 500 # 500 secs ( 8minute, 20 sec) > - RRDResolution.add_item :Medium, 2230 > - RRDResolution.add_item :Long, 26350 > end > > > diff --git a/src/app/views/graph/history_graphs.rhtml b/src/app/views/graph/history_graphs.rhtml > index bebe5d9..97ed67f 100644 > --- a/src/app/views/graph/history_graphs.rhtml > +++ b/src/app/views/graph/history_graphs.rhtml > @@ -10,7 +10,7 @@ $('#flex_history_chart').flash( > height: 300, > wmode: 'transparent', > menu: false, > - flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/1" } > + flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/<%= Time.now.to_i - 60 * 60 * 24 %>/<%= Time.now.to_i %>" } > }, > { version: 9 } > ); > diff --git a/src/config/routes.rb b/src/config/routes.rb > index 8d538cb..0dbe0d6 100644 > --- a/src/config/routes.rb > +++ b/src/config/routes.rb > @@ -41,6 +41,7 @@ ActionController::Routing::Routes.draw do |map| > map.connect ':controller/service.wsdl', :action => 'wsdl' > > # Install the default route as the lowest priority. > + map.connect 'graph/flexchart_data/:id/:target/:startTime/:endTime', :controller => 'graph', :action => 'flexchart_data' > map.connect 'graph/flexchart_data/:id/:target/:days', :controller => 'graph', :action => 'flexchart_data' > map.connect ':controller/:action/:id.:format' > map.connect ':controller/:action/:id' > diff --git a/src/flexchart/flexchart.mxml b/src/flexchart/flexchart.mxml > index 35fa9a6..c4a089f 100644 > --- a/src/flexchart/flexchart.mxml > +++ b/src/flexchart/flexchart.mxml > @@ -6,6 +6,7 @@ > import mx.containers.VBox; > import mx.effects.Resize; > import org.ovirt.Constants; > + import org.ovirt.ApplicationBus; > import org.ovirt.charts.Chart; > import org.ovirt.charts.BarChart; > > @@ -21,6 +22,7 @@ > private var contractResources:Resize = new Resize(); > > private function myInit():void { > + > mainChart.height = Constants.height; > mainChart.width = Constants.width; > hostsChart.width = Constants.width; > @@ -42,6 +44,9 @@ > contractResources.heightTo = 0; > contractResources.target = hostsChart; > contractResources.duration = 500; > + > + ApplicationBus.instance().barClickAction = zoomIntoSeries; > + > } > > private function zoomOutSeries(e:Event):void { > @@ -61,7 +66,8 @@ > > </mx:Script> > > - <mx:VBox id="mainChart" click="zoomIntoSeries(event)" /> > - <mx:VBox id="hostsChart" visible="false" opaqueBackground="0x00ff00" click="zoomOutSeries(event)"/> > - > + <mx:VBox id="mainChart" /> > + <mx:VBox id="hostsChart" visible="false" opaqueBackground="0x00ff00" click="zoomOutSeries(event)"> > + <mx:Text text="This Space Reserved for Drill-down Hosts Chart" /> > + </mx:VBox> > </mx:Application> > diff --git a/src/flexchart/org/ovirt/ApplicationBus.as b/src/flexchart/org/ovirt/ApplicationBus.as > new file mode 100644 > index 0000000..7d6d431 > --- /dev/null > +++ b/src/flexchart/org/ovirt/ApplicationBus.as > @@ -0,0 +1,39 @@ > +/* > + Copyright (C) 2008 Red Hat, Inc. > + Written by Steve Linabery <slinabery at redhat.com> > + > + 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. > + > + You should have received a copy of the GNU General Public License > + along with this program; if not, write to the Free Software > + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > + MA 02110-1301, USA. A copy of the GNU General Public License is > + also available at http://www.gnu.org/copyleft/gpl.html. > +*/ > + > +//A way to expose some functions that are defined in flexchart.mxml to > +//our ActionScript classes without needing to access Application directly > + > +package org.ovirt { > + public class ApplicationBus { > + > + private static var _instance:ApplicationBus; > + > + public static function instance():ApplicationBus { > + if (_instance == null) { > + _instance = new ApplicationBus(); > + } > + return _instance; > + } > + > + public var barClickAction:Function; > + > + } > +} > diff --git a/src/flexchart/org/ovirt/Constants.as b/src/flexchart/org/ovirt/Constants.as > index 996a31e..14f6465 100644 > --- a/src/flexchart/org/ovirt/Constants.as > +++ b/src/flexchart/org/ovirt/Constants.as > @@ -23,6 +23,6 @@ package org.ovirt { > public static var width:int = 722; > public static var height:int = 297; > public static var barSpacing:int = 2; > - public static var labelHeight:int = 40; > + public static var labelHeight:int = 20; > } > } > diff --git a/src/flexchart/org/ovirt/DataSource.as b/src/flexchart/org/ovirt/DataSource.as > index 44e482d..4438900 100644 > --- a/src/flexchart/org/ovirt/DataSource.as > +++ b/src/flexchart/org/ovirt/DataSource.as > @@ -20,13 +20,13 @@ > > package org.ovirt { > > - import flash.net.URLLoader; > - import flash.net.URLRequest; > import com.adobe.serialization.json.JSON; > import flash.events.Event; > import flash.events.IOErrorEvent; > - import org.ovirt.data.DataSeries; > + import flash.net.URLLoader; > + import flash.net.URLRequest; > import org.ovirt.charts.Chart; > + import org.ovirt.data.DataSeries; > > public class DataSource { > > @@ -52,6 +52,7 @@ package org.ovirt { > } > > private function ioError( e:IOErrorEvent ):void { > + trace("ioError"); > //FIXME: > //do something useful with this error > } > diff --git a/src/flexchart/org/ovirt/charts/BarChart.as b/src/flexchart/org/ovirt/charts/BarChart.as > index 83cf0bb..88a75ca 100644 > --- a/src/flexchart/org/ovirt/charts/BarChart.as > +++ b/src/flexchart/org/ovirt/charts/BarChart.as > @@ -19,40 +19,127 @@ > */ > > package org.ovirt.charts { > - > + import flash.events.Event; > + import flash.events.MouseEvent; > + import mx.collections.ArrayCollection; > import mx.containers.Box; > import mx.containers.HBox; > import mx.containers.VBox; > - import mx.controls.Text; > import mx.containers.Canvas; > + import mx.controls.TextInput; > + import mx.controls.DateField; > + import mx.controls.Button; > + import mx.controls.PopUpMenuButton; > + import mx.controls.Text; > + import mx.events.MenuEvent; > + import mx.formatters.DateFormatter; > import org.ovirt.data.*; > import org.ovirt.elements.*; > import org.ovirt.Constants; > + import org.ovirt.ApplicationBus; > > public class BarChart extends Chart { > > - private var chartArea:HBox; > - private var labelArea:Canvas; > + private var chartArea:Canvas; > + private var XAxisLabelArea:Canvas; > + private var startDateField:DateField; > + private var endDateField:DateField; > + private var startTimeField:TextInput; > + private var endTimeField:TextInput; > + private var button:Button; > + private var menu:PopUpMenuButton; > + private var dateBar:Box; > + private var datePattern:RegExp; > + > > public function BarChart(container:Box, > datasourceUrl:String) { > super(container,datasourceUrl); > - chartArea = new HBox(); > - chartArea.setStyle("horizontalGap",Constants.barSpacing); > - chartArea.setStyle("verticalAlign","bottom"); > + container.setStyle("verticalGap","2"); > + datePattern = /^(\d+):(\d+)$/; > + } > + > + > + private function timeRangeAdjusted(event:Event):void { > + var t1:Number = startDateField.selectedDate.getTime() > + + (parseHour(startTimeField.text) * 3600 * 1000) > + + (parseMinute(startTimeField.text) * 60 * 1000); > + setStartTime(Math.floor(t1 / 1000)); > + var t2:Number = endDateField.selectedDate.getTime() > + + (parseHour(endTimeField.text) * 3600 * 1000) > + + (parseMinute(endTimeField.text) * 60 * 1000); > + setEndTime(Math.floor(t2 / 1000)); > + load(); > + } > + > + private function typeSelected(event:MenuEvent):void { > + target = event.label; > + load(); > + } > + > + > + private function pad(input:int):String { > + if (input < 10) { > + return "0" + input; > + } else { > + return "" + input; > + } > + } > + > + private function parseHour(input:String):int { > + var answer:int = 0; > + try { > + var obj:Object = datePattern.exec(input); > + if (obj != null) { > + answer = int(obj[1].toString()); > + } > + } catch (e:Error) {} > + return answer; > + } > + > + private function parseMinute(input:String):int { > + var answer:int = 0; > + try { > + var obj:Object = datePattern.exec(input); > + if (obj != null) { > + answer = int(obj[2].toString()); > + } > + } catch (e:Error) {} > + return answer; > + } > + > + override public function addData(dataSeries:DataSeries):void { > + container.removeAllChildren(); > + > + var dateFormat:DateFormatter = new DateFormatter(); > + > + //since we're reusing objects, we need to get rid of stale > + //EventListener references > + if (chartArea != null) { > + var kids:Array = chartArea.getChildren(); > + var i:int; > + for (i = 0; i < kids.length; i++) { > + (kids[i] as SingleBar).destroy(); > + } > + } > + > + chartArea = new Canvas(); > chartArea.percentHeight = 80; > chartArea.percentWidth = 100; > + chartArea.setStyle("backgroundColor","0xbbccdd"); > this.container.addChild(chartArea); > > - labelArea = new Canvas(); > - labelArea.height = Constants.labelHeight; > - labelArea.minHeight = Constants.labelHeight; > - labelArea.percentWidth = 100; > - this.container.addChild(labelArea); > - } > + XAxisLabelArea = new Canvas(); > + XAxisLabelArea.height = Constants.labelHeight; > + XAxisLabelArea.minHeight = Constants.labelHeight; > + XAxisLabelArea.percentWidth = 100; > + this.container.addChild(XAxisLabelArea); > > - override public function addData(dataSeries:DataSeries):void { > try { > + > + dateBar = new HBox(); > + dateBar.setVisible(true); > + this.container.addChild(dateBar); > var dataPoints:Array = dataSeries.getDataPoints(); > var maxValue:Number = dataSeries.getMaxValue(); > var scale:Number = maxValue; > @@ -65,74 +152,72 @@ package org.ovirt.charts { > throw new Error("No data points in range"); > } > > - //have to iterate through datapoint.timestamp strings, > - //create a TextLiberation object with them, and add them to > - //a parent container before we can tell how wide they are in pixels. > - var labelWidth:Number = 0; > - for (var i:int = 0; i < size; i++) { > - var dataPoint:DataPoint = dataPoints[i] as DataPoint; > - var textTemp:TextLiberation > - new TextLiberation(dataPoint.getTimestamp()); > - textTemp.setVisible(false); > - chartArea.addChild(textTemp); > - var tempLabelWidth:Number = textTemp.getTextWidth(); > - if (! isNaN(tempLabelWidth)) { > - labelWidth = Math.max(labelWidth, tempLabelWidth); > - } > - } > - //now we have to remove all the children we just added, since we don't > - //really want them to be part of the chart. > - chartArea.removeAllChildren(); > - > - //we always want an odd number of y-axis labels, and we'll > - //determine this by using the labelWidth we just determined > - var labelCount:int = Math.floor(Constants.width / labelWidth); > - if (labelCount > 3 && labelCount % 2 == 1) { > - labelCount--; > - } > - > //the distance between left edges of adjacent bars > - var gridWidth:Number = Constants.width / size; > + var gridWidth:Number = Math.floor(Constants.width / size); > > //the width of each SingleBar (does not including padding between bars) > var barWidth:Number = gridWidth - Constants.barSpacing; > > - //use this to center y-axis labels on the bars > - var labelOffset:Number = barWidth / 2; > + //due to the discrete number of pixels, there may be space at the > + //right side of the graph that needs to be made up by padding > + //bars here and there > + var shortfall:Number = Constants.width - (gridWidth * size); > + var makeup:Number = Math.round(size / shortfall); > + var madeup:Number = 0; > + > + //variable to hold the numbered day of the month of the last > + //XAxisLabel added to the label area > + var lastDate:Number; > > - //distance between first and last label > - var labelSpace:Number = Constants.width - gridWidth; > - var labelSpacing:Number = labelSpace / labelCount; > + //variable to hold the x-coordinate of the next bar to be added to > + //the chart > + var currentBarPosition:int = 0; > > //add the bars & labels to the chart > var labelCounter:int = 0; > for (i = 0; i < size; i++) { > - dataPoint = dataPoints[i] as DataPoint; > + > + var dataPoint:DataPoint = dataPoints[i] as DataPoint; > + if (i == 0) { > + lastDate = dataPoint.getTimestamp().date; > + } > + > + //show long date format for first & last XAxisLabels, > + //as well as whenever the date changes > + if (i == 0 || i == size - 1 > + || dataPoint.getTimestamp().date != lastDate) { > + dateFormat.formatString = "DD-MMM-YYYY JJ:NN"; > + } else { > + dateFormat.formatString = "JJ:NN"; > + } > + > var value:Number = dataPoint.getValue(); > - var bar:SingleBar = new SingleBar(dataPoint); > - bar.percentHeight = ((value / scale) * 80); > - bar.width = barWidth; > - bar.setVisible(true); > + var bar:SingleBar = new SingleBar(dataPoint,scale); > chartArea.addChild(bar); > - var currentLabelPosition:int = labelCounter * labelSpacing + > - labelOffset; > - > - if (currentLabelPosition >= i * gridWidth && > - currentLabelPosition < (i + 1) * gridWidth) { > - var label:YAxisLabel = new YAxisLabel(dataPoint.getTimestamp()); > - label.setVisible(false); > - label.y = ((labelCounter + 1) % 2) * 13 + 4; > - labelArea.addChild(label); > - //make sure the label is fully within the chart width > - label.x = Math.max(0, > - Math.min((i) * gridWidth - > - (label.labelText.getTextWidth() / 2) + > - labelOffset, > - Constants.width - > - label.labelText.getTextWidth() - 6) > - ); > + bar.width = barWidth; > + bar.addEventListener(MouseEvent.CLICK, > + ApplicationBus.instance().barClickAction); > + bar.x = currentBarPosition; > + if (makeup > 0 && i % makeup == 0 && madeup < shortfall) { > + bar.width = bar.width + 1; > + madeup++; > + } > + > + //add XAxisLabels at the endpoints of the time range, > + //as well as the center if there are more than 6 points > + //and two more if there are more than 14 points > + if ((size > 6 && i == Math.floor(size / 2)) > + || (size > 14 > + && (i == Math.floor(size / 4) > + || i == Math.floor(size * 3 / 4))) > + || i == 0 > + || i == size - 1) { > + var label:XAxisLabel > + new XAxisLabel(dateFormat.format(dataPoint.getTimestamp())); > + label.setCenter(currentBarPosition + bar.width / 2); > label.setVisible(true); > - labelCounter++; > + label.y = 6; > + XAxisLabelArea.addChild(label); > > //add a 'tick' in the center of the bar to which this label > //corresponds > @@ -140,13 +225,73 @@ package org.ovirt.charts { > ind.opaqueBackground = 0x000000; > ind.width=1; > ind.height=3; > - ind.x = (i) * gridWidth + labelOffset; > + ind.x = label.getCenter(); > ind.y = 0; > ind.setVisible(true); > ind.setStyle("backgroundColor","0x000000"); > - labelArea.addChild(ind); > + XAxisLabelArea.addChild(ind); > + lastDate = dataPoint.getTimestamp().date; > } > + currentBarPosition += (bar.width + Constants.barSpacing); > + } > + > + //fill in the time range selection bar > + var t:Date; > + var f1:Text = new Text(); > + f1.text = "View data between"; > + dateBar.addChild(f1); > + t = new Date(dataPoints[0].getTimestamp().getTime()); > + startDateField = new DateField(); > + startDateField.selectedDate = t; > + startDateField.editable = true; > + startTimeField = new TextInput(); > + startTimeField.minWidth = 50; > + startTimeField.maxWidth = 50; > + startTimeField.text = pad(t.hours) + ":" + pad(t.minutes); > + dateBar.addChild(startTimeField); > + dateBar.addChild(startDateField); > + var f2:Text = new Text(); > + f2.text = "and"; > + dateBar.addChild(f2); > + > + t = new Date(dataPoints[size - 1].getTimestamp().getTime()); > + endDateField = new DateField(); > + endDateField.selectedDate = t; > + endDateField.editable = true; > + endTimeField = new TextInput(); > + endTimeField.minWidth = 50; > + endTimeField.maxWidth = 50; > + endTimeField.text = pad(t.hours) + ":" + pad(t.minutes); > + dateBar.addChild(endTimeField); > + dateBar.addChild(endDateField); > + > + button = new Button(); > + button.label = "go"; > + button.addEventListener(MouseEvent.CLICK,timeRangeAdjusted); > + dateBar.addChild(button); > + > + //FIXME: these should be fetched from the graph controller so > + //that different types can be added (or restricted) dynamically > + var menuItems:ArrayCollection > + new ArrayCollection( [{label: "memory"}, > + {label: "cpu"}, > + {label: "load"}, > + {label: "netin"}, > + {label: "netout"}, > + {label: "disk"} > + ]); > + > + > + if (menu != null) { > + menu.removeEventListener(MenuEvent.ITEM_CLICK,typeSelected); > } > + > + menu = new PopUpMenuButton(); > + menu.label = "Select Data Type"; > + menu.dataProvider = menuItems; > + menu.addEventListener(MenuEvent.ITEM_CLICK,typeSelected); > + dateBar.addChild(menu); > + > } catch (e:Error) { > var err:Text = new Text(); > err.text = e.message; > diff --git a/src/flexchart/org/ovirt/charts/Chart.as b/src/flexchart/org/ovirt/charts/Chart.as > index 26c8d02..f2faf33 100644 > --- a/src/flexchart/org/ovirt/charts/Chart.as > +++ b/src/flexchart/org/ovirt/charts/Chart.as > @@ -19,19 +19,32 @@ > */ > > package org.ovirt.charts { > - > - public class Chart { > - > import org.ovirt.DataSource; > import mx.containers.Box; > import org.ovirt.data.DataSeries; > > + public class Chart { > + > protected var container:Box; > protected var datasourceUrl:String; > > + protected var startTime:Number; > + protected var endTime:Number; > + protected var target:String; > + protected var id:int; > + > public function Chart(container:Box, datasourceUrl:String) { > this.container = container; > this.datasourceUrl = datasourceUrl; > + if (datasourceUrl != null) { > + var results:Array = datasourceUrl.split("/"); > + if (results != null && results.length > 7) { > + setId(new int(results[4])); > + setTarget(results[5] as String); > + setStartTime(new int(results[6])); > + setEndTime(new int(results[7])); > + } > + } > } > > public function addData(dataSeries:DataSeries):void { > @@ -40,7 +53,24 @@ package org.ovirt.charts { > > public function load():void { > var dataSource:DataSource = new DataSource(this); > - dataSource.retrieveData(datasourceUrl); > + var myString:String = "/ovirt/graph/flexchart_data/" + id + "/" + target + "/" + startTime + "/" + endTime; > + dataSource.retrieveData(myString); > + } > + > + public function setStartTime(startTime:Number):void { > + this.startTime = startTime; > + } > + > + public function setEndTime(endTime:Number):void { > + this.endTime = endTime; > + } > + > + public function setTarget(target:String):void { > + this.target = target; > + } > + > + public function setId(id:int):void { > + this.id = id; > } > } > } > diff --git a/src/flexchart/org/ovirt/data/DataPoint.as b/src/flexchart/org/ovirt/data/DataPoint.as > index 00cd0a4..5d59de9 100644 > --- a/src/flexchart/org/ovirt/data/DataPoint.as > +++ b/src/flexchart/org/ovirt/data/DataPoint.as > @@ -22,17 +22,18 @@ package org.ovirt.data { > > public class DataPoint { > > - private var timestamp:String; > + private var timestamp:Date; > private var value:Number; > private var description:String; > > - public function DataPoint (timestamp:String, value:Number, description:String) { > + public function DataPoint (timestamp:Date, value:Number, > + description:String) { > this.timestamp = timestamp; > this.value = value; > this.description = description; > } > > - public function getTimestamp():String { > + public function getTimestamp():Date { > return timestamp; > } > > diff --git a/src/flexchart/org/ovirt/data/DataSeries.as b/src/flexchart/org/ovirt/data/DataSeries.as > index 764fd34..eb653e9 100644 > --- a/src/flexchart/org/ovirt/data/DataSeries.as > +++ b/src/flexchart/org/ovirt/data/DataSeries.as > @@ -36,9 +36,15 @@ package org.ovirt.data { > dataPoints = new Array(); > var inDataPoints:Array = object["vectors"] as Array; > for (var i:int = 0; i < inDataPoints.length; i++) { > - dataPoints.push(new DataPoint((inDataPoints[i] as Array)[0] as String, > - (inDataPoints[i] as Array)[1] as Number, > - description)); > + var value:Number = 0; > + var valuea:Number = (inDataPoints[i] as Array)[1] as Number; > + if (!isNaN(valuea)) { > + value = (inDataPoints[i] as Array)[1] as Number; > + } > + var seconds:int = int((inDataPoints[i] as Array)[0]) * 1000; > + dataPoints.push(new DataPoint(new Date(seconds), > + value, > + description)); > } > maxValue = object["max_value"] as Number; > } > diff --git a/src/flexchart/org/ovirt/elements/SingleBar.as b/src/flexchart/org/ovirt/elements/SingleBar.as > index 6e09bff..e7caf93 100644 > --- a/src/flexchart/org/ovirt/elements/SingleBar.as > +++ b/src/flexchart/org/ovirt/elements/SingleBar.as > @@ -20,39 +20,66 @@ > > package org.ovirt.elements { > > - import mx.containers.Box; > - import mx.controls.ToolTip; > - import mx.managers.ToolTipManager; > + import flash.display.DisplayObject; > import flash.events.Event; > import flash.events.MouseEvent; > import flash.geom.Rectangle; > - import flash.display.DisplayObject; > + import mx.containers.Canvas; > + import mx.controls.ToolTip; > + import mx.events.FlexEvent; > + import mx.events.ResizeEvent; > + import mx.formatters.DateFormatter; > + import mx.managers.ToolTipManager; > import org.ovirt.data.DataPoint; > > - public class SingleBar extends Box { > + public class SingleBar extends Canvas { > > private var tip:ToolTip; > private var dataPoint:DataPoint; > + private var scale:Number; > + private var dateFormat:DateFormatter = new DateFormatter(); > > - public function SingleBar(dataPoint:DataPoint) { > + public function SingleBar(dataPoint:DataPoint,scale:Number) { > super(); > this.dataPoint = dataPoint; > + this.scale = scale; > addEventListener(MouseEvent.MOUSE_OVER,showTip); > addEventListener(MouseEvent.MOUSE_OUT,destroyTip); > - this.setStyle("backgroundColor","0x0000FF"); > - this.setStyle("left","1"); > - this.setStyle("right","1"); > + addEventListener(ResizeEvent.RESIZE,myResize); > + addEventListener(FlexEvent.CREATION_COMPLETE,myResize); > + addEventListener(Event.RENDER,myResize); > + this.setStyle("backgroundColor","0x2875c1"); > + dateFormat.formatString = "DD-MMM-YYYY JJ:NN"; > + } > + > + public function destroy():void { > + removeEventListener(MouseEvent.MOUSE_OVER,showTip); > + removeEventListener(MouseEvent.MOUSE_OUT,destroyTip); > + removeEventListener(ResizeEvent.RESIZE,myResize); > + removeEventListener(FlexEvent.CREATION_COMPLETE,myResize); > + removeEventListener(FlexEvent.UPDATE_COMPLETE,myResize); > + removeEventListener(Event.RENDER,myResize); > + } > + > + > + private function myResize(event:Event):void { > + trace(event.type); > + this.height = (dataPoint.getValue() / scale) * parent.height * .9 * -1; > + this.y = parent.height; > } > > private function showTip(event:Event):void { > + > var w:Number = this.stage.width; > var target:DisplayObject = event.currentTarget as DisplayObject; > var pt:Rectangle = this.stage.getBounds(target); > var yPos:Number = pt.y * -1; > var xPos:Number = pt.x * -1; > - tip = ToolTipManager.createToolTip(dataPoint.getDescription() + "\n" + > - dataPoint.getTimestamp() + "\n" + > - dataPoint.getValue(), > + tip = ToolTipManager.createToolTip(dataPoint.getDescription() + > + "\n" + > + dateFormat.format(dataPoint.getTimestamp()) + > + "\n" + > + dataPoint.getValue(), > xPos,yPos) as ToolTip; > tip.x = Math.min(tip.x, > w - tip.width); > diff --git a/src/flexchart/org/ovirt/elements/XAxisLabel.as b/src/flexchart/org/ovirt/elements/XAxisLabel.as > new file mode 100644 > index 0000000..9bf4c26 > --- /dev/null > +++ b/src/flexchart/org/ovirt/elements/XAxisLabel.as > @@ -0,0 +1,66 @@ > +/* > + Copyright (C) 2008 Red Hat, Inc. > + Written by Steve Linabery <slinabery at redhat.com> > + > + 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. > + > + You should have received a copy of the GNU General Public License > + along with this program; if not, write to the Free Software > + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > + MA 02110-1301, USA. A copy of the GNU General Public License is > + also available at http://www.gnu.org/copyleft/gpl.html. > +*/ > + > +package org.ovirt.elements { > + > + import mx.containers.Box; > + import mx.core.ScrollPolicy; > + import flash.events.*; > + import flash.events.MouseEvent; > + import mx.events.*; > + import mx.events.FlexEvent; > + > + public class XAxisLabel extends Box { > + > + public var labelText:TextLiberation; > + private var center:int; > + > + public function XAxisLabel(text:String) { > + super(); > + labelText = new TextLiberation(text); > + labelText.setVisible(true); > + this.addChild(labelText); > + this.horizontalScrollPolicy = ScrollPolicy.OFF; > + this.verticalScrollPolicy = ScrollPolicy.OFF; > + this.setStyle("paddingLeft","0"); > + this.setStyle("paddingRight","0"); > + addEventListener(FlexEvent.CREATION_COMPLETE,centerLabel); > + } > + > + public function setCenter(center:int):void { > + this.center = center; > + } > + > + public function getCenter():int { > + return center; > + } > + > + private function centerLabel(event:Event):void { > + this.x = center - labelText.getTextWidth() / 2; > + if (parent != null) { > + if (this.x < 0) { > + this.x = 0; > + } else if (this.x > parent.width - labelText.getTextWidth() - 5) { > + this.x = parent.width - labelText.getTextWidth() - 5; > + } > + } > + } > + } > +} > diff --git a/src/flexchart/org/ovirt/elements/YAxisLabel.as b/src/flexchart/org/ovirt/elements/YAxisLabel.as > index 0e93b97..73e1239 100644 > --- a/src/flexchart/org/ovirt/elements/YAxisLabel.as > +++ b/src/flexchart/org/ovirt/elements/YAxisLabel.as > @@ -18,24 +18,11 @@ > also available at http://www.gnu.org/copyleft/gpl.html. > */ > > -package org.ovirt.elements { > +//class for labeling the scale of the y-axis of a chart > > - import mx.containers.Box; > - import mx.core.ScrollPolicy; > +package org.ovirt.elements { > > public class YAxisLabel extends Box { > > - public var labelText:TextLiberation; > - > - public function YAxisLabel(text:String) { > - super(); > - labelText = new TextLiberation(text); > - labelText.setVisible(true); > - this.addChild(labelText); > - this.horizontalScrollPolicy = ScrollPolicy.OFF; > - this.verticalScrollPolicy = ScrollPolicy.OFF; > - this.setStyle("paddingLeft","0"); > - this.setStyle("paddingRight","0"); > - } > } > } > -- > 1.5.6.5 >
Mark Wagner
2008-Nov-20 13:41 UTC
[Ovirt-devel] [PATCH server] Add time range and data type selection to flexchart.
Steve Linabery wrote:> Also > -change color scheme to match tallen prototype pallette > -change SingleBar elements to dynamically resized Canvas objects instead of HBox objects > -add new route to accept second-resolution timestamps for time range selection > --- > src/app/controllers/graph_controller.rb | 59 ++++- > src/app/util/stats/StatsTypes.rb | 8 +- > src/app/views/graph/history_graphs.rhtml | 2 +- > src/config/routes.rb | 1 + > src/flexchart/flexchart.mxml | 12 +- > src/flexchart/org/ovirt/ApplicationBus.as | 39 ++++ > src/flexchart/org/ovirt/Constants.as | 2 +- > src/flexchart/org/ovirt/DataSource.as | 7 +- > src/flexchart/org/ovirt/charts/BarChart.as | 287 ++++++++++++++++++------ > src/flexchart/org/ovirt/charts/Chart.as | 38 +++- > src/flexchart/org/ovirt/data/DataPoint.as | 7 +- > src/flexchart/org/ovirt/data/DataSeries.as | 12 +- > src/flexchart/org/ovirt/elements/SingleBar.as | 51 ++++- > src/flexchart/org/ovirt/elements/XAxisLabel.as | 66 ++++++ > src/flexchart/org/ovirt/elements/YAxisLabel.as | 17 +-- > 15 files changed, 476 insertions(+), 132 deletions(-) > create mode 100644 src/flexchart/org/ovirt/ApplicationBus.as > create mode 100644 src/flexchart/org/ovirt/elements/XAxisLabel.as > > > > diff --git a/src/app/util/stats/StatsTypes.rb b/src/app/util/stats/StatsTypes.rb > index 41c2977..4896bb3 100644 > --- a/src/app/util/stats/StatsTypes.rb > +++ b/src/app/util/stats/StatsTypes.rb > @@ -324,10 +324,12 @@ class RRDResolution > end > > # Set up the resolutions for our rrd > + RRDResolution.add_item :Minimum, 10 # Ten secs > + RRDResolution.add_item :Short, 70 # Seventy secs > + RRDResolution.add_item :Medium, 500 # 500 secs ( 8minute, 20 sec) > + RRDResolution.add_item :Long, 2230 > + RRDResolution.add_item :Maximum, 26350 > RRDResolution.add_item :Default, 10 # Ten secs > - RRDResolution.add_item :Short, 500 # 500 secs ( 8minute, 20 sec) > - RRDResolution.add_item :Medium, 2230 > - RRDResolution.add_item :Long, 26350 > end >Steve We use the RRDResolution as the Ruby equiv of an enum. So if you are changing the values here, you will also need to change them in statsTest.rb or that will break. I realize that most of the tests are commented out, but that was more cuz I hadn't enabled them all before submittal. AFAIK, most of them should work and in theory, they should be active and part of the test suite. -mark
Steve Linabery
2008-Nov-20 15:52 UTC
[Ovirt-devel] [PATCH server] Add time range and data type selection to flexchart.
Also: -change color scheme to match tallen prototype pallette -change SingleBar elements to dynamically resized Canvas objects instead of HBox objects -add new route to accept second-resolution timestamps for time range selection --- src/app/controllers/graph_controller.rb | 59 ++++- src/app/util/stats/StatsTypes.rb | 8 +- src/app/views/graph/history_graphs.rhtml | 2 +- src/config/routes.rb | 1 + src/flexchart/flexchart.mxml | 12 +- src/flexchart/org/ovirt/ApplicationBus.as | 39 ++++ src/flexchart/org/ovirt/Constants.as | 2 +- src/flexchart/org/ovirt/DataSource.as | 7 +- src/flexchart/org/ovirt/charts/BarChart.as | 287 ++++++++++++++++++------ src/flexchart/org/ovirt/charts/Chart.as | 38 +++- src/flexchart/org/ovirt/data/DataPoint.as | 7 +- src/flexchart/org/ovirt/data/DataSeries.as | 12 +- src/flexchart/org/ovirt/elements/SingleBar.as | 51 ++++- src/flexchart/org/ovirt/elements/XAxisLabel.as | 66 ++++++ src/flexchart/org/ovirt/elements/YAxisLabel.as | 17 +-- 15 files changed, 476 insertions(+), 132 deletions(-) create mode 100644 src/flexchart/org/ovirt/ApplicationBus.as create mode 100644 src/flexchart/org/ovirt/elements/XAxisLabel.as diff --git a/src/app/controllers/graph_controller.rb b/src/app/controllers/graph_controller.rb index 0105ea6..d636544 100644 --- a/src/app/controllers/graph_controller.rb +++ b/src/app/controllers/graph_controller.rb @@ -4,20 +4,53 @@ class GraphController < ApplicationController layout nil def flexchart_data - - #FIXME: use the stats package aggregation (when it's available) - #instead of the old method - graph_obj = history_graph_data_object - - #FIXME: for this release, the flexchart shows only peak values, - # and only shows a default of the last 40 data points in rrd. - graph_data = { :labels => graph_obj[:timepoints].last(40), - :values => graph_obj[:dataset][2][:values].last(40) } - my_data = graph_data[:labels].zip(graph_data[:values]) - graph = { :vectors => my_data, - :max_value => graph_obj[:total_peak], - :description => params[:target] - } + @id = params[:id] + target = params[:target] + startTime = params[:startTime].to_i + endTime = params[:endTime].to_i + duration = endTime - startTime + + #the maximum number of data points we want in any chart + maxPoints = 100 + resolution + case + when duration / RRDResolution::Minimum < maxPoints + RRDResolution::Minimum + when duration / RRDResolution::Short < maxPoints + RRDResolution::Short + when duration / RRDResolution::Medium < maxPoints + RRDResolution::Medium + when duration / RRDResolution::Long < maxPoints + RRDResolution::Long + else + RRDResolution::Maximum + end + devclass = DEV_KEY_CLASSES[target] + counter = DEV_KEY_COUNTERS[target] + + #FIXME: until stats aggregation is pushed, we just get stats data for + # the first host in the pool. If no host in pool, the chart will be + # empty. + pool = Pool.find(@id) + hosts = pool.hosts + host = pool.hosts[0] + requestList = [ ] + requestList.push StatsRequest.new(host.hostname, devclass, 0, counter, startTime, duration, resolution, DataFunction::Peak) + statsList = getStatsData?(requestList) + + #The aggregated (summed) stats will come back as a single stats list + stat = statsList[0] + vectors = [ ] + data = stat.get_data? + data.each{ |datum| + val = datum.get_value? + val = 0 if val.nan? + vectors.push [datum.get_timestamp?.to_i, val] + } + graph = { :vectors => vectors, + :max_value => stat.get_max_value?, + :description => target + } render :json => graph end diff --git a/src/app/util/stats/StatsTypes.rb b/src/app/util/stats/StatsTypes.rb index 41c2977..4896bb3 100644 --- a/src/app/util/stats/StatsTypes.rb +++ b/src/app/util/stats/StatsTypes.rb @@ -324,10 +324,12 @@ class RRDResolution end # Set up the resolutions for our rrd + RRDResolution.add_item :Minimum, 10 # Ten secs + RRDResolution.add_item :Short, 70 # Seventy secs + RRDResolution.add_item :Medium, 500 # 500 secs ( 8minute, 20 sec) + RRDResolution.add_item :Long, 2230 + RRDResolution.add_item :Maximum, 26350 RRDResolution.add_item :Default, 10 # Ten secs - RRDResolution.add_item :Short, 500 # 500 secs ( 8minute, 20 sec) - RRDResolution.add_item :Medium, 2230 - RRDResolution.add_item :Long, 26350 end diff --git a/src/app/views/graph/history_graphs.rhtml b/src/app/views/graph/history_graphs.rhtml index bebe5d9..97ed67f 100644 --- a/src/app/views/graph/history_graphs.rhtml +++ b/src/app/views/graph/history_graphs.rhtml @@ -10,7 +10,7 @@ $('#flex_history_chart').flash( height: 300, wmode: 'transparent', menu: false, - flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/1" } + flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/<%= Time.now.to_i - 60 * 60 * 24 %>/<%= Time.now.to_i %>" } }, { version: 9 } ); diff --git a/src/config/routes.rb b/src/config/routes.rb index 8d538cb..0dbe0d6 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -41,6 +41,7 @@ ActionController::Routing::Routes.draw do |map| map.connect ':controller/service.wsdl', :action => 'wsdl' # Install the default route as the lowest priority. + map.connect 'graph/flexchart_data/:id/:target/:startTime/:endTime', :controller => 'graph', :action => 'flexchart_data' map.connect 'graph/flexchart_data/:id/:target/:days', :controller => 'graph', :action => 'flexchart_data' map.connect ':controller/:action/:id.:format' map.connect ':controller/:action/:id' diff --git a/src/flexchart/flexchart.mxml b/src/flexchart/flexchart.mxml index 35fa9a6..c4a089f 100644 --- a/src/flexchart/flexchart.mxml +++ b/src/flexchart/flexchart.mxml @@ -6,6 +6,7 @@ import mx.containers.VBox; import mx.effects.Resize; import org.ovirt.Constants; + import org.ovirt.ApplicationBus; import org.ovirt.charts.Chart; import org.ovirt.charts.BarChart; @@ -21,6 +22,7 @@ private var contractResources:Resize = new Resize(); private function myInit():void { + mainChart.height = Constants.height; mainChart.width = Constants.width; hostsChart.width = Constants.width; @@ -42,6 +44,9 @@ contractResources.heightTo = 0; contractResources.target = hostsChart; contractResources.duration = 500; + + ApplicationBus.instance().barClickAction = zoomIntoSeries; + } private function zoomOutSeries(e:Event):void { @@ -61,7 +66,8 @@ </mx:Script> - <mx:VBox id="mainChart" click="zoomIntoSeries(event)" /> - <mx:VBox id="hostsChart" visible="false" opaqueBackground="0x00ff00" click="zoomOutSeries(event)"/> - + <mx:VBox id="mainChart" /> + <mx:VBox id="hostsChart" visible="false" opaqueBackground="0x00ff00" click="zoomOutSeries(event)"> + <mx:Text text="This Space Reserved for Drill-down Hosts Chart" /> + </mx:VBox> </mx:Application> diff --git a/src/flexchart/org/ovirt/ApplicationBus.as b/src/flexchart/org/ovirt/ApplicationBus.as new file mode 100644 index 0000000..7d6d431 --- /dev/null +++ b/src/flexchart/org/ovirt/ApplicationBus.as @@ -0,0 +1,39 @@ +/* + Copyright (C) 2008 Red Hat, Inc. + Written by Steve Linabery <slinabery at redhat.com> + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. A copy of the GNU General Public License is + also available at http://www.gnu.org/copyleft/gpl.html. +*/ + +//A way to expose some functions that are defined in flexchart.mxml to +//our ActionScript classes without needing to access Application directly + +package org.ovirt { + public class ApplicationBus { + + private static var _instance:ApplicationBus; + + public static function instance():ApplicationBus { + if (_instance == null) { + _instance = new ApplicationBus(); + } + return _instance; + } + + public var barClickAction:Function; + + } +} diff --git a/src/flexchart/org/ovirt/Constants.as b/src/flexchart/org/ovirt/Constants.as index 996a31e..14f6465 100644 --- a/src/flexchart/org/ovirt/Constants.as +++ b/src/flexchart/org/ovirt/Constants.as @@ -23,6 +23,6 @@ package org.ovirt { public static var width:int = 722; public static var height:int = 297; public static var barSpacing:int = 2; - public static var labelHeight:int = 40; + public static var labelHeight:int = 20; } } diff --git a/src/flexchart/org/ovirt/DataSource.as b/src/flexchart/org/ovirt/DataSource.as index 44e482d..4438900 100644 --- a/src/flexchart/org/ovirt/DataSource.as +++ b/src/flexchart/org/ovirt/DataSource.as @@ -20,13 +20,13 @@ package org.ovirt { - import flash.net.URLLoader; - import flash.net.URLRequest; import com.adobe.serialization.json.JSON; import flash.events.Event; import flash.events.IOErrorEvent; - import org.ovirt.data.DataSeries; + import flash.net.URLLoader; + import flash.net.URLRequest; import org.ovirt.charts.Chart; + import org.ovirt.data.DataSeries; public class DataSource { @@ -52,6 +52,7 @@ package org.ovirt { } private function ioError( e:IOErrorEvent ):void { + trace("ioError"); //FIXME: //do something useful with this error } diff --git a/src/flexchart/org/ovirt/charts/BarChart.as b/src/flexchart/org/ovirt/charts/BarChart.as index 83cf0bb..88a75ca 100644 --- a/src/flexchart/org/ovirt/charts/BarChart.as +++ b/src/flexchart/org/ovirt/charts/BarChart.as @@ -19,40 +19,127 @@ */ package org.ovirt.charts { - + import flash.events.Event; + import flash.events.MouseEvent; + import mx.collections.ArrayCollection; import mx.containers.Box; import mx.containers.HBox; import mx.containers.VBox; - import mx.controls.Text; import mx.containers.Canvas; + import mx.controls.TextInput; + import mx.controls.DateField; + import mx.controls.Button; + import mx.controls.PopUpMenuButton; + import mx.controls.Text; + import mx.events.MenuEvent; + import mx.formatters.DateFormatter; import org.ovirt.data.*; import org.ovirt.elements.*; import org.ovirt.Constants; + import org.ovirt.ApplicationBus; public class BarChart extends Chart { - private var chartArea:HBox; - private var labelArea:Canvas; + private var chartArea:Canvas; + private var XAxisLabelArea:Canvas; + private var startDateField:DateField; + private var endDateField:DateField; + private var startTimeField:TextInput; + private var endTimeField:TextInput; + private var button:Button; + private var menu:PopUpMenuButton; + private var dateBar:Box; + private var datePattern:RegExp; + public function BarChart(container:Box, datasourceUrl:String) { super(container,datasourceUrl); - chartArea = new HBox(); - chartArea.setStyle("horizontalGap",Constants.barSpacing); - chartArea.setStyle("verticalAlign","bottom"); + container.setStyle("verticalGap","2"); + datePattern = /^(\d+):(\d+)$/; + } + + + private function timeRangeAdjusted(event:Event):void { + var t1:Number = startDateField.selectedDate.getTime() + + (parseHour(startTimeField.text) * 3600 * 1000) + + (parseMinute(startTimeField.text) * 60 * 1000); + setStartTime(Math.floor(t1 / 1000)); + var t2:Number = endDateField.selectedDate.getTime() + + (parseHour(endTimeField.text) * 3600 * 1000) + + (parseMinute(endTimeField.text) * 60 * 1000); + setEndTime(Math.floor(t2 / 1000)); + load(); + } + + private function typeSelected(event:MenuEvent):void { + target = event.label; + load(); + } + + + private function pad(input:int):String { + if (input < 10) { + return "0" + input; + } else { + return "" + input; + } + } + + private function parseHour(input:String):int { + var answer:int = 0; + try { + var obj:Object = datePattern.exec(input); + if (obj != null) { + answer = int(obj[1].toString()); + } + } catch (e:Error) {} + return answer; + } + + private function parseMinute(input:String):int { + var answer:int = 0; + try { + var obj:Object = datePattern.exec(input); + if (obj != null) { + answer = int(obj[2].toString()); + } + } catch (e:Error) {} + return answer; + } + + override public function addData(dataSeries:DataSeries):void { + container.removeAllChildren(); + + var dateFormat:DateFormatter = new DateFormatter(); + + //since we're reusing objects, we need to get rid of stale + //EventListener references + if (chartArea != null) { + var kids:Array = chartArea.getChildren(); + var i:int; + for (i = 0; i < kids.length; i++) { + (kids[i] as SingleBar).destroy(); + } + } + + chartArea = new Canvas(); chartArea.percentHeight = 80; chartArea.percentWidth = 100; + chartArea.setStyle("backgroundColor","0xbbccdd"); this.container.addChild(chartArea); - labelArea = new Canvas(); - labelArea.height = Constants.labelHeight; - labelArea.minHeight = Constants.labelHeight; - labelArea.percentWidth = 100; - this.container.addChild(labelArea); - } + XAxisLabelArea = new Canvas(); + XAxisLabelArea.height = Constants.labelHeight; + XAxisLabelArea.minHeight = Constants.labelHeight; + XAxisLabelArea.percentWidth = 100; + this.container.addChild(XAxisLabelArea); - override public function addData(dataSeries:DataSeries):void { try { + + dateBar = new HBox(); + dateBar.setVisible(true); + this.container.addChild(dateBar); var dataPoints:Array = dataSeries.getDataPoints(); var maxValue:Number = dataSeries.getMaxValue(); var scale:Number = maxValue; @@ -65,74 +152,72 @@ package org.ovirt.charts { throw new Error("No data points in range"); } - //have to iterate through datapoint.timestamp strings, - //create a TextLiberation object with them, and add them to - //a parent container before we can tell how wide they are in pixels. - var labelWidth:Number = 0; - for (var i:int = 0; i < size; i++) { - var dataPoint:DataPoint = dataPoints[i] as DataPoint; - var textTemp:TextLiberation - new TextLiberation(dataPoint.getTimestamp()); - textTemp.setVisible(false); - chartArea.addChild(textTemp); - var tempLabelWidth:Number = textTemp.getTextWidth(); - if (! isNaN(tempLabelWidth)) { - labelWidth = Math.max(labelWidth, tempLabelWidth); - } - } - //now we have to remove all the children we just added, since we don't - //really want them to be part of the chart. - chartArea.removeAllChildren(); - - //we always want an odd number of y-axis labels, and we'll - //determine this by using the labelWidth we just determined - var labelCount:int = Math.floor(Constants.width / labelWidth); - if (labelCount > 3 && labelCount % 2 == 1) { - labelCount--; - } - //the distance between left edges of adjacent bars - var gridWidth:Number = Constants.width / size; + var gridWidth:Number = Math.floor(Constants.width / size); //the width of each SingleBar (does not including padding between bars) var barWidth:Number = gridWidth - Constants.barSpacing; - //use this to center y-axis labels on the bars - var labelOffset:Number = barWidth / 2; + //due to the discrete number of pixels, there may be space at the + //right side of the graph that needs to be made up by padding + //bars here and there + var shortfall:Number = Constants.width - (gridWidth * size); + var makeup:Number = Math.round(size / shortfall); + var madeup:Number = 0; + + //variable to hold the numbered day of the month of the last + //XAxisLabel added to the label area + var lastDate:Number; - //distance between first and last label - var labelSpace:Number = Constants.width - gridWidth; - var labelSpacing:Number = labelSpace / labelCount; + //variable to hold the x-coordinate of the next bar to be added to + //the chart + var currentBarPosition:int = 0; //add the bars & labels to the chart var labelCounter:int = 0; for (i = 0; i < size; i++) { - dataPoint = dataPoints[i] as DataPoint; + + var dataPoint:DataPoint = dataPoints[i] as DataPoint; + if (i == 0) { + lastDate = dataPoint.getTimestamp().date; + } + + //show long date format for first & last XAxisLabels, + //as well as whenever the date changes + if (i == 0 || i == size - 1 + || dataPoint.getTimestamp().date != lastDate) { + dateFormat.formatString = "DD-MMM-YYYY JJ:NN"; + } else { + dateFormat.formatString = "JJ:NN"; + } + var value:Number = dataPoint.getValue(); - var bar:SingleBar = new SingleBar(dataPoint); - bar.percentHeight = ((value / scale) * 80); - bar.width = barWidth; - bar.setVisible(true); + var bar:SingleBar = new SingleBar(dataPoint,scale); chartArea.addChild(bar); - var currentLabelPosition:int = labelCounter * labelSpacing + - labelOffset; - - if (currentLabelPosition >= i * gridWidth && - currentLabelPosition < (i + 1) * gridWidth) { - var label:YAxisLabel = new YAxisLabel(dataPoint.getTimestamp()); - label.setVisible(false); - label.y = ((labelCounter + 1) % 2) * 13 + 4; - labelArea.addChild(label); - //make sure the label is fully within the chart width - label.x = Math.max(0, - Math.min((i) * gridWidth - - (label.labelText.getTextWidth() / 2) + - labelOffset, - Constants.width - - label.labelText.getTextWidth() - 6) - ); + bar.width = barWidth; + bar.addEventListener(MouseEvent.CLICK, + ApplicationBus.instance().barClickAction); + bar.x = currentBarPosition; + if (makeup > 0 && i % makeup == 0 && madeup < shortfall) { + bar.width = bar.width + 1; + madeup++; + } + + //add XAxisLabels at the endpoints of the time range, + //as well as the center if there are more than 6 points + //and two more if there are more than 14 points + if ((size > 6 && i == Math.floor(size / 2)) + || (size > 14 + && (i == Math.floor(size / 4) + || i == Math.floor(size * 3 / 4))) + || i == 0 + || i == size - 1) { + var label:XAxisLabel + new XAxisLabel(dateFormat.format(dataPoint.getTimestamp())); + label.setCenter(currentBarPosition + bar.width / 2); label.setVisible(true); - labelCounter++; + label.y = 6; + XAxisLabelArea.addChild(label); //add a 'tick' in the center of the bar to which this label //corresponds @@ -140,13 +225,73 @@ package org.ovirt.charts { ind.opaqueBackground = 0x000000; ind.width=1; ind.height=3; - ind.x = (i) * gridWidth + labelOffset; + ind.x = label.getCenter(); ind.y = 0; ind.setVisible(true); ind.setStyle("backgroundColor","0x000000"); - labelArea.addChild(ind); + XAxisLabelArea.addChild(ind); + lastDate = dataPoint.getTimestamp().date; } + currentBarPosition += (bar.width + Constants.barSpacing); + } + + //fill in the time range selection bar + var t:Date; + var f1:Text = new Text(); + f1.text = "View data between"; + dateBar.addChild(f1); + t = new Date(dataPoints[0].getTimestamp().getTime()); + startDateField = new DateField(); + startDateField.selectedDate = t; + startDateField.editable = true; + startTimeField = new TextInput(); + startTimeField.minWidth = 50; + startTimeField.maxWidth = 50; + startTimeField.text = pad(t.hours) + ":" + pad(t.minutes); + dateBar.addChild(startTimeField); + dateBar.addChild(startDateField); + var f2:Text = new Text(); + f2.text = "and"; + dateBar.addChild(f2); + + t = new Date(dataPoints[size - 1].getTimestamp().getTime()); + endDateField = new DateField(); + endDateField.selectedDate = t; + endDateField.editable = true; + endTimeField = new TextInput(); + endTimeField.minWidth = 50; + endTimeField.maxWidth = 50; + endTimeField.text = pad(t.hours) + ":" + pad(t.minutes); + dateBar.addChild(endTimeField); + dateBar.addChild(endDateField); + + button = new Button(); + button.label = "go"; + button.addEventListener(MouseEvent.CLICK,timeRangeAdjusted); + dateBar.addChild(button); + + //FIXME: these should be fetched from the graph controller so + //that different types can be added (or restricted) dynamically + var menuItems:ArrayCollection + new ArrayCollection( [{label: "memory"}, + {label: "cpu"}, + {label: "load"}, + {label: "netin"}, + {label: "netout"}, + {label: "disk"} + ]); + + + if (menu != null) { + menu.removeEventListener(MenuEvent.ITEM_CLICK,typeSelected); } + + menu = new PopUpMenuButton(); + menu.label = "Select Data Type"; + menu.dataProvider = menuItems; + menu.addEventListener(MenuEvent.ITEM_CLICK,typeSelected); + dateBar.addChild(menu); + } catch (e:Error) { var err:Text = new Text(); err.text = e.message; diff --git a/src/flexchart/org/ovirt/charts/Chart.as b/src/flexchart/org/ovirt/charts/Chart.as index 26c8d02..f2faf33 100644 --- a/src/flexchart/org/ovirt/charts/Chart.as +++ b/src/flexchart/org/ovirt/charts/Chart.as @@ -19,19 +19,32 @@ */ package org.ovirt.charts { - - public class Chart { - import org.ovirt.DataSource; import mx.containers.Box; import org.ovirt.data.DataSeries; + public class Chart { + protected var container:Box; protected var datasourceUrl:String; + protected var startTime:Number; + protected var endTime:Number; + protected var target:String; + protected var id:int; + public function Chart(container:Box, datasourceUrl:String) { this.container = container; this.datasourceUrl = datasourceUrl; + if (datasourceUrl != null) { + var results:Array = datasourceUrl.split("/"); + if (results != null && results.length > 7) { + setId(new int(results[4])); + setTarget(results[5] as String); + setStartTime(new int(results[6])); + setEndTime(new int(results[7])); + } + } } public function addData(dataSeries:DataSeries):void { @@ -40,7 +53,24 @@ package org.ovirt.charts { public function load():void { var dataSource:DataSource = new DataSource(this); - dataSource.retrieveData(datasourceUrl); + var myString:String = "/ovirt/graph/flexchart_data/" + id + "/" + target + "/" + startTime + "/" + endTime; + dataSource.retrieveData(myString); + } + + public function setStartTime(startTime:Number):void { + this.startTime = startTime; + } + + public function setEndTime(endTime:Number):void { + this.endTime = endTime; + } + + public function setTarget(target:String):void { + this.target = target; + } + + public function setId(id:int):void { + this.id = id; } } } diff --git a/src/flexchart/org/ovirt/data/DataPoint.as b/src/flexchart/org/ovirt/data/DataPoint.as index 00cd0a4..5d59de9 100644 --- a/src/flexchart/org/ovirt/data/DataPoint.as +++ b/src/flexchart/org/ovirt/data/DataPoint.as @@ -22,17 +22,18 @@ package org.ovirt.data { public class DataPoint { - private var timestamp:String; + private var timestamp:Date; private var value:Number; private var description:String; - public function DataPoint (timestamp:String, value:Number, description:String) { + public function DataPoint (timestamp:Date, value:Number, + description:String) { this.timestamp = timestamp; this.value = value; this.description = description; } - public function getTimestamp():String { + public function getTimestamp():Date { return timestamp; } diff --git a/src/flexchart/org/ovirt/data/DataSeries.as b/src/flexchart/org/ovirt/data/DataSeries.as index 764fd34..709ceea 100644 --- a/src/flexchart/org/ovirt/data/DataSeries.as +++ b/src/flexchart/org/ovirt/data/DataSeries.as @@ -36,9 +36,15 @@ package org.ovirt.data { dataPoints = new Array(); var inDataPoints:Array = object["vectors"] as Array; for (var i:int = 0; i < inDataPoints.length; i++) { - dataPoints.push(new DataPoint((inDataPoints[i] as Array)[0] as String, - (inDataPoints[i] as Array)[1] as Number, - description)); + var value:Number = 0; + var valuea:Number = (inDataPoints[i] as Array)[1] as Number; + if (!isNaN(valuea)) { + value = (inDataPoints[i] as Array)[1] as Number; + } + var seconds:Number = new Number((inDataPoints[i] as Array)[0]) * 1000; + dataPoints.push(new DataPoint(new Date(seconds), + value, + description)); } maxValue = object["max_value"] as Number; } diff --git a/src/flexchart/org/ovirt/elements/SingleBar.as b/src/flexchart/org/ovirt/elements/SingleBar.as index 6e09bff..e7caf93 100644 --- a/src/flexchart/org/ovirt/elements/SingleBar.as +++ b/src/flexchart/org/ovirt/elements/SingleBar.as @@ -20,39 +20,66 @@ package org.ovirt.elements { - import mx.containers.Box; - import mx.controls.ToolTip; - import mx.managers.ToolTipManager; + import flash.display.DisplayObject; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Rectangle; - import flash.display.DisplayObject; + import mx.containers.Canvas; + import mx.controls.ToolTip; + import mx.events.FlexEvent; + import mx.events.ResizeEvent; + import mx.formatters.DateFormatter; + import mx.managers.ToolTipManager; import org.ovirt.data.DataPoint; - public class SingleBar extends Box { + public class SingleBar extends Canvas { private var tip:ToolTip; private var dataPoint:DataPoint; + private var scale:Number; + private var dateFormat:DateFormatter = new DateFormatter(); - public function SingleBar(dataPoint:DataPoint) { + public function SingleBar(dataPoint:DataPoint,scale:Number) { super(); this.dataPoint = dataPoint; + this.scale = scale; addEventListener(MouseEvent.MOUSE_OVER,showTip); addEventListener(MouseEvent.MOUSE_OUT,destroyTip); - this.setStyle("backgroundColor","0x0000FF"); - this.setStyle("left","1"); - this.setStyle("right","1"); + addEventListener(ResizeEvent.RESIZE,myResize); + addEventListener(FlexEvent.CREATION_COMPLETE,myResize); + addEventListener(Event.RENDER,myResize); + this.setStyle("backgroundColor","0x2875c1"); + dateFormat.formatString = "DD-MMM-YYYY JJ:NN"; + } + + public function destroy():void { + removeEventListener(MouseEvent.MOUSE_OVER,showTip); + removeEventListener(MouseEvent.MOUSE_OUT,destroyTip); + removeEventListener(ResizeEvent.RESIZE,myResize); + removeEventListener(FlexEvent.CREATION_COMPLETE,myResize); + removeEventListener(FlexEvent.UPDATE_COMPLETE,myResize); + removeEventListener(Event.RENDER,myResize); + } + + + private function myResize(event:Event):void { + trace(event.type); + this.height = (dataPoint.getValue() / scale) * parent.height * .9 * -1; + this.y = parent.height; } private function showTip(event:Event):void { + var w:Number = this.stage.width; var target:DisplayObject = event.currentTarget as DisplayObject; var pt:Rectangle = this.stage.getBounds(target); var yPos:Number = pt.y * -1; var xPos:Number = pt.x * -1; - tip = ToolTipManager.createToolTip(dataPoint.getDescription() + "\n" + - dataPoint.getTimestamp() + "\n" + - dataPoint.getValue(), + tip = ToolTipManager.createToolTip(dataPoint.getDescription() + + "\n" + + dateFormat.format(dataPoint.getTimestamp()) + + "\n" + + dataPoint.getValue(), xPos,yPos) as ToolTip; tip.x = Math.min(tip.x, w - tip.width); diff --git a/src/flexchart/org/ovirt/elements/XAxisLabel.as b/src/flexchart/org/ovirt/elements/XAxisLabel.as new file mode 100644 index 0000000..9bf4c26 --- /dev/null +++ b/src/flexchart/org/ovirt/elements/XAxisLabel.as @@ -0,0 +1,66 @@ +/* + Copyright (C) 2008 Red Hat, Inc. + Written by Steve Linabery <slinabery at redhat.com> + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. A copy of the GNU General Public License is + also available at http://www.gnu.org/copyleft/gpl.html. +*/ + +package org.ovirt.elements { + + import mx.containers.Box; + import mx.core.ScrollPolicy; + import flash.events.*; + import flash.events.MouseEvent; + import mx.events.*; + import mx.events.FlexEvent; + + public class XAxisLabel extends Box { + + public var labelText:TextLiberation; + private var center:int; + + public function XAxisLabel(text:String) { + super(); + labelText = new TextLiberation(text); + labelText.setVisible(true); + this.addChild(labelText); + this.horizontalScrollPolicy = ScrollPolicy.OFF; + this.verticalScrollPolicy = ScrollPolicy.OFF; + this.setStyle("paddingLeft","0"); + this.setStyle("paddingRight","0"); + addEventListener(FlexEvent.CREATION_COMPLETE,centerLabel); + } + + public function setCenter(center:int):void { + this.center = center; + } + + public function getCenter():int { + return center; + } + + private function centerLabel(event:Event):void { + this.x = center - labelText.getTextWidth() / 2; + if (parent != null) { + if (this.x < 0) { + this.x = 0; + } else if (this.x > parent.width - labelText.getTextWidth() - 5) { + this.x = parent.width - labelText.getTextWidth() - 5; + } + } + } + } +} diff --git a/src/flexchart/org/ovirt/elements/YAxisLabel.as b/src/flexchart/org/ovirt/elements/YAxisLabel.as index 0e93b97..73e1239 100644 --- a/src/flexchart/org/ovirt/elements/YAxisLabel.as +++ b/src/flexchart/org/ovirt/elements/YAxisLabel.as @@ -18,24 +18,11 @@ also available at http://www.gnu.org/copyleft/gpl.html. */ -package org.ovirt.elements { +//class for labeling the scale of the y-axis of a chart - import mx.containers.Box; - import mx.core.ScrollPolicy; +package org.ovirt.elements { public class YAxisLabel extends Box { - public var labelText:TextLiberation; - - public function YAxisLabel(text:String) { - super(); - labelText = new TextLiberation(text); - labelText.setVisible(true); - this.addChild(labelText); - this.horizontalScrollPolicy = ScrollPolicy.OFF; - this.verticalScrollPolicy = ScrollPolicy.OFF; - this.setStyle("paddingLeft","0"); - this.setStyle("paddingRight","0"); - } } } -- 1.5.6.5