pulse-meter 0.1.11 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -18
- data/Rakefile +1 -1
- data/examples/basic.ru +20 -12
- data/examples/full/server.ru +9 -18
- data/examples/minimal/client.rb +2 -1
- data/lib/pulse-meter/version.rb +1 -1
- data/lib/pulse-meter/visualize/dsl/layout.rb +11 -10
- data/lib/pulse-meter/visualize/dsl/page.rb +15 -6
- data/lib/pulse-meter/visualize/layout.rb +4 -7
- data/lib/pulse-meter/visualize/page.rb +2 -2
- data/lib/pulse-meter/visualize/public/js/application.coffee +109 -67
- data/lib/pulse-meter/visualize/public/js/application.js +114 -69
- data/lib/pulse-meter/visualize/sensor.rb +11 -7
- data/lib/pulse-meter/visualize/series_extractor.rb +4 -4
- data/lib/pulse-meter/visualize/views/main.haml +16 -4
- data/lib/pulse-meter/visualize/widget.rb +64 -8
- data/spec/pulse_meter/visualize/dsl/layout_spec.rb +7 -7
- data/spec/pulse_meter/visualize/dsl/page_spec.rb +2 -2
- data/spec/pulse_meter/visualize/layout_spec.rb +7 -8
- data/spec/pulse_meter/visualize/page_spec.rb +55 -85
- data/spec/pulse_meter/visualize/sensor_spec.rb +17 -17
- data/spec/pulse_meter/visualize/series_extractor_spec.rb +3 -3
- data/spec/pulse_meter/visualize/widget_spec.rb +26 -35
- data/spec/pulse_meter/visualizer_spec.rb +2 -2
- metadata +3 -4
- data/lib/pulse-meter/visualize/public/js/highcharts.js +0 -203
data/README.md
CHANGED
@@ -394,20 +394,15 @@ A more complicated visualization
|
|
394
394
|
# Use local time for x-axis of charts
|
395
395
|
l.use_utc false
|
396
396
|
|
397
|
-
#
|
398
|
-
l.
|
399
|
-
|
400
|
-
# Transfer some global parameters to Highcharts
|
401
|
-
l.highchart_options({
|
402
|
-
tooltip: {
|
403
|
-
value_decimals: 2
|
404
|
-
}
|
397
|
+
# Transfer some global parameters to Google Charts
|
398
|
+
l.gchart_options({
|
399
|
+
backgroundColor: '#CCC'
|
405
400
|
})
|
406
401
|
|
407
402
|
# Add some pages
|
408
403
|
l.page "Request count" do |p|
|
409
404
|
|
410
|
-
# Add chart (of
|
405
|
+
# Add chart (of Google Charts `area' style, `pie' and `line' are also available)
|
411
406
|
p.area "Requests per minute" do |w|
|
412
407
|
|
413
408
|
# Plot :requests_per_minute values on this chart with black color
|
@@ -431,11 +426,9 @@ A more complicated visualization
|
|
431
426
|
# Occupy half (5/10) of the page (horizontally)
|
432
427
|
w.width 5
|
433
428
|
|
434
|
-
# Transfer page-wide (and page-specific) options to
|
435
|
-
p.
|
436
|
-
|
437
|
-
height: 300
|
438
|
-
}
|
429
|
+
# Transfer page-wide (and page-specific) options to Google Charts
|
430
|
+
p.gchart_options({
|
431
|
+
height: 400
|
439
432
|
})
|
440
433
|
end
|
441
434
|
|
@@ -484,10 +477,8 @@ A more complicated visualization
|
|
484
477
|
|
485
478
|
end
|
486
479
|
|
487
|
-
p.
|
488
|
-
|
489
|
-
height: 500
|
490
|
-
}
|
480
|
+
p.ghchart_options({
|
481
|
+
height: 500
|
491
482
|
})
|
492
483
|
end
|
493
484
|
|
data/Rakefile
CHANGED
data/examples/basic.ru
CHANGED
@@ -10,7 +10,8 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
10
10
|
|
11
11
|
l.page "Counts" do |p|
|
12
12
|
|
13
|
-
p.
|
13
|
+
p.line "Lama count" do |c|
|
14
|
+
c.sensor :lama_count, color: '#CC1155'
|
14
15
|
c.redraw_interval 5
|
15
16
|
c.values_label 'Count'
|
16
17
|
c.width 5
|
@@ -26,7 +27,19 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
26
27
|
c.timespan 1200
|
27
28
|
end
|
28
29
|
|
29
|
-
p.
|
30
|
+
p.line "Rhino & Lama & Goose count comparison" do |c|
|
31
|
+
c.redraw_interval 5
|
32
|
+
c.values_label 'Count'
|
33
|
+
c.width 5
|
34
|
+
c.show_last_point true
|
35
|
+
c.timespan 1200
|
36
|
+
|
37
|
+
c.sensor :rhino_count, color: '#AAAAAA'
|
38
|
+
c.sensor :lama_count, color: '#CC1155'
|
39
|
+
c.sensor :goose_count
|
40
|
+
end
|
41
|
+
|
42
|
+
p.table "Rhino & Lama & Goose count comparison" do |c|
|
30
43
|
c.redraw_interval 5
|
31
44
|
c.values_label 'Count'
|
32
45
|
c.width 5
|
@@ -92,19 +105,14 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
92
105
|
c.sensor :rhino_average_age
|
93
106
|
end
|
94
107
|
|
95
|
-
p.
|
96
|
-
|
97
|
-
height: 300
|
98
|
-
}
|
108
|
+
p.gchart_options({
|
109
|
+
background_color: '#CCC'
|
99
110
|
})
|
100
111
|
end
|
101
112
|
|
102
|
-
l.use_utc
|
103
|
-
l.
|
104
|
-
|
105
|
-
tooltip: {
|
106
|
-
value_decimals: 2
|
107
|
-
}
|
113
|
+
l.use_utc true
|
114
|
+
l.gchart_options({
|
115
|
+
height: 300
|
108
116
|
})
|
109
117
|
end
|
110
118
|
|
data/examples/full/server.ru
CHANGED
@@ -12,20 +12,15 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
12
12
|
# Use local time for x-axis of charts
|
13
13
|
l.use_utc false
|
14
14
|
|
15
|
-
#
|
16
|
-
l.
|
17
|
-
|
18
|
-
# Transfer some global parameters to Highcharts
|
19
|
-
l.highchart_options({
|
20
|
-
tooltip: {
|
21
|
-
value_decimals: 2
|
22
|
-
}
|
15
|
+
# Transfer some global parameters to Google Charts
|
16
|
+
l.gchart_options({
|
17
|
+
backgroundColor: '#CCC'
|
23
18
|
})
|
24
19
|
|
25
20
|
# Add some pages
|
26
21
|
l.page "Request count" do |p|
|
27
22
|
|
28
|
-
# Add chart (of
|
23
|
+
# Add chart (of Google Charts `area' style, `pie' and `line' are also available)
|
29
24
|
p.area "Requests per minute" do |w|
|
30
25
|
|
31
26
|
# Plot :requests_per_minute values on this chart with black color
|
@@ -49,11 +44,9 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
49
44
|
# Occupy half (5/10) of the page (horizontally)
|
50
45
|
w.width 5
|
51
46
|
|
52
|
-
# Transfer page-wide (and page-specific) options to
|
53
|
-
p.
|
54
|
-
|
55
|
-
height: 300
|
56
|
-
}
|
47
|
+
# Transfer page-wide (and page-specific) options to Google Charts
|
48
|
+
p.gchart_options({
|
49
|
+
height: 500
|
57
50
|
})
|
58
51
|
end
|
59
52
|
|
@@ -102,10 +95,8 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
102
95
|
|
103
96
|
end
|
104
97
|
|
105
|
-
p.
|
106
|
-
|
107
|
-
height: 500
|
108
|
-
}
|
98
|
+
p.gchart_options({
|
99
|
+
height: 500
|
109
100
|
})
|
110
101
|
end
|
111
102
|
|
data/examples/minimal/client.rb
CHANGED
data/lib/pulse-meter/version.rb
CHANGED
@@ -3,15 +3,13 @@ module PulseMeter
|
|
3
3
|
module DSL
|
4
4
|
class Layout
|
5
5
|
DEFAULT_TITLE = "Pulse Meter"
|
6
|
-
|
7
|
-
DEFAULT_HIGHCHART_OPTIONS = {}
|
6
|
+
DEFAULT_GCHART_OPTIONS = {}
|
8
7
|
|
9
8
|
def initialize
|
10
9
|
@pages = []
|
11
10
|
@title = DEFAULT_TITLE
|
12
11
|
@use_utc = true
|
13
|
-
@
|
14
|
-
@highchart_options = DEFAULT_HIGHCHART_OPTIONS.dup
|
12
|
+
@gchart_options = DEFAULT_GCHART_OPTIONS.dup
|
15
13
|
end
|
16
14
|
|
17
15
|
def title(title)
|
@@ -22,12 +20,16 @@ module PulseMeter
|
|
22
20
|
@use_utc = use
|
23
21
|
end
|
24
22
|
|
25
|
-
def outlier_color(
|
26
|
-
|
23
|
+
def outlier_color(_)
|
24
|
+
STDERR.puts "DEPRECATION: outlier_color DSL helper does not take effect anymore"
|
27
25
|
end
|
28
26
|
|
29
|
-
def highchart_options(
|
30
|
-
|
27
|
+
def highchart_options(_)
|
28
|
+
STDERR.puts "DEPRECATION: highchart_options DSL helper does not take effect anymore, use gchart_options instead"
|
29
|
+
end
|
30
|
+
|
31
|
+
def gchart_options(options = {})
|
32
|
+
@gchart_options.merge!(options)
|
31
33
|
end
|
32
34
|
|
33
35
|
def page(title, &block)
|
@@ -43,8 +45,7 @@ module PulseMeter
|
|
43
45
|
pages: pages,
|
44
46
|
title: title,
|
45
47
|
use_utc: @use_utc,
|
46
|
-
|
47
|
-
highchart_options: @highchart_options
|
48
|
+
gchart_options: @gchart_options
|
48
49
|
} )
|
49
50
|
end
|
50
51
|
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module PulseMeter
|
2
2
|
module Visualize
|
3
3
|
module DSL
|
4
|
-
WIDGETS = %w(pie
|
5
|
-
|
4
|
+
WIDGETS = %w(pie line area table)
|
5
|
+
DEFAULT_GCHART_OPTIONS = {}
|
6
6
|
|
7
7
|
class Page
|
8
8
|
def initialize(title = nil)
|
9
9
|
@title = title || ""
|
10
10
|
@widgets = []
|
11
|
-
@
|
11
|
+
@gchart_options = DEFAULT_GCHART_OPTIONS.dup
|
12
12
|
end
|
13
13
|
|
14
14
|
def widget(type, title = '', widget_args = nil, &block)
|
@@ -26,19 +26,28 @@ module PulseMeter
|
|
26
26
|
EVAL
|
27
27
|
end
|
28
28
|
|
29
|
+
def spline(*args, &block)
|
30
|
+
STDERR.puts "DEPRECATION: spline widget is no longer available. Using `line' as a fallback."
|
31
|
+
line(*args, &block)
|
32
|
+
end
|
33
|
+
|
29
34
|
def title(new_title)
|
30
35
|
@title = new_title || ''
|
31
36
|
end
|
32
37
|
|
33
|
-
def highchart_options(
|
34
|
-
|
38
|
+
def highchart_options(_)
|
39
|
+
STDERR.puts "DEPRECATION: highchart_options DSL helper does not take effect anymore, use gchart_options instead"
|
40
|
+
end
|
41
|
+
|
42
|
+
def gchart_options(options = {})
|
43
|
+
@gchart_options.merge!(options)
|
35
44
|
end
|
36
45
|
|
37
46
|
def to_page
|
38
47
|
args = {
|
39
48
|
title: @title,
|
40
49
|
widgets: @widgets.map(&:to_widget),
|
41
|
-
|
50
|
+
gchart_options: @gchart_options
|
42
51
|
}
|
43
52
|
PulseMeter::Visualize::Page.new(args)
|
44
53
|
end
|
@@ -5,16 +5,14 @@ module PulseMeter
|
|
5
5
|
|
6
6
|
attr_reader :title
|
7
7
|
attr_reader :use_utc
|
8
|
-
attr_reader :
|
9
|
-
attr_reader :highchart_options
|
8
|
+
attr_reader :gchart_options
|
10
9
|
|
11
10
|
def initialize(args)
|
12
11
|
raise ArgumentError unless args.respond_to?('[]')
|
13
12
|
@title = args[:title] or raise ArgumentError, ":title not specified"
|
14
13
|
@pages = args[:pages] or raise ArgumentError, ":pages not specified"
|
15
14
|
@use_utc = args[:use_utc]
|
16
|
-
@
|
17
|
-
@highchart_options = args[:highchart_options]
|
15
|
+
@gchart_options = args[:gchart_options]
|
18
16
|
end
|
19
17
|
|
20
18
|
def to_app
|
@@ -27,7 +25,7 @@ module PulseMeter
|
|
27
25
|
res << {
|
28
26
|
id: i + 1,
|
29
27
|
title: p.title,
|
30
|
-
|
28
|
+
gchart_options: p.gchart_options
|
31
29
|
}
|
32
30
|
end
|
33
31
|
res
|
@@ -36,8 +34,7 @@ module PulseMeter
|
|
36
34
|
def options
|
37
35
|
{
|
38
36
|
use_utc: @use_utc,
|
39
|
-
|
40
|
-
highchart_options: @highchart_options
|
37
|
+
gchart_options: @gchart_options
|
41
38
|
}
|
42
39
|
end
|
43
40
|
|
@@ -3,13 +3,13 @@ module PulseMeter
|
|
3
3
|
class Page
|
4
4
|
attr_reader :widgets
|
5
5
|
attr_reader :title
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :gchart_options
|
7
7
|
|
8
8
|
def initialize(args)
|
9
9
|
raise ArgumentError unless args.respond_to?('[]')
|
10
10
|
@title = args[:title] or raise ArgumentError, ":title not specified"
|
11
11
|
@widgets = args[:widgets] or raise ArgumentError, ":widgets not specified"
|
12
|
-
@
|
12
|
+
@gchart_options = args[:gchart_options] or raise ArgumentError, ":gchart_options not specified"
|
13
13
|
end
|
14
14
|
|
15
15
|
def widget_data(widget_id, opts = {})
|
@@ -1,12 +1,6 @@
|
|
1
1
|
$ ->
|
2
2
|
globalOptions = gon.options
|
3
3
|
|
4
|
-
Highcharts.setOptions {
|
5
|
-
global: {
|
6
|
-
useUTC: globalOptions.useUtc
|
7
|
-
}
|
8
|
-
}
|
9
|
-
|
10
4
|
PageInfo = Backbone.Model.extend {
|
11
5
|
}
|
12
6
|
|
@@ -29,7 +23,7 @@ $ ->
|
|
29
23
|
|
30
24
|
PageTitleView = Backbone.View.extend {
|
31
25
|
tagName: 'li'
|
32
|
-
|
26
|
+
|
33
27
|
template: _.template('<a href="#/pages/<%= id %>"><%= title %></a>')
|
34
28
|
|
35
29
|
initialize: ->
|
@@ -99,19 +93,20 @@ $ ->
|
|
99
93
|
@setNextFetch()
|
100
94
|
|
101
95
|
cutoffValue: (v, min, max) ->
|
102
|
-
if
|
103
|
-
v
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
96
|
+
if v isnt null
|
97
|
+
if min isnt null && v < min
|
98
|
+
min
|
99
|
+
else if max isnt null && v > max
|
100
|
+
max
|
101
|
+
else
|
102
|
+
v
|
103
|
+
else
|
104
|
+
0
|
108
105
|
|
109
106
|
cutoff: (min, max) ->
|
110
|
-
_.each(@get('series'), (
|
111
|
-
|
112
|
-
|
113
|
-
@cutoffValue(v, min, max)
|
114
|
-
, this)
|
107
|
+
_.each(@get('series').rows, (row) ->
|
108
|
+
for i in [1 .. row.length - 1]
|
109
|
+
row[i] = @cutoffValue(row[i], min, max)
|
115
110
|
, this)
|
116
111
|
|
117
112
|
forceUpdate: ->
|
@@ -119,6 +114,97 @@ $ ->
|
|
119
114
|
success: (model, response) ->
|
120
115
|
model.trigger('redraw')
|
121
116
|
}
|
117
|
+
|
118
|
+
pieData: ->
|
119
|
+
data = new google.visualization.DataTable()
|
120
|
+
data.addColumn('string', 'Title')
|
121
|
+
data.addColumn('number', @get('valuesTitle'))
|
122
|
+
data.addRows(@get('series').data)
|
123
|
+
data
|
124
|
+
|
125
|
+
dateOffset: ->
|
126
|
+
if globalOptions.useUtc
|
127
|
+
(new Date).getTimezoneOffset() * 60000
|
128
|
+
else
|
129
|
+
0
|
130
|
+
|
131
|
+
lineData: ->
|
132
|
+
title = @get('title')
|
133
|
+
data = new google.visualization.DataTable()
|
134
|
+
data.addColumn('datetime', 'Time')
|
135
|
+
dateOffset = @dateOffset()
|
136
|
+
series = @get('series')
|
137
|
+
_.each series.titles, (t) ->
|
138
|
+
data.addColumn('number', t)
|
139
|
+
|
140
|
+
console.log series
|
141
|
+
_.each series.rows, (row) ->
|
142
|
+
row[0] = new Date(row[0] + dateOffset)
|
143
|
+
data.addRow(row)
|
144
|
+
data
|
145
|
+
|
146
|
+
options: ->
|
147
|
+
{
|
148
|
+
title: @get('title')
|
149
|
+
lineWidth: 1
|
150
|
+
chartArea: {
|
151
|
+
left: 40
|
152
|
+
width: '100%'
|
153
|
+
}
|
154
|
+
height: 300
|
155
|
+
legend: {
|
156
|
+
position: 'bottom'
|
157
|
+
}
|
158
|
+
vAxis: {
|
159
|
+
title: @get('valuesTitle')
|
160
|
+
}
|
161
|
+
axisTitlesPosition: 'in'
|
162
|
+
}
|
163
|
+
|
164
|
+
pieOptions: ->
|
165
|
+
$.extend true, @options(), {
|
166
|
+
slices: @get('series').options
|
167
|
+
}
|
168
|
+
|
169
|
+
lineOptions: ->
|
170
|
+
$.extend true, @options(), {
|
171
|
+
hAxis: {
|
172
|
+
format: 'yyyy.MM.dd HH:mm:ss'
|
173
|
+
}
|
174
|
+
series: @get('series').options
|
175
|
+
}
|
176
|
+
|
177
|
+
tableOptions: ->
|
178
|
+
$.extend true, @lineOptions(), {
|
179
|
+
sortColumn: 0
|
180
|
+
sortAscending: false
|
181
|
+
}
|
182
|
+
|
183
|
+
chartOptions: ->
|
184
|
+
opts = if @get('type') == 'pie'
|
185
|
+
@pieOptions()
|
186
|
+
else if @get('type') == 'table'
|
187
|
+
@tableOptions()
|
188
|
+
else
|
189
|
+
@lineOptions()
|
190
|
+
$.extend true, opts, globalOptions.gchartOptions, pageInfos.selected().get('gchartOptions')
|
191
|
+
|
192
|
+
chartData: ->
|
193
|
+
if @get('type') == 'pie'
|
194
|
+
@pieData()
|
195
|
+
else
|
196
|
+
@lineData()
|
197
|
+
|
198
|
+
chartClass: ->
|
199
|
+
if @get('type') == 'pie'
|
200
|
+
google.visualization.PieChart
|
201
|
+
else if @get('type') == 'area'
|
202
|
+
google.visualization.AreaChart
|
203
|
+
else if @get('type') == 'table'
|
204
|
+
google.visualization.Table
|
205
|
+
else
|
206
|
+
google.visualization.LineChart
|
207
|
+
|
122
208
|
}
|
123
209
|
|
124
210
|
WidgetList = Backbone.Collection.extend {
|
@@ -135,58 +221,14 @@ $ ->
|
|
135
221
|
|
136
222
|
updateData: (min, max) ->
|
137
223
|
@model.cutoff(min, max)
|
138
|
-
|
139
|
-
newSeries = @model.get('series')
|
140
|
-
for i in [0 .. chartSeries.length - 1]
|
141
|
-
if newSeries[i]?
|
142
|
-
chartSeries[i].setData(newSeries[i].data, false)
|
143
|
-
@chart.redraw()
|
224
|
+
@chart.draw(@model.chartData(), @model.chartOptions())
|
144
225
|
|
145
226
|
render: ->
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
plotBorderWidth: 1
|
150
|
-
spacingLeft: 0
|
151
|
-
spacingRight: 0
|
152
|
-
type: @model.get('type')
|
153
|
-
animation: false
|
154
|
-
zoomType: 'x'
|
155
|
-
}
|
156
|
-
credits: {
|
157
|
-
enabled: false
|
158
|
-
}
|
159
|
-
title: {
|
160
|
-
text: @model.get('title')
|
161
|
-
}
|
162
|
-
xAxis: {
|
163
|
-
type: 'datetime'
|
164
|
-
}
|
165
|
-
yAxis: {
|
166
|
-
title: {
|
167
|
-
text: @model.get('valuesTitle')
|
168
|
-
}
|
169
|
-
}
|
170
|
-
tooltip: {
|
171
|
-
xDateFormat: '%Y-%m-%d %H:%M:%S'
|
172
|
-
valueDecimals: 6
|
173
|
-
}
|
174
|
-
series: @model.get('series')
|
175
|
-
plotOptions: {
|
176
|
-
series: {
|
177
|
-
animation: false
|
178
|
-
lineWidth: 1
|
179
|
-
shadow: false
|
180
|
-
marker: {
|
181
|
-
radius: 0
|
182
|
-
}
|
183
|
-
}
|
184
|
-
}
|
185
|
-
}
|
186
|
-
$.extend(true, options, globalOptions.highchartOptions, pageInfos.selected().get('highchartOptions'))
|
187
|
-
@chart = new Highcharts.Chart(options)
|
227
|
+
chartClass = @model.chartClass()
|
228
|
+
@chart = new chartClass(@el)
|
229
|
+
@updateData()
|
188
230
|
|
189
|
-
|
231
|
+
}
|
190
232
|
|
191
233
|
WidgetView = Backbone.View.extend {
|
192
234
|
tagName: 'div'
|