pulse-meter 0.1.11 → 0.2.0
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.
- 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'
|