[Ovirt-devel] Re: [PATCH server] Add to summary pages a rudimentary flash chart written in flex framework
Jason Guiditta
jguiditt at redhat.com
Fri Oct 10 20:56:55 UTC 2008
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>
More information about the ovirt-devel
mailing list