Jason Guiditta
2008-Oct-10 20:56 UTC
[Ovirt-devel] Re: [PATCH server] Add to summary pages a rudimentary flash chart written in flex framework
Assuming we are pushing this in with all the temporary caveats for generating the swf, this does work for me, but a few questions/comments inline. ACK with suggestions added, I tested locally and they work fine for me. On Fri, 2008-10-10 at 15:00 -0500, Steve Linabery wrote:> --- > src/app/controllers/graph_controller.rb | 30 +- > src/app/views/graph/flexchart_data.rhtml | 1 + > src/app/views/graph/history_graphs.rhtml | 87 +--- > src/config/routes.rb | 1 + > src/flexchart/README.txt | 8 + > src/flexchart/com/adobe/serialization/json/JSON.as | 85 +++ > .../com/adobe/serialization/json/JSONDecoder.as | 221 ++++++++ > .../com/adobe/serialization/json/JSONEncoder.as | 299 +++++++++++ > .../com/adobe/serialization/json/JSONParseError.as | 87 +++ > .../com/adobe/serialization/json/JSONToken.as | 104 ++++ > .../com/adobe/serialization/json/JSONTokenType.as | 67 +++ > .../com/adobe/serialization/json/JSONTokenizer.as | 547 ++++++++++++++++++++ > src/flexchart/flexchart.mxml | 20 + > src/flexchart/org/ovirt/ChartLoader.as | 64 +++ > src/flexchart/org/ovirt/DataSeries.as | 42 ++ > src/flexchart/org/ovirt/DataSource.as | 57 ++ > src/public/javascripts/jquery.flash.js | 288 ++++++++++ > 17 files changed, 1930 insertions(+), 78 deletions(-) > create mode 100644 src/app/views/graph/flexchart_data.rhtml > create mode 100644 src/flexchart/README.txt > create mode 100644 src/flexchart/com/adobe/serialization/json/JSON.as > create mode 100644 src/flexchart/com/adobe/serialization/json/JSONDecoder.as > create mode 100644 src/flexchart/com/adobe/serialization/json/JSONEncoder.as > create mode 100644 src/flexchart/com/adobe/serialization/json/JSONParseError.as > create mode 100644 src/flexchart/com/adobe/serialization/json/JSONToken.as > create mode 100644 src/flexchart/com/adobe/serialization/json/JSONTokenType.as > create mode 100644 src/flexchart/com/adobe/serialization/json/JSONTokenizer.as > create mode 100644 src/flexchart/flexchart.mxml > create mode 100644 src/flexchart/org/ovirt/ChartLoader.as > create mode 100644 src/flexchart/org/ovirt/DataSeries.as > create mode 100644 src/flexchart/org/ovirt/DataSource.as > create mode 100644 src/public/javascripts/jquery.flash.js > > diff --git a/src/app/controllers/graph_controller.rb b/src/app/controllers/graph_controller.rb > index dbe2afc..6450935 100644 > --- a/src/app/controllers/graph_controller.rb > +++ b/src/app/controllers/graph_controller.rb > @@ -3,7 +3,24 @@ require 'util/stats/Stats' > class GraphController < ApplicationController > layout nil > > - # generate layout for avaialability bar graphs > + 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] > + } > + end > + > + > + # generate layout for availability bar graphs > def availability_graph > @id = params[:id] > @target = params[:target] > @@ -67,6 +84,10 @@ class GraphController < ApplicationController > > # retrieves data for history graphs > def history_graph_data > + render :json => history_graph_data_object > + end > + > + def history_graph_data_object > history_graphs > myDays = params[:days] > target = params[:target] > @@ -212,9 +233,10 @@ class GraphController < ApplicationController > :stroke => @avg_history[:color], > :strokeWidth => 1 > } > - ] > + ], > + :total_peak => total_peak > } > - render :json => graph_object > + > end > > > @@ -261,7 +283,7 @@ class GraphController < ApplicationController > } > ] > } > - render :json => graph_object > + > > end > > diff --git a/src/app/views/graph/flexchart_data.rhtml b/src/app/views/graph/flexchart_data.rhtml > new file mode 100644 > index 0000000..a79ce06 > --- /dev/null > +++ b/src/app/views/graph/flexchart_data.rhtml > @@ -0,0 +1 @@Don't understand why you are doing this here, rather than doing a render :json in the controller?> +<%= @graph.to_json %> > diff --git a/src/app/views/graph/history_graphs.rhtml b/src/app/views/graph/history_graphs.rhtml > index 2b6874f..f372e4b 100644 > --- a/src/app/views/graph/history_graphs.rhtml > +++ b/src/app/views/graph/history_graphs.rhtml > @@ -1,76 +1,15 @@ > +<%= javascript_include_tag "jquery.flash.js" %> > +<div id="the-div-name"></div> > <script type="text/javascript"> > - > -var graph = "load_history"; > -var days = "7"; > - > -function swap_history_graph(newgraph, newdays){ > - if(newgraph == null) newgraph = graph > - if(newdays == null) newdays = days > - $('.history_graph').hide(); > - $('#' + newgraph + "_" + newdays).parent().show(); > - eval("draw_" + newgraph + "_" + newdays + "_graph_get_data()"); > -} > -function swap_history_graph_target(title, newgraph){ > - swap_history_graph(newgraph, null); > - $('#history_graph_selection').html(title + ' <%= image_tag 'icon_menu_arrow.gif' %>'); > - graph = newgraph > -} > -function swap_history_graph_time(title, newdays){ > - swap_history_graph(null, newdays); > - $('#history_graph_time_selection').html(title + ' <%= image_tag 'icon_menu_arrow.gif' %>'); > - days = newdays > -} > - > -</script> > - > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_cpu_history_1_graph', :div_id => 'cpu_history_1', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 20, :scaleX => 173, :ticksY => 10, :scaleY => 110, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'cpu', :poolType => @poolType, :days => 1 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_cpu_history_7_graph', :div_id => 'cpu_history_7', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 39, :scaleX => 272, :ticksY => 10, :scaleY => 110, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'cpu', :poolType => @poolType, :days => 7 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_cpu_history_30_graph', :div_id => 'cpu_history_30', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 120, :scaleX => 1200, :ticksY => 10, :scaleY => 110, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'cpu', :poolType => @poolType, :days => 30 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_memory_history_1_graph', :div_id => 'memory_history_1', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 20, :scaleX => 173, :ticksY => 50, :scaleY => 756, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'memory', :poolType => @poolType, :days => 1 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_memory_history_7_graph', :div_id => 'memory_history_7', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 39, :scaleX => 272, :ticksY => 50, :scaleY => 756, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'memory', :poolType => @poolType, :days => 7 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_memory_history_30_graph', :div_id => 'memory_history_30', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 120, :scaleX => 1162, :ticksY => 50, :scaleY => 756, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'memory', :poolType => @poolType, :days => 30 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_load_history_1_graph', :div_id => 'load_history_1', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 20, :scaleX => 173, :ticksY => 2, :scaleY => 23, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'load', :poolType => @poolType, :days => 1 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_load_history_7_graph', :div_id => 'load_history_7', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 39, :scaleX => 272, :ticksY => 2, :scaleY => 23, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'load', :poolType => @poolType, :days => 7 } ) } %> > -<%= render :partial => '/layouts/graph', :locals => { :drawMe => false, :includeDiv => false, :methodName=> 'draw_load_history_30_graph', :div_id => 'load_history_30', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 120, :scaleX => 1200, :ticksY => 2, :scaleY => 23, :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'load', :poolType => @poolType, :days => 30 } ) } %> > - > -<div id="history_graphs"> > - <div id="history_graphs_control"> > - <div class="history_graphs_menu"> > - <ul> > - <li><div id="history_graph_selection" class="history_graph_menu_header">Overall Load <%= image_tag 'icon_menu_arrow.gif' %></div></li> > - <li class="history_graph_menu_item history_graph_menu_fitem"><a href="#" onclick="swap_history_graph_target('Overall Load', 'load_history')" >Overall Load</a></li> > - <li class="history_graph_menu_item"><a href="#" onclick="swap_history_graph_target('CPU History', 'cpu_history');" >CPU History</a></li> > - <li class="history_graph_menu_item history_graph_menu_litem"><a href="#" onclick="swap_history_graph_target('Memory History', 'memory_history');">Memory History</a></li> > - </ul> > - </div> > - <div class="history_graphs_menu"> > - <ul> > - <li><div id="history_graph_time_selection" class="history_graph_menu_header">Last 7 Days <%= image_tag 'icon_menu_arrow.gif' %></div></li> > - <li class="history_graph_menu_item history_graph_menu_fitem"<a href="#" onclick="swap_history_graph_time('Last 24 Hours', '1')" >Last 24 Hours</a></li> > - <li class="history_graph_menu_item"><a href="#" onclick="swap_history_graph_time('Last 7 days', '7')" >Last 7 Days</a></li> > - <li class="history_graph_menu_item history_graph_menu_litem"><a href="#" onclick="swap_history_graph_time('Last 30 Days', '30')" >Last 30 Days</a></li> > - </ul> > - </div> > - <div class="history_graphs_legend"> > - <font color="<%= @peak_history[:color] %>">Peak </font> > - <font color="<%= @avg_history[:color] %>">Average </font> > - <font color="<%= @roll_peak_history[:color] %>">Rolling Peak </font> > - <font color="<%= @roll_avg_history[:color] %>">Rolling Average </font> > - </div> > - </div> > - <div id="history_graphs_graphs"> > - <div class="history_graph"><div id="cpu_history_1"> </div></div> > - <div class="history_graph"><div id="cpu_history_7"> </div></div> > - <div class="history_graph"><div id="cpu_history_30"> </div></div> > - <div class="history_graph"><div id="memory_history_1"> </div></div> > - <div class="history_graph"><div id="memory_history_7"> </div></div> > - <div class="history_graph"><div id="memory_history_30"> </div></div> > - <div class="history_graph"><div id="load_history_1"> </div></div> > - <div class="history_graph"><div id="load_history_7"> </div></div> > - <div class="history_graph"><div id="load_history_30"> </div></div> > - </div> > -</div> > -I like how much shorter this is! However, not so keen on having the path hardcoded like this. I did a little poking, and think you might want to add a helper to application_helper.rb like so: def flash_path(source) compute_public_path(source, 'swfs', 'swf') end 'swfs' is a directory under /public. I think we should have all the flash swfs (even if it is only this one) in a dir rather than top level, doesn't have to be called swfs, could be something else.> -<script type="text/javascript"> > - swap_history_graph(null, null); // display 1st graph > +$(document).ready(function(){This div needs a more appropriate name that describes what it contains.> +$('#the-div-name').flash( > + { > + src: '/ovirt/flexchart.swf',This line can be changed to: src: '<%=flash_path("flexchart")%>',> + width: 720, > + height: 300, > + flashvars: { flexchart_data: '/ovirt/graph/flexchart_data/<%= @id %>/memory/1' }Similarly, please do this one as: flashvars: { flexchart_data: "<%= url_for :controller =>'/graph', :action => 'flexchart_data' %>/<%= @id %>/memory/1" }> + }, > + { version: 9 } > + ); > +}); > </script> > diff --git a/src/config/routes.rb b/src/config/routes.rb > index 6f8e481..8d538cb 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/:days', :controller => 'graph', :action => 'flexchart_data' > map.connect ':controller/:action/:id.:format' > map.connect ':controller/:action/:id' > > diff --git a/src/flexchart/README.txt b/src/flexchart/README.txt > new file mode 100644 > index 0000000..66eb183 > --- /dev/null > +++ b/src/flexchart/README.txt > @@ -0,0 +1,8 @@ > +Until mxmlc gets packaged and this becomes part of autobuild, > +you must obtain the open flex SDK to build the swf movie. > + > +Once you have mxmlc on your system, run: > + > +mxmlc flexchart.mxml > + > +in this directory, and copy the resulting file flexchart.swf to /usr/share/ovirt-server/public on your appliance.<snip adobe code> Don't know AS well enough to comment on quality of code too much, mostly seems reasonable as a first cut.> diff --git a/src/flexchart/flexchart.mxml b/src/flexchart/flexchart.mxml > new file mode 100644 > index 0000000..796329d > --- /dev/null > +++ b/src/flexchart/flexchart.mxml > @@ -0,0 +1,20 @@ > +<?xml version="1.0"?> > +<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="populate(flexChart)"> > + <mx:Script> > + <![CDATA[ > + > + import mx.containers.Box; > + import org.ovirt.*; > + > + private function populate(chart:Box):void { > + var chartLoader:ChartLoader = new ChartLoader(chart, parameters['flexchart_data']); > + chartLoader.load(); > + } > + > + ]]> > + </mx:Script> > + <mx:Panel height="100%" width="100%" visible="true"> > + <mx:HBox id="flexChart" height="100%" width="100%" visible="true" verticalAlign="bottom" opaqueBackground="0xFFFFFF" borderThickness="0"> > + </mx:HBox> > + </mx:Panel> > +</mx:Application> > diff --git a/src/flexchart/org/ovirt/ChartLoader.as b/src/flexchart/org/ovirt/ChartLoader.as > new file mode 100644 > index 0000000..4e493a4 > --- /dev/null > +++ b/src/flexchart/org/ovirt/ChartLoader.as > @@ -0,0 +1,64 @@ > +/* > + 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 { > + > + import mx.containers.Box; > + import mx.containers.HBox; > + import mx.controls.Text; > + > + public class ChartLoader { > + > + private var element:Box; > + private var datasourceUrl:String; > + > + public function ChartLoader(element:Box, datasourceUrl:String) { > + this.element = element; > + this.datasourceUrl = datasourceUrl; > + } > + > + public function addData(dataSeries:DataSeries):void { > + var points:Array = dataSeries.getPoints(); > + var maxValue:Number = dataSeries.getMaxValue(); > + var scale:Number = maxValue; > + if (scale == 0) { scale = 1; } > + var size:int = points.length; > + element.removeAllChildren(); > + element.setStyle("horizontalGap","2");Maybe I am spoiled by ruby and jquery - no .each in AS?> + for (var i:int = 0; i < size; i++) { > + var value:Number = (points[i] as Array)[1]; > + var bar:HBox = new HBox(); > + bar.percentHeight = ((value / scale) * 90); > + bar.percentWidth = (100 / size); > + bar.setStyle("backgroundColor","0x0000FF"); > + bar.setStyle("left","1"); > + bar.setStyle("right","1"); > + bar.visible = true; > + bar.setVisible(true); > + element.addChild(bar); > + } > + } > + > + public function load():void { > + var dataSource:DataSource = new DataSource(this); > + dataSource.retrieveData(datasourceUrl); > + } > + } > +} > \ No newline at end of file > diff --git a/src/flexchart/org/ovirt/DataSeries.as b/src/flexchart/org/ovirt/DataSeries.as > new file mode 100644 > index 0000000..d63162a > --- /dev/null > +++ b/src/flexchart/org/ovirt/DataSeries.as > @@ -0,0 +1,42 @@ > +/* > + 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. > +*/ > + > +//class to encapsulate the json object representation of a data > +//series returned from stats package > + > +package org.ovirt { > + > + public class DataSeries { > + > + private var object:Object; > + > + public function DataSeries (object:Object) { > + this.object = object; > + } > + > + public function getPoints():Array { > + return object["vectors"] as Array; > + } > + > + public function getMaxValue():Number { > + return object["max_value"] as Number; > + } > + } > +} > \ No newline at end of file > diff --git a/src/flexchart/org/ovirt/DataSource.as b/src/flexchart/org/ovirt/DataSource.as > new file mode 100644 > index 0000000..1a64f03 > --- /dev/null > +++ b/src/flexchart/org/ovirt/DataSource.as > @@ -0,0 +1,57 @@ > +/* > + 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 { > + > + import flash.net.URLLoader; > + import flash.net.URLRequest; > + import com.adobe.serialization.json.JSON; > + import flash.events.Event; > + import flash.events.IOErrorEvent; > + > + public class DataSource { > + > + private var chartLoader:ChartLoader; > + > + public function DataSource(chartLoader:ChartLoader) { > + this.chartLoader = chartLoader; > + } > + > + public function retrieveData(url:String):void { > + var loader:URLLoader = new URLLoader(); > + loader.addEventListener( IOErrorEvent.IO_ERROR, this.ioError ); > + loader.addEventListener( Event.COMPLETE, dataLoaded ); > + var request:URLRequest = new URLRequest(url); > + loader.load(request); > + } > + > + private function dataLoaded(event:Event):void { > + var loader:URLLoader = URLLoader(event.target); > + var object:Object = JSON.decode(loader.data); > + var series:DataSeries = new DataSeries(object); > + chartLoader.addData(series); > + } > + > + private function ioError( e:IOErrorEvent ):void { > + //FIXME: > + //do something useful with this error > + } > + } > +}<snip new jq plugin>