gerbilcharts 0.2.13 → 0.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. data/History.txt +35 -0
  2. data/Manifest.txt +8 -0
  3. data/README.txt +8 -8
  4. data/lib/gerbilcharts/charts.rb +3 -0
  5. data/lib/gerbilcharts/charts/bubble_chart.rb +30 -0
  6. data/lib/gerbilcharts/charts/conversation_ring.rb +31 -0
  7. data/lib/gerbilcharts/charts/line_chart.rb +1 -0
  8. data/lib/gerbilcharts/charts/matrix_chart.rb +32 -0
  9. data/lib/gerbilcharts/charts/square_line_chart.rb +7 -0
  10. data/lib/gerbilcharts/models.rb +1 -0
  11. data/lib/gerbilcharts/models/bucketized_timeseries_graph_model.rb +1 -1
  12. data/lib/gerbilcharts/models/graph_model_group.rb +19 -2
  13. data/lib/gerbilcharts/models/matrix_model.rb +85 -0
  14. data/lib/gerbilcharts/models/monotonous_graph_model.rb +5 -1
  15. data/lib/gerbilcharts/models/presets.rb +18 -11
  16. data/lib/gerbilcharts/models/raw_range.rb +1 -1
  17. data/lib/gerbilcharts/models/round_range.rb +3 -1
  18. data/lib/gerbilcharts/models/round_time_range.rb +19 -29
  19. data/lib/gerbilcharts/models/sampled_timeseries_graph_model.rb +1 -1
  20. data/lib/gerbilcharts/public/brushmetal.css +123 -74
  21. data/lib/gerbilcharts/public/gerbil.js +104 -64
  22. data/lib/gerbilcharts/surfaces.rb +3 -0
  23. data/lib/gerbilcharts/surfaces/bubble_surface.rb +105 -0
  24. data/lib/gerbilcharts/surfaces/chart.rb +4 -2
  25. data/lib/gerbilcharts/surfaces/conversation_ring.rb +64 -0
  26. data/lib/gerbilcharts/surfaces/detailed_legend.rb +35 -13
  27. data/lib/gerbilcharts/surfaces/graph_element.rb +8 -1
  28. data/lib/gerbilcharts/surfaces/grid.rb +8 -2
  29. data/lib/gerbilcharts/surfaces/horizontal_axis.rb +12 -3
  30. data/lib/gerbilcharts/surfaces/horizontal_time_axis.rb +5 -4
  31. data/lib/gerbilcharts/surfaces/impulse_surface.rb +1 -1
  32. data/lib/gerbilcharts/surfaces/legend.rb +43 -6
  33. data/lib/gerbilcharts/surfaces/mark_band.rb +17 -1
  34. data/lib/gerbilcharts/surfaces/matrix_surface.rb +87 -0
  35. data/lib/gerbilcharts/surfaces/pie_surface.rb +109 -96
  36. data/lib/gerbilcharts/surfaces/rect.rb +18 -0
  37. data/lib/gerbilcharts/surfaces/stacked_area_surface.rb +42 -2
  38. data/lib/gerbilcharts/surfaces/stacked_grid.rb +1 -3
  39. data/lib/gerbilcharts/surfaces/title_panel.rb +7 -2
  40. data/lib/gerbilcharts/surfaces/tracker.rb +4 -1
  41. data/lib/gerbilcharts/surfaces/vertical_axis.rb +2 -2
  42. data/lib/gerbilcharts/svgdc.rb +1 -0
  43. data/lib/gerbilcharts/svgdc/css_inliner.rb +2 -2
  44. data/lib/gerbilcharts/svgdc/svg_ellipse.rb +21 -0
  45. data/lib/gerbilcharts/svgdc/svg_polygon.rb +19 -0
  46. data/lib/gerbilcharts/svgdc/svgdc.rb +4 -1
  47. data/lib/gerbilcharts/version.rb +2 -3
  48. data/test/test_bar.rb +1 -1
  49. data/test/test_bubble.rb +52 -0
  50. data/test/test_conversation.rb +34 -0
  51. data/test/test_lines.rb +3 -3
  52. data/test/test_matrix.rb +34 -0
  53. data/test/test_noob.rb +9 -6
  54. data/test/test_pie.rb +2 -2
  55. data/test/test_ranges.rb +15 -2
  56. data/test/test_sa.rb +88 -0
  57. metadata +14 -2
@@ -27,6 +27,13 @@ class Rect
27
27
  @top += t
28
28
  @bottom -= b
29
29
  end
30
+
31
+ def deflate(n)
32
+ @left+=n
33
+ @top+=n
34
+ @bottom-=n
35
+ @right-=n
36
+ end
30
37
 
31
38
  def initfrom(r)
32
39
  @left=r.left
@@ -74,6 +81,17 @@ class Rect
74
81
  def height
75
82
  @bottom-@top
76
83
  end
84
+
85
+ def translate_x(newx)
86
+ w=width
87
+ @left=newx
88
+ @right=@left+w
89
+ end
90
+
91
+ def offset_x(delta)
92
+ @left+=delta
93
+ @right+=delta
94
+ end
77
95
 
78
96
 
79
97
  def to_s
@@ -9,22 +9,61 @@ class StackedAreaSurface < Surface
9
9
  end
10
10
 
11
11
  def int_render(g)
12
- rx = parent.modelgroup.effective_round_range_x
12
+ range_options_x = parent.get_global_option(:scaling_x,:auto)
13
+
14
+ rx = parent.modelgroup.effective_range_x(range_options_x)
13
15
  ry = parent.modelgroup.cumulative_sweep_round_range_y0
14
16
 
15
17
  # ajax if used
16
18
  if parent.usesAjax?
17
19
  set_ajaxSurfaceContext(rx.rmax,ry.rmax,"SA")
18
20
  end
21
+
22
+
23
+ # draw reference if any
24
+ if parent.modelgroup.has_ref_model?
25
+ ref_model = parent.modelgroup.ref_model
26
+
27
+ y_zero = scale_y 0,ry
28
+ g.begin_polygon
29
+ firstpoint=true
30
+ xpos,ypos=0,0
31
+ last_x=nil
32
+ ref_model.each_tuple do |x,y|
33
+
34
+ xpos = scale_x x,rx
35
+ ypos = scale_y y,ry
36
+
37
+ if firstpoint
38
+ g.polygon_point xpos,y_zero
39
+ firstpoint=false
40
+ end
41
+
42
+ if last_x && (x-last_x) > parent.modelgroup.sweep_interval
43
+ g.polygon_point scale_x(last_x,rx) ,y_zero
44
+ g.polygon_point scale_x(x,rx) ,y_zero
45
+ end
46
+ last_x=x
47
+
48
+ g.polygon_point xpos,ypos
49
+ end
50
+ g.polygon_point xpos,y_zero
51
+ g.end_polygon(:id => "ref_mod")
52
+ end
19
53
 
20
54
  # prepare models for sweeping
21
55
  sweep_pos= rx.rmin
22
56
  sweep_to = rx.rmax
23
57
  polygons = []
24
58
  modnames = []
59
+
60
+ klass_poly = parent.get_global_option(:squarize,false) ?
61
+ GerbilCharts::SVGDC::SVGSquarizedPolygon :
62
+ GerbilCharts::SVGDC::SVGPolygon
63
+
25
64
  parent.modelgroup.each_model do | mod|
26
65
  mod.begin_sweep
27
- polygons << GerbilCharts::SVGDC::SVGPolygon.new
66
+ polygons << klass_poly.new
28
67
  modnames << mod.name
29
68
  end
30
69
 
@@ -36,6 +75,7 @@ class StackedAreaSurface < Surface
36
75
  acc_y = 0
37
76
  parent.modelgroup.each_model_with_index do | mod, i|
38
77
  acc_y += mod.sweep(sweep_pos)
78
+
39
79
  xpos = scale_x sweep_pos,rx
40
80
  ypos = scale_y acc_y,ry
41
81
 
@@ -5,11 +5,9 @@ module GerbilCharts::Surfaces
5
5
  class StackedGrid < Grid
6
6
 
7
7
  protected
8
-
9
8
  def grid_range_y
10
- return parent.modelgroup.cumulative_round_range_y0
9
+ return parent.modelgroup.cumulative_sweep_round_range_y0
11
10
  end
12
-
13
11
  end
14
12
 
15
13
  end
@@ -21,10 +21,15 @@ class TitlePanel < Panel
21
21
  if @just == :left
22
22
  g.textout(@bounds.left, @bounds.top+16, parent.modelgroup.name, opts)
23
23
  else
24
- opts.store( "text-anchor", "end" )
25
- g.textout(@bounds.right, @bounds.top+16, parent.modelgroup.name, opts)
24
+ g.textout(@bounds.right, @bounds.top+16, parent.modelgroup.name, opts.merge("text-anchor" => "end"))
26
25
  end
27
26
 
27
+ xoff=0
28
+ parent.get_global_option(:toolhrefs,[]).each do |tool|
29
+ topts = {:class => "titletool"}
30
+ g.textout(@bounds.left + xoff, @bounds.top+29, tool[0] , topts.merge(:href => tool[1]) )
31
+ xoff =xoff+ 7*(tool[0].length )
32
+ end
28
33
  end
29
34
 
30
35
  def align_to_anchor(anc)
@@ -20,10 +20,12 @@ class Tracker < GraphElement
20
20
  # render the elements directly
21
21
  def render_direct(xfrag)
22
22
 
23
+
23
24
  # calculate scaling factors
24
25
  range_options_x = parent.get_global_option(:scaling_x,:auto)
25
26
  rx = parent.modelgroup.effective_range_x(range_options_x)
26
27
 
28
+ return unless rx.rmin.is_a? Time
27
29
 
28
30
  xfrag.g(:id=> 'gtrackerrect', :visibility=>'hidden') {
29
31
  xfrag.rect(:id=>"trackerrect", :class=>"trackerrect",
@@ -52,12 +54,13 @@ class Tracker < GraphElement
52
54
  }
53
55
  }
54
56
 
55
- raise "Time Tracker expects X-Axis to be a time interval " unless rx.rmin.is_a? Time
56
57
 
57
58
  xfrag.g(:id=>"gtrackerdata", :visibility=>'hidden',
58
59
  :gerb_fromts=>rx.rmin.tv_sec,
59
60
  :gerb_seconds=>rx.rmax.tv_sec-rx.rmin.tv_sec,
60
61
  :gerb_scale =>(rx.delta)/parent.anchor.bounds.width,
62
+ :gerb_tzoffset => Time.new.utc_offset,
63
+ :gerb_tzname => Time.new.zone,
61
64
  :gerb_selts=>1,
62
65
  :gerb_selsecs=>1)
63
66
 
@@ -34,13 +34,13 @@ class VerticalAxis < Axis
34
34
 
35
35
  ry.each_label do |val,label|
36
36
  yp = scale_y val,ry
37
- yp_label=yp
37
+ yp_label=yp + 4
38
38
 
39
39
  # make sure the edge ones are visible
40
40
  yp_label = max(yp_label,@bounds.top+10)
41
41
 
42
42
  g.textout(@bounds.right-4, yp_label, label, {:class => "axislabel", "text-anchor" => "end"})
43
- g.line(@bounds.right-3,yp,@bounds.right+2,yp, {:class => "axistickmajor"})
43
+ g.line(@bounds.right-1,yp,@bounds.right+1,yp, {:class => "axistickmajor"})
44
44
  end
45
45
 
46
46
  end
@@ -23,3 +23,4 @@ require 'gerbilcharts/svgdc/svg_text'
23
23
  require 'gerbilcharts/svgdc/svg_arc'
24
24
  require 'gerbilcharts/svgdc/svg_arc'
25
25
  require 'gerbilcharts/svgdc/css_inliner'
26
+ require 'gerbilcharts/svgdc/svg_ellipse'
@@ -22,8 +22,8 @@ class CssInliner
22
22
  parts.each { |p| p.strip!; p.squeeze!(' ');}
23
23
 
24
24
  case parts[0][0]
25
- when 35; @selector_table.store(parts[0][1..-1],parts[1])
26
- when 46; @class_table.store(parts[0][1..-1],parts[1])
25
+ when '#'; @selector_table.store(parts[0][1..-1],parts[1])
26
+ when '.'; @class_table.store(parts[0][1..-1],parts[1])
27
27
  end
28
28
  end
29
29
  end
@@ -0,0 +1,21 @@
1
+ module GerbilCharts::SVGDC
2
+
3
+ # = SVGEllpise
4
+ # Draws a circle at x, y, and radius rx, ry
5
+ #
6
+ class SVGEllipse < SVGShape
7
+
8
+ attr_accessor :x,:y,:rx,:ry
9
+
10
+ def initialize(x,y,rx,ry)
11
+ @x,@y,@rx,@ry=x,y,rx,ry
12
+ super()
13
+ end
14
+
15
+ def render(xfrag)
16
+ h = { :cx => @x, :cy => @y, :rx => @rx, :ry => @ry }
17
+ xfrag.ellipse(h.merge(render_attributes))
18
+ end
19
+ end
20
+
21
+ end
@@ -31,4 +31,23 @@ class SVGPolygon < SVGShape
31
31
 
32
32
  end
33
33
 
34
+
35
+ class SVGSquarizedPolygon < SVGPolygon
36
+
37
+
38
+ def addpoint(x,y)
39
+ @lastpt ||= []
40
+
41
+ unless @lastpt.empty?
42
+ xf,yf = "%.2f"%x, "%.2f"%@lastpt[1]
43
+ @operstring << "#{xf},#{yf} "
44
+ xf,yf = "%.2f"%x, "%.2f"%y
45
+ @operstring << "#{xf},#{yf} "
46
+ end
47
+
48
+ @lastpt = [x,y]
49
+ end
50
+
51
+ end
52
+
34
53
  end
@@ -204,7 +204,7 @@ class SVGDC
204
204
  # render tooltips optional
205
205
  if @use_tooltips
206
206
  doc.g(:id=>'ToolTip', :opacity=>'0.8', :visibility=>'hidden', "pointer-events"=>'none') {
207
- doc.rect(:id=>'tipbox', :x=>'0', :y=>'5', :width=>'88', :height=> '20', :rx=> '2', :ry=> '2', :fill=>'white', :stroke=>'black')
207
+ doc.rect(:id=>'tipbox', :x=>'0', :y=>'5', :width=>'88', :height=> '40', :rx=> '2', :ry=> '2', :fill=>'white', :stroke=>'black')
208
208
  doc.text(:id=>'tipText', :x=>'5', :y=>'20', "font-family"=> 'Arial', "font-size"=>'12') {
209
209
  doc.tspan(:id=>'tipTitle', :x=>'5', "font-weight"=>'bold') {
210
210
  doc.cdata!("")
@@ -350,6 +350,9 @@ class SVGDC
350
350
  rectangle(r.left, r.top, r.width, r.height, opt)
351
351
  end
352
352
 
353
+ def circle_r(r, opt={} )
354
+ circle( (r.left + r.right)/2, (r.bottom+r.top)/2, [r.width,r.height].min/2, opt)
355
+ end
353
356
 
354
357
  def select_presentation(opt={})
355
358
  @curr_presentation_context.store(:pen,opt[:pen]) if opt[:pen]
@@ -1,9 +1,8 @@
1
1
  module GerbilCharts
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 2
5
- TINY = 13
6
-
4
+ MINOR = 5
5
+ TINY = 9
7
6
  STRING = [MAJOR, MINOR, TINY].join('.')
8
7
  end
9
8
  end
data/test/test_bar.rb CHANGED
@@ -26,7 +26,7 @@ class TestBar < Test::Unit::TestCase
26
26
  ["VIVEK", 145,112, 22, 45, 18, 170]
27
27
  ]
28
28
  )
29
- mychart = GerbilCharts::Charts::BarChart.new( :width => 550, :height => 200,
29
+ mychart = GerbilCharts::Charts::BarChart.new( :width => 300, :height => 200,
30
30
  :style => 'inline:brushmetal.css', :auto_tooltips => true ,
31
31
  :javascripts => ['/tmp/gerbil.js', '/tmp/prototype.js'],
32
32
  :filter => 'LikeButton'
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ require 'trafgen'
4
+
5
+ # tests some easy to use mods
6
+ # newbies enter here
7
+ class TestChartsNoob < Test::Unit::TestCase
8
+
9
+
10
+ # alerts
11
+ def test_alerts
12
+
13
+ mychart = GerbilCharts::Charts::BubbleChart.new( :width => 650, :height => 200,
14
+ :style => 'inline:brushmetal.css',
15
+ :javascripts => ['inline:/tmp/gerbil.js','inline:/tmp/prototype.js'] )
16
+
17
+
18
+ tend = Time.now
19
+ tbegin = tend - 3600*24
20
+
21
+ model1 = GerbilCharts::Models::BucketizedTimeSeriesGraphModel.new( "eth0", 3600 )
22
+ TimeSeriesDataGenerator.new(tbegin,tend,300,1000, 5000 ).each_tuple do |t,v|
23
+ model1.add(t,v)
24
+ end
25
+
26
+ model2 = GerbilCharts::Models::BucketizedTimeSeriesGraphModel.new( "wan1", 3600 )
27
+ TimeSeriesDataGenerator.new(tbegin,tend,300,2, 20).each_tuple do |t,v|
28
+ model2.add(t,v)
29
+ end
30
+
31
+ model3 = GerbilCharts::Models::BucketizedTimeSeriesGraphModel.new( "fiber0", 3600 )
32
+ TimeSeriesDataGenerator.new(tbegin,tend,1800,0, 10).each_tuple do |t,v|
33
+ model3.add(t,v)
34
+ end
35
+
36
+ modelgroup = GerbilCharts::Models::GraphModelGroup.new( "External Traffic")
37
+ modelgroup.add model2
38
+ modelgroup.add model1
39
+ modelgroup.add model3
40
+
41
+
42
+ mychart.modelgroup=modelgroup
43
+
44
+ mychart.render('/tmp/n_bubble.svg')
45
+
46
+ end
47
+
48
+
49
+
50
+ end
51
+
52
+
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ require 'trafgen'
4
+
5
+ # test conversation bar chart
6
+ class TestConversation < Test::Unit::TestCase
7
+
8
+ def test_monthly_sales
9
+ modelgroup = GerbilCharts::Models::MatrixModel.new("Conversation Ring Chart")
10
+
11
+ modelgroup.add("192.168.1.25","192.168.1.20",500)
12
+ modelgroup.add("192.168.1.25","192.168.1.21",7500)
13
+ modelgroup.add("192.168.1.25","192.168.1.22",6500)
14
+ modelgroup.add("192.168.1.25","192.168.1.23",5500)
15
+ modelgroup.add("192.168.1.25","192.168.1.24",4500)
16
+ modelgroup.add("192.168.1.25","192.168.1.26",10500)
17
+ modelgroup.add("192.168.1.25","192.168.1.27",500)
18
+ modelgroup.add("192.168.1.25","192.168.1.28",2500)
19
+ modelgroup.add("192.168.1.25","192.168.1.29",3500)
20
+ modelgroup.add("192.168.1.25","192.168.1.30",500)
21
+
22
+ modelgroup.calc_matrix
23
+
24
+ mychart = GerbilCharts::Charts::ConversationRing.new( :width => 450, :height => 250,
25
+ :style => 'inline:brushmetal.css', :auto_tooltips => true ,
26
+ :javascripts => ['/tmp/gerbil.js', '/tmp/prototype.js'],
27
+ :filter => 'LikeButton'
28
+ )
29
+
30
+ mychart.modelgroup = modelgroup
31
+ mychart.render('/tmp/con_monthly_sales.svg')
32
+
33
+ end
34
+ end
data/test/test_lines.rb CHANGED
@@ -35,9 +35,9 @@ class TestLines < Test::Unit::TestCase
35
35
  # test a line chart
36
36
  def test_line_1
37
37
 
38
- mychart = GerbilCharts::Charts::AreaChart.new( :width => 450, :height => 250,
39
- :javascripts => ['inline:/tmp/gerbil.js' ],
40
- :auto_tooltips => true, :style => 'inline:brushmetal.css')
38
+ mychart = GerbilCharts::Charts::AreaChart.new( :width => 750, :height => 250,
39
+ :javascripts => ['inline:gerbil.js' ],
40
+ :auto_tooltips => false, :style => 'inline:brushmetal.css' )
41
41
  mychart.setmodelgroup(@modgroup)
42
42
  mychart.render('/tmp/sq_linechart1.svg')
43
43
 
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ require 'trafgen'
4
+
5
+ # test conversation bar chart
6
+ class TestMatrix < Test::Unit::TestCase
7
+
8
+ def test_monthly_sales
9
+ modelgroup = GerbilCharts::Models::MatrixModel.new("Traffic Matrix")
10
+
11
+ modelgroup.add("192.168.1.25","192.168.1.20",12500)
12
+ modelgroup.add("192.168.1.26","192.168.1.21",7500)
13
+ modelgroup.add("192.168.1.25","192.168.1.22",6500)
14
+ modelgroup.add("192.168.1.27","192.168.1.23",5500)
15
+ modelgroup.add("192.168.1.28","192.168.1.24",4500)
16
+ modelgroup.add("192.168.1.29","192.168.1.26",10500)
17
+ modelgroup.add("192.168.1.25","192.168.1.27",500)
18
+ modelgroup.add("192.168.1.25","192.168.1.28",2500)
19
+ modelgroup.add("192.168.1.30","192.168.1.29",3500)
20
+ modelgroup.add("192.168.1.52","192.168.1.30",500)
21
+
22
+ modelgroup.calc_matrix
23
+
24
+ mychart = GerbilCharts::Charts::MatrixChart.new( :width => 1000, :height => 400,
25
+ :style => 'inline:brushmetal.css', :auto_tooltips => true ,
26
+ :javascripts => ['/tmp/gerbil.js', '/tmp/prototype.js'],
27
+ :filter => 'LikeButton'
28
+ )
29
+
30
+ mychart.modelgroup = modelgroup
31
+ mychart.render('/tmp/matrix_monthly_sales.svg')
32
+
33
+ end
34
+ end
data/test/test_noob.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/test_helper.rb'
2
2
 
3
- require 'trafgen'
3
+ require './trafgen'
4
4
 
5
5
  # tests some easy to use mods
6
6
  # newbies enter here
@@ -11,15 +11,16 @@ class TestChartsNoob < Test::Unit::TestCase
11
11
  # use a simple timeseries model
12
12
  def test_monthly_sales
13
13
 
14
- mychart = GerbilCharts::Charts::LineChart.new( :width => 350, :height => 200, :style => 'brushmetal.css',
15
- :circle_data_points => true )
14
+ mychart = GerbilCharts::Charts::SquareLineChart.new( :width => 350, :height => 200, :style => 'inline:brushmetal.css',
15
+ :circle_data_points => true, :scaling_y => :auto,
16
+ :javascripts => ['/tmp/gerbil.js','/tmp/prototype.js'] )
16
17
 
17
18
  modelgroup = GerbilCharts::Models::SimpleTimeSeriesModelGroup.new(
18
19
  :title => "Sales figures",
19
20
  :timeseries => (1..6).collect { |month| Time.local(2008,month) },
20
21
  :models => [ ["Bruce", 1, 10, 18, 28, 80, 122],
21
- ["Rex" , 112,22, 45, 70, 218, 309],
22
- ["Buzo" , 0, 23, 25, 40, 18, 59]
22
+ ["Rex" , 112,22, 45, 70, 218, 309],
23
+ ["Buzo" , 0, 23, 25, 40, 18, 59]
23
24
  ]
24
25
  )
25
26
 
@@ -69,7 +70,9 @@ class TestChartsNoob < Test::Unit::TestCase
69
70
 
70
71
 
71
72
  # demo stacked area
72
- mysachart = GerbilCharts::Charts::StackedAreaChart.new( :width => 450, :height => 200, :style => 'brushmetal.css')
73
+ mysachart = GerbilCharts::Charts::StackedAreaChart.new( :width => 450, :height => 200, :style => 'inline:brushmetal.css',
74
+ :scaling_y => :auto, :auto_tooltips => true ,
75
+ :javascripts => ['/tmp/gerbil.js' , '/tmp/prototype.js' ])
73
76
  mysachart.modelgroup=modelgroup
74
77
  mysachart.render('/tmp/n_daily_traffic_sa.svg')
75
78