pulse-meter 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module PulseMeter
2
- VERSION = "0.2.8"
2
+ VERSION = "0.2.9"
3
3
  end
@@ -43,6 +43,20 @@ module PulseMeter
43
43
  content_type :json
44
44
  camelize_keys(@layout.widget(page_id - 1, id - 1, timespan: timespan)).to_json
45
45
  end
46
+
47
+ get '/sensors' do
48
+ content_type :json
49
+ camelize_keys(@layout.sensor_list).to_json
50
+ end
51
+
52
+ get '/dynamic_widget' do
53
+ content_type :json
54
+ camelize_keys(@layout.dynamic_widget(
55
+ timespan: params[:timespan],
56
+ sensors: params[:sensor],
57
+ type: params[:type])
58
+ ).to_json
59
+ end
46
60
  end
47
61
  end
48
62
  end
@@ -1,6 +1,8 @@
1
1
  module PulseMeter
2
2
  module Visualize
3
3
  class Layout < Base
4
+ include PulseMeter::Mixins::Utils
5
+
4
6
  def initialize(opts)
5
7
  super
6
8
  @opts[:pages] ||= []
@@ -37,6 +39,41 @@ module PulseMeter
37
39
  def widgets(page_id)
38
40
  pages[page_id].widget_datas
39
41
  end
42
+
43
+ def sensor_list
44
+ PulseMeter::Sensor::Base
45
+ .list_objects
46
+ .select{|s| s.is_a?(PulseMeter::Sensor::Timeline)}
47
+ .map do |s|
48
+ {
49
+ id: s.name,
50
+ annotation: s.annotation,
51
+ type: s.class.to_s.split('::').last,
52
+ interval: s.interval
53
+ }
54
+ end
55
+ end
56
+
57
+ DEFAULT_TIMESPAN_IN_INTERVALS = 50
58
+
59
+ def dynamic_widget(args)
60
+ sensor_names = args[:sensors]
61
+ sensors = sensor_names.map{|n| PulseMeter::Sensor::Base.restore(n)}
62
+ timespan = if args[:timespan] && !args[:timespan].empty?
63
+ args[:timespan].to_i
64
+ else
65
+ sensors.first.interval * DEFAULT_TIMESPAN_IN_INTERVALS
66
+ end
67
+
68
+ type = args[:type]
69
+ widget_dsl_class = constantize("PulseMeter::Visualize::DSL::Widgets::#{type.capitalize}")
70
+ widget = widget_dsl_class.new('Dynamic Widget')
71
+ widget.timespan(timespan)
72
+ sensor_names.each{|n| widget.sensor(n)}
73
+
74
+ widget.to_data.data(id: 1)
75
+ end
76
+
40
77
  end
41
78
  end
42
79
  end
@@ -10,16 +10,22 @@
10
10
  margin-bottom: 50px;
11
11
  }
12
12
 
13
+ #dynamic-chart-controls{
14
+ margin-top: 20px;
15
+ }
16
+
13
17
  #chart-controls input{
14
- width: 30px;
18
+ width: 30px;
15
19
  }
16
20
 
17
21
  #chart-controls .space{
18
- padding-left: 10px;
22
+ padding-left: 10px;
19
23
  }
20
24
 
21
25
  .footer{
22
- margin-top: 100px;
23
- text-align: center;
26
+ margin-top: 100px;
27
+ text-align: center;
24
28
  }
25
29
 
30
+ #dynamic-plotarea {
31
+ }
@@ -2,10 +2,31 @@ document.startApp = ->
2
2
  globalOptions = gon.options
3
3
 
4
4
  String::capitalize = ->
5
- @charAt(0).toUpperCase() + @slice(1)
5
+ @charAt(0).toUpperCase() + @slice(1)
6
6
  String::strip = ->
7
7
  if String::trim? then @trim() else @replace /^\s+|\s+$/g, ""
8
8
 
9
+ Number::humanize = ->
10
+ interval = this
11
+ res = ""
12
+ s = interval % 60
13
+ res = "#{s} s" if s > 0
14
+ interval = (interval - s) / 60
15
+ return res unless interval > 0
16
+
17
+ m = interval % 60
18
+ res = "#{m} m #{res}".strip() if m > 0
19
+ interval = (interval - m) / 60
20
+ return res unless interval > 0
21
+
22
+ h = interval % 24
23
+ res = "#{h} h #{res}".strip() if h > 0
24
+ d = (interval - h) / 24
25
+ if d > 0
26
+ "#{d} d #{res}".strip()
27
+ else
28
+ res
29
+
9
30
  PageInfo = Backbone.Model.extend {
10
31
  }
11
32
 
@@ -14,9 +35,13 @@ document.startApp = ->
14
35
  selected: ->
15
36
  @find (m) ->
16
37
  m.get 'selected'
17
-
38
+
18
39
  selectFirst: ->
19
40
  @at(0).set('selected', true) if @length > 0
41
+
42
+ selectNone: ->
43
+ @each (m) ->
44
+ m.set 'selected', false
20
45
 
21
46
  selectPage: (id) ->
22
47
  @each (m) ->
@@ -83,12 +108,18 @@ document.startApp = ->
83
108
  height: 300
84
109
  }
85
110
 
86
- mergedOptions: -> $.extend(true,
87
- @options(),
88
- globalOptions.gchartOptions,
89
- pageInfos.selected().get('gchartOptions')
90
- @get('gchartOptions')
91
- )
111
+ mergedOptions: ->
112
+ pageOptions = if pageInfos.selected()
113
+ pageInfos.selected().get('gchartOptions')
114
+ else
115
+ {}
116
+
117
+ $.extend(true,
118
+ @options(),
119
+ globalOptions.gchartOptions,
120
+ pageOptions,
121
+ @get('gchartOptions')
122
+ )
92
123
 
93
124
  data: -> new google.visualization.DataTable
94
125
 
@@ -171,7 +202,7 @@ document.startApp = ->
171
202
  position: 'bottom'
172
203
  }
173
204
  vAxis: {
174
- title: "#{@get('valuesTitle')} / #{@humanizedInterval()}"
205
+ title: @valuesTitle()
175
206
  }
176
207
  hAxis: {
177
208
  format: format
@@ -180,27 +211,15 @@ document.startApp = ->
180
211
  axisTitlesPosition: 'in'
181
212
  }
182
213
 
183
- humanizedInterval: ->
184
- interval = @get('interval')
185
- res = ""
186
- s = interval % 60
187
- res = "#{s} s" if s > 0
188
- interval = (interval - s) / 60
189
- return res unless interval > 0
190
-
191
- m = interval % 60
192
- res = "#{m} m #{res}".strip() if m > 0
193
- interval = (interval - m) / 60
194
- return res unless interval > 0
195
-
196
- h = interval % 24
197
- res = "#{h} h #{res}".strip() if h > 0
198
- d = (interval - h) / 24
199
- if d > 0
200
- "#{d} d #{res}".strip()
214
+ valuesTitle: ->
215
+ if @get('valuesTitle')
216
+ "#{@get('valuesTitle')} / #{@humanizedInterval()}"
201
217
  else
202
- res
218
+ @humanizedInterval()
219
+
203
220
 
221
+ humanizedInterval: ->
222
+ @get('interval').humanize()
204
223
 
205
224
  cutoff: (min, max) ->
206
225
  _.each(@get('series').rows, (row) ->
@@ -288,12 +307,191 @@ document.startApp = ->
288
307
 
289
308
  }
290
309
 
310
+ DynamicWidget = Backbone.Model.extend {
311
+
312
+ increaseTimespan: (inc) ->
313
+ @set('timespan', @timespan() + inc)
314
+
315
+ resetTimespan: ->
316
+ @set('timespan', null)
317
+
318
+ timespan: -> @get('timespan')
319
+
320
+ sensorArgs: ->
321
+ _.map(@get('sensorIds'), (name) -> "sensor[]=#{name}").join('&')
322
+
323
+ url: ->
324
+ url = "#{ROOT}dynamic_widget?#{@sensorArgs()}&type=#{@get('type')}"
325
+
326
+ timespan = @timespan()
327
+ url = "#{url}&timespan=#{timespan}" if timespan? && !_.isNaN(timespan)
328
+
329
+ url
330
+
331
+ forceUpdate: ->
332
+ @fetch {
333
+ success: (model, response) ->
334
+ model.trigger('redraw')
335
+ }
336
+ }
337
+
291
338
  WidgetList = Backbone.Collection.extend {
292
339
  model: Widget
293
340
  url: ->
294
341
  ROOT + 'pages/' + pageInfos.selected().id + '/widgets'
295
342
  }
296
343
 
344
+ SensorInfo = Backbone.Model.extend {
345
+
346
+ }
347
+
348
+ SensorInfoList = Backbone.Collection.extend {
349
+ model: SensorInfo
350
+ url: ->
351
+ ROOT + 'sensors'
352
+ }
353
+
354
+
355
+ SensorInfoListView = Backbone.View.extend {
356
+ tagName: 'div'
357
+
358
+ template: ->
359
+ _.template($("#sensor-list").html())
360
+
361
+ initialize: (sensorInfo) ->
362
+ @sensorInfo = sensorInfo
363
+ @sensorInfo.bind 'reset', @render, this
364
+
365
+ render: ->
366
+ @$el.html @template()({sensors: @sensorInfo.toJSON()})
367
+
368
+ selectedSensors: ->
369
+ checked = _.filter @$el.find('.sensor-box'), (el) -> $(el).is(':checked')
370
+ ids = {}
371
+ _.each checked, (box) -> ids[box.id] = true
372
+ selected = @sensorInfo.filter (sensor) -> ids[sensor.id]
373
+
374
+ }
375
+
376
+ DynamicChartView = Backbone.View.extend {
377
+ initialize: ->
378
+ @sensors = []
379
+ @type = 'Area'
380
+ @widget = new DynamicWidget
381
+
382
+ @widget.bind('destroy', @remove, this)
383
+ @widget.bind('redraw', @redrawChart, this)
384
+
385
+ tagName: 'div'
386
+
387
+ events: {
388
+ "click #refresh-chart": 'update'
389
+ "click #extend-timespan": 'extendTimespan'
390
+ "click #reset-timespan": 'resetTimespan'
391
+ }
392
+
393
+ template: -> _.template($("#dynamic-widget-plotarea").html())
394
+
395
+ render: ->
396
+ @$el.html(@template()())
397
+
398
+ extendTimespan: ->
399
+ select = @$el.find("#extend-timespan-val")
400
+ val = select.first().val()
401
+ @widget.increaseTimespan(parseInt(val))
402
+ @update()
403
+
404
+ resetTimespan: ->
405
+ @widget.resetTimespan()
406
+ @update()
407
+
408
+ sensorIds: -> _.map(@sensors, (s) -> s.id)
409
+
410
+ redrawChart: ->
411
+ if @presenter
412
+ @presenter.draw()
413
+ else
414
+ @presenter = WidgetPresenter.create(@widget, @chartContainer())
415
+
416
+
417
+ chartContainer: ->
418
+ @$el.find('#chart')[0]
419
+
420
+ update: ->
421
+ @widget.forceUpdate() if @sensors.length > 0
422
+
423
+ draw: (sensors, type) ->
424
+ @sensors = sensors
425
+ @type = type
426
+
427
+ @widget.set('sensorIds', @sensorIds())
428
+ @widget.set('type', @type)
429
+
430
+ @presenter = null
431
+ $(@chartContainer()).empty()
432
+ @widget.forceUpdate()
433
+ }
434
+
435
+ DynamicWidgetView = Backbone.View.extend {
436
+ tagName: 'div'
437
+
438
+ initialize: ->
439
+ @sensorInfo = new SensorInfoList
440
+
441
+ @sensorListView = new SensorInfoListView(@sensorInfo)
442
+ @chartView = new DynamicChartView
443
+
444
+ @$el.html(@template()())
445
+
446
+ @$el.find('#sensor-list-area').append(@sensorListView.el)
447
+
448
+ @chartView.render()
449
+ @$el.find('#dynamic-plotarea').append(@chartView.el)
450
+
451
+ events: {
452
+ "click #sensor-controls #refresh": 'refresh'
453
+ "click #sensor-controls #draw": 'drawChart'
454
+ }
455
+
456
+ template: ->
457
+ _.template($("#dynamic-widget").html())
458
+
459
+ errorTemplate: -> _.template($("#dynamic-widget-error").html())
460
+
461
+ error: (error)->
462
+ @$el.find('#errors').append(@errorTemplate()(error: error))
463
+
464
+
465
+ refresh: ->
466
+ @sensorInfo.fetch()
467
+
468
+ intervalsEqual: (sensors) ->
469
+ interval = sensors[0].get('interval')
470
+ badIntervals = _.filter(sensors, (s) ->
471
+ s.get('interval') != interval
472
+ )
473
+ badIntervals.length == 0
474
+
475
+ drawChart: ->
476
+ selectedSensors = @sensorListView.selectedSensors()
477
+ return unless selectedSensors.length > 0
478
+
479
+ unless @intervalsEqual(selectedSensors)
480
+ @error('Selected sensors have different intervals')
481
+ return
482
+
483
+ type = @$el.find('#chart-type').val()
484
+ @chartView.draw(selectedSensors, type)
485
+
486
+ render: (container) ->
487
+ container.empty()
488
+ container.append(@$el)
489
+ @sensorInfo.fetch()
490
+ @chartView.update()
491
+
492
+ }
493
+
494
+
297
495
  WidgetChartView = Backbone.View.extend {
298
496
  tagName: 'div'
299
497
 
@@ -370,8 +568,9 @@ document.startApp = ->
370
568
 
371
569
  widgetList = new WidgetList
372
570
  setInterval( ->
373
- widgetList.each (w) ->
374
- w.refetch()
571
+ if pageInfos.selected()
572
+ widgetList.each (w) ->
573
+ w.refetch()
375
574
  , 200)
376
575
 
377
576
  WidgetListView = Backbone.View.extend {
@@ -382,9 +581,7 @@ document.startApp = ->
382
581
  container = $('#widgets')
383
582
  container.empty()
384
583
  widgetList.each (w) ->
385
- view = new WidgetView {
386
- model: w
387
- }
584
+ view = new WidgetView { model: w }
388
585
  view.render()
389
586
  container.append(view.el)
390
587
  view.renderChart()
@@ -395,14 +592,19 @@ document.startApp = ->
395
592
  AppRouter = Backbone.Router.extend {
396
593
  routes: {
397
594
  'pages/:id': 'getPage'
595
+ 'custom': 'custom'
398
596
  '*actions': 'defaultRoute'
399
597
  }
400
598
  getPage: (ids) ->
401
599
  id = parseInt(ids)
402
600
  pageInfos.selectPage(id)
403
601
  widgetList.fetch()
602
+ custom: ->
603
+ pageInfos.selectNone()
604
+ dynamicWidget = new DynamicWidgetView
605
+ dynamicWidget.render($('#widgets'))
404
606
  defaultRoute: (actions) ->
405
- @navigate('//pages/1') if pageInfos.length > 0
607
+ @navigate('//custom')
406
608
  }
407
609
 
408
610
  appRouter = new AppRouter
@@ -4,7 +4,7 @@
4
4
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
5
5
 
6
6
  document.startApp = function() {
7
- var AppRouter, AreaPresenter, GaugePresenter, LinePresenter, PageInfo, PageInfoList, PageTitleView, PageTitlesView, PiePresenter, SeriesPresenter, TablePresenter, TimelinePresenter, Widget, WidgetChartView, WidgetList, WidgetListView, WidgetPresenter, WidgetView, appRouter, globalOptions, pageInfos, pageTitlesApp, widgetList, widgetListApp;
7
+ var AppRouter, AreaPresenter, DynamicChartView, DynamicWidget, DynamicWidgetView, GaugePresenter, LinePresenter, PageInfo, PageInfoList, PageTitleView, PageTitlesView, PiePresenter, SensorInfo, SensorInfoList, SensorInfoListView, SeriesPresenter, TablePresenter, TimelinePresenter, Widget, WidgetChartView, WidgetList, WidgetListView, WidgetPresenter, WidgetView, appRouter, globalOptions, pageInfos, pageTitlesApp, widgetList, widgetListApp;
8
8
  globalOptions = gon.options;
9
9
  String.prototype.capitalize = function() {
10
10
  return this.charAt(0).toUpperCase() + this.slice(1);
@@ -16,6 +16,27 @@
16
16
  return this.replace(/^\s+|\s+$/g, "");
17
17
  }
18
18
  };
19
+ Number.prototype.humanize = function() {
20
+ var d, h, interval, m, res, s;
21
+ interval = this;
22
+ res = "";
23
+ s = interval % 60;
24
+ if (s > 0) res = "" + s + " s";
25
+ interval = (interval - s) / 60;
26
+ if (!(interval > 0)) return res;
27
+ m = interval % 60;
28
+ if (m > 0) res = ("" + m + " m " + res).strip();
29
+ interval = (interval - m) / 60;
30
+ if (!(interval > 0)) return res;
31
+ h = interval % 24;
32
+ if (h > 0) res = ("" + h + " h " + res).strip();
33
+ d = (interval - h) / 24;
34
+ if (d > 0) {
35
+ return ("" + d + " d " + res).strip();
36
+ } else {
37
+ return res;
38
+ }
39
+ };
19
40
  PageInfo = Backbone.Model.extend({});
20
41
  PageInfoList = Backbone.Collection.extend({
21
42
  model: PageInfo,
@@ -27,6 +48,11 @@
27
48
  selectFirst: function() {
28
49
  if (this.length > 0) return this.at(0).set('selected', true);
29
50
  },
51
+ selectNone: function() {
52
+ return this.each(function(m) {
53
+ return m.set('selected', false);
54
+ });
55
+ },
30
56
  selectPage: function(id) {
31
57
  return this.each(function(m) {
32
58
  return m.set('selected', m.id === id);
@@ -101,7 +127,9 @@
101
127
  };
102
128
 
103
129
  WidgetPresenter.prototype.mergedOptions = function() {
104
- return $.extend(true, this.options(), globalOptions.gchartOptions, pageInfos.selected().get('gchartOptions'), this.get('gchartOptions'));
130
+ var pageOptions;
131
+ pageOptions = pageInfos.selected() ? pageInfos.selected().get('gchartOptions') : {};
132
+ return $.extend(true, this.options(), globalOptions.gchartOptions, pageOptions, this.get('gchartOptions'));
105
133
  };
106
134
 
107
135
  WidgetPresenter.prototype.data = function() {
@@ -234,7 +262,7 @@
234
262
  position: 'bottom'
235
263
  },
236
264
  vAxis: {
237
- title: "" + (this.get('valuesTitle')) + " / " + (this.humanizedInterval())
265
+ title: this.valuesTitle()
238
266
  },
239
267
  hAxis: {
240
268
  format: format
@@ -244,28 +272,18 @@
244
272
  });
245
273
  };
246
274
 
247
- SeriesPresenter.prototype.humanizedInterval = function() {
248
- var d, h, interval, m, res, s;
249
- interval = this.get('interval');
250
- res = "";
251
- s = interval % 60;
252
- if (s > 0) res = "" + s + " s";
253
- interval = (interval - s) / 60;
254
- if (!(interval > 0)) return res;
255
- m = interval % 60;
256
- if (m > 0) res = ("" + m + " m " + res).strip();
257
- interval = (interval - m) / 60;
258
- if (!(interval > 0)) return res;
259
- h = interval % 24;
260
- if (h > 0) res = ("" + h + " h " + res).strip();
261
- d = (interval - h) / 24;
262
- if (d > 0) {
263
- return ("" + d + " d " + res).strip();
275
+ SeriesPresenter.prototype.valuesTitle = function() {
276
+ if (this.get('valuesTitle')) {
277
+ return "" + (this.get('valuesTitle')) + " / " + (this.humanizedInterval());
264
278
  } else {
265
- return res;
279
+ return this.humanizedInterval();
266
280
  }
267
281
  };
268
282
 
283
+ SeriesPresenter.prototype.humanizedInterval = function() {
284
+ return this.get('interval').humanize();
285
+ };
286
+
269
287
  SeriesPresenter.prototype.cutoff = function(min, max) {
270
288
  return _.each(this.get('series').rows, function(row) {
271
289
  var i, value, _i, _ref, _results;
@@ -416,12 +434,193 @@
416
434
  });
417
435
  }
418
436
  });
437
+ DynamicWidget = Backbone.Model.extend({
438
+ increaseTimespan: function(inc) {
439
+ return this.set('timespan', this.timespan() + inc);
440
+ },
441
+ resetTimespan: function() {
442
+ return this.set('timespan', null);
443
+ },
444
+ timespan: function() {
445
+ return this.get('timespan');
446
+ },
447
+ sensorArgs: function() {
448
+ return _.map(this.get('sensorIds'), function(name) {
449
+ return "sensor[]=" + name;
450
+ }).join('&');
451
+ },
452
+ url: function() {
453
+ var timespan, url;
454
+ url = "" + ROOT + "dynamic_widget?" + (this.sensorArgs()) + "&type=" + (this.get('type'));
455
+ timespan = this.timespan();
456
+ if ((timespan != null) && !_.isNaN(timespan)) {
457
+ url = "" + url + "&timespan=" + timespan;
458
+ }
459
+ return url;
460
+ },
461
+ forceUpdate: function() {
462
+ return this.fetch({
463
+ success: function(model, response) {
464
+ return model.trigger('redraw');
465
+ }
466
+ });
467
+ }
468
+ });
419
469
  WidgetList = Backbone.Collection.extend({
420
470
  model: Widget,
421
471
  url: function() {
422
472
  return ROOT + 'pages/' + pageInfos.selected().id + '/widgets';
423
473
  }
424
474
  });
475
+ SensorInfo = Backbone.Model.extend({});
476
+ SensorInfoList = Backbone.Collection.extend({
477
+ model: SensorInfo,
478
+ url: function() {
479
+ return ROOT + 'sensors';
480
+ }
481
+ });
482
+ SensorInfoListView = Backbone.View.extend({
483
+ tagName: 'div',
484
+ template: function() {
485
+ return _.template($("#sensor-list").html());
486
+ },
487
+ initialize: function(sensorInfo) {
488
+ this.sensorInfo = sensorInfo;
489
+ return this.sensorInfo.bind('reset', this.render, this);
490
+ },
491
+ render: function() {
492
+ return this.$el.html(this.template()({
493
+ sensors: this.sensorInfo.toJSON()
494
+ }));
495
+ },
496
+ selectedSensors: function() {
497
+ var checked, ids, selected;
498
+ checked = _.filter(this.$el.find('.sensor-box'), function(el) {
499
+ return $(el).is(':checked');
500
+ });
501
+ ids = {};
502
+ _.each(checked, function(box) {
503
+ return ids[box.id] = true;
504
+ });
505
+ return selected = this.sensorInfo.filter(function(sensor) {
506
+ return ids[sensor.id];
507
+ });
508
+ }
509
+ });
510
+ DynamicChartView = Backbone.View.extend({
511
+ initialize: function() {
512
+ this.sensors = [];
513
+ this.type = 'Area';
514
+ this.widget = new DynamicWidget;
515
+ this.widget.bind('destroy', this.remove, this);
516
+ return this.widget.bind('redraw', this.redrawChart, this);
517
+ },
518
+ tagName: 'div',
519
+ events: {
520
+ "click #refresh-chart": 'update',
521
+ "click #extend-timespan": 'extendTimespan',
522
+ "click #reset-timespan": 'resetTimespan'
523
+ },
524
+ template: function() {
525
+ return _.template($("#dynamic-widget-plotarea").html());
526
+ },
527
+ render: function() {
528
+ return this.$el.html(this.template()());
529
+ },
530
+ extendTimespan: function() {
531
+ var select, val;
532
+ select = this.$el.find("#extend-timespan-val");
533
+ val = select.first().val();
534
+ this.widget.increaseTimespan(parseInt(val));
535
+ return this.update();
536
+ },
537
+ resetTimespan: function() {
538
+ this.widget.resetTimespan();
539
+ return this.update();
540
+ },
541
+ sensorIds: function() {
542
+ return _.map(this.sensors, function(s) {
543
+ return s.id;
544
+ });
545
+ },
546
+ redrawChart: function() {
547
+ if (this.presenter) {
548
+ return this.presenter.draw();
549
+ } else {
550
+ return this.presenter = WidgetPresenter.create(this.widget, this.chartContainer());
551
+ }
552
+ },
553
+ chartContainer: function() {
554
+ return this.$el.find('#chart')[0];
555
+ },
556
+ update: function() {
557
+ if (this.sensors.length > 0) return this.widget.forceUpdate();
558
+ },
559
+ draw: function(sensors, type) {
560
+ this.sensors = sensors;
561
+ this.type = type;
562
+ this.widget.set('sensorIds', this.sensorIds());
563
+ this.widget.set('type', this.type);
564
+ this.presenter = null;
565
+ $(this.chartContainer()).empty();
566
+ return this.widget.forceUpdate();
567
+ }
568
+ });
569
+ DynamicWidgetView = Backbone.View.extend({
570
+ tagName: 'div',
571
+ initialize: function() {
572
+ this.sensorInfo = new SensorInfoList;
573
+ this.sensorListView = new SensorInfoListView(this.sensorInfo);
574
+ this.chartView = new DynamicChartView;
575
+ this.$el.html(this.template()());
576
+ this.$el.find('#sensor-list-area').append(this.sensorListView.el);
577
+ this.chartView.render();
578
+ return this.$el.find('#dynamic-plotarea').append(this.chartView.el);
579
+ },
580
+ events: {
581
+ "click #sensor-controls #refresh": 'refresh',
582
+ "click #sensor-controls #draw": 'drawChart'
583
+ },
584
+ template: function() {
585
+ return _.template($("#dynamic-widget").html());
586
+ },
587
+ errorTemplate: function() {
588
+ return _.template($("#dynamic-widget-error").html());
589
+ },
590
+ error: function(error) {
591
+ return this.$el.find('#errors').append(this.errorTemplate()({
592
+ error: error
593
+ }));
594
+ },
595
+ refresh: function() {
596
+ return this.sensorInfo.fetch();
597
+ },
598
+ intervalsEqual: function(sensors) {
599
+ var badIntervals, interval;
600
+ interval = sensors[0].get('interval');
601
+ badIntervals = _.filter(sensors, function(s) {
602
+ return s.get('interval') !== interval;
603
+ });
604
+ return badIntervals.length === 0;
605
+ },
606
+ drawChart: function() {
607
+ var selectedSensors, type;
608
+ selectedSensors = this.sensorListView.selectedSensors();
609
+ if (!(selectedSensors.length > 0)) return;
610
+ if (!this.intervalsEqual(selectedSensors)) {
611
+ this.error('Selected sensors have different intervals');
612
+ return;
613
+ }
614
+ type = this.$el.find('#chart-type').val();
615
+ return this.chartView.draw(selectedSensors, type);
616
+ },
617
+ render: function(container) {
618
+ container.empty();
619
+ container.append(this.$el);
620
+ this.sensorInfo.fetch();
621
+ return this.chartView.update();
622
+ }
623
+ });
425
624
  WidgetChartView = Backbone.View.extend({
426
625
  tagName: 'div',
427
626
  initialize: function() {
@@ -506,9 +705,11 @@
506
705
  });
507
706
  widgetList = new WidgetList;
508
707
  setInterval(function() {
509
- return widgetList.each(function(w) {
510
- return w.refetch();
511
- });
708
+ if (pageInfos.selected()) {
709
+ return widgetList.each(function(w) {
710
+ return w.refetch();
711
+ });
712
+ }
512
713
  }, 200);
513
714
  WidgetListView = Backbone.View.extend({
514
715
  initialize: function() {
@@ -533,6 +734,7 @@
533
734
  AppRouter = Backbone.Router.extend({
534
735
  routes: {
535
736
  'pages/:id': 'getPage',
737
+ 'custom': 'custom',
536
738
  '*actions': 'defaultRoute'
537
739
  },
538
740
  getPage: function(ids) {
@@ -541,8 +743,14 @@
541
743
  pageInfos.selectPage(id);
542
744
  return widgetList.fetch();
543
745
  },
746
+ custom: function() {
747
+ var dynamicWidget;
748
+ pageInfos.selectNone();
749
+ dynamicWidget = new DynamicWidgetView;
750
+ return dynamicWidget.render($('#widgets'));
751
+ },
544
752
  defaultRoute: function(actions) {
545
- if (pageInfos.length > 0) return this.navigate('//pages/1');
753
+ return this.navigate('//custom');
546
754
  }
547
755
  });
548
756
  appRouter = new AppRouter;
@@ -19,6 +19,7 @@
19
19
 
20
20
  - %w(area line table pie gauge).each do |wtype|
21
21
  = partial "widgets/#{wtype}"
22
+ = partial "sensors"
22
23
 
23
24
  .container#main
24
25
  .row
@@ -26,7 +27,8 @@
26
27
  .navbar
27
28
  .navbar-inner
28
29
  .container
29
- %span.brand= @title
30
- %ul.nav#page-titles
30
+ %a{href: '#/custom'}
31
+ %span.brand= @title
32
+ %ul.nav#page-titles
31
33
  #widgets.row
32
34
 
@@ -0,0 +1,66 @@
1
+ %script#sensor-list{type: 'text/template'}
2
+ %table#sensors.table.table-striped
3
+ %tr
4
+ %th
5
+ %th Title
6
+ %th Type
7
+ %th Interval
8
+ <% _.each(sensors, function(sensor) { %>
9
+ %tr
10
+ %td
11
+ :plain
12
+ <input class="sensor-box" type="checkbox" id="<%- sensor.id %>" />
13
+ %td
14
+ <%- sensor.annotation %>
15
+ %sub <%- sensor.id %>
16
+ %td <%- sensor.type %>
17
+ %td <%- sensor.interval.humanize() %>
18
+ <% }) %>
19
+
20
+ %script#dynamic-widget-error{type: 'text/template'}
21
+ .alert.alert-error
22
+ .button.close{'data-dismiss' => 'alert'} ×
23
+ <%- error %>
24
+
25
+ %script#dynamic-widget-plotarea{type: 'text/template'}
26
+ .well
27
+ #chart
28
+ %h3 Choose sensors to plot
29
+ #dynamic-chart-controls
30
+ .form-inline
31
+ %button#refresh-chart.btn.btn-mini
32
+ %i.icon-refresh
33
+ Refresh chart
34
+ Timespan:
35
+ %select#extend-timespan-val.btn-mini.span1
36
+ = partial "widgets/extend_options"
37
+ %button#extend-timespan.btn.btn-mini
38
+ %i.icon-arrow-left
39
+ Extend
40
+ %button#reset-timespan.btn.btn-mini
41
+ %i.icon-arrow-right
42
+ Reset
43
+
44
+
45
+ %script#dynamic-widget{type: 'text/template'}
46
+ .span10
47
+ #dynamic-plotarea
48
+ #errors
49
+ .span10
50
+ #sensor-controls.form-horizontal
51
+ %p.form-inline
52
+ Chart type
53
+ %select#chart-type{value: 'area'}
54
+ %option{value: 'area'} Area
55
+ %option{value: 'line'} Line
56
+ %option{value: 'table'} Table
57
+ %button#draw.btn.btn-mini.btn-primary
58
+ %i.icon-play
59
+ Draw
60
+ %button#refresh.btn.btn-mini
61
+ %i.icon-refresh
62
+ Refresh sensor list
63
+ #sensor-list-area
64
+
65
+
66
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pulse-meter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-08-02 00:00:00.000000000 Z
13
+ date: 2012-08-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: gon-sinatra
@@ -370,7 +370,6 @@ files:
370
370
  - lib/pulse-meter/visualize/dsl/page.rb
371
371
  - lib/pulse-meter/visualize/dsl/sensor.rb
372
372
  - lib/pulse-meter/visualize/dsl/widget.rb
373
- - lib/pulse-meter/visualize/dsl/widget_old.rb
374
373
  - lib/pulse-meter/visualize/dsl/widgets/area.rb
375
374
  - lib/pulse-meter/visualize/dsl/widgets/gauge.rb
376
375
  - lib/pulse-meter/visualize/dsl/widgets/line.rb
@@ -394,6 +393,7 @@ files:
394
393
  - lib/pulse-meter/visualize/sensor.rb
395
394
  - lib/pulse-meter/visualize/series_extractor.rb
396
395
  - lib/pulse-meter/visualize/views/main.haml
396
+ - lib/pulse-meter/visualize/views/sensors.haml
397
397
  - lib/pulse-meter/visualize/views/widgets/area.haml
398
398
  - lib/pulse-meter/visualize/views/widgets/extend_options.haml
399
399
  - lib/pulse-meter/visualize/views/widgets/gauge.haml
@@ -475,7 +475,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
475
475
  version: '0'
476
476
  segments:
477
477
  - 0
478
- hash: -2299028715226414900
478
+ hash: -3651038348403284878
479
479
  requirements: []
480
480
  rubyforge_project:
481
481
  rubygems_version: 1.8.24
@@ -1,95 +0,0 @@
1
- module PulseMeter
2
- module Visualize
3
- module DSL
4
- class Widget
5
- include PulseMeter::Mixins::Utils
6
-
7
- DEFAULT_WIDTH = 10
8
- DEFAULT_TIMESPAN = 60 * 60 * 24 # One day
9
- DEFAULT_GCHART_OPTIONS = {}
10
-
11
- def initialize(type, title = '')
12
- raise BadWidgetType, type if type.to_s.empty?
13
- @type = type
14
- @title = title.to_s || ''
15
- @values_label = ''
16
- @width = DEFAULT_WIDTH
17
- @sensors = []
18
- @show_last_point = false
19
- @redraw_interval = nil
20
- @timespan = DEFAULT_TIMESPAN
21
- @gchart_options = DEFAULT_GCHART_OPTIONS.dup
22
- end
23
-
24
- def process_args(args)
25
- [:sensor, :title, :width, :values_label, :show_last_point, :redraw_interval, :timespan].each do |arg|
26
- if args.has_key?(arg)
27
- send(arg, args[arg])
28
- end
29
- end
30
- end
31
-
32
- def redraw_interval(new_redraw_interval)
33
- new_redraw_interval = new_redraw_interval.to_i
34
- raise BadWidgetRedrawInterval, new_redraw_interval unless new_redraw_interval > 0
35
- @redraw_interval = new_redraw_interval
36
- end
37
-
38
- def show_last_point(new_show_last_point)
39
- @show_last_point = !!new_show_last_point
40
- end
41
-
42
- def timespan(new_timespan)
43
- new_timespan = new_timespan.to_i
44
- raise BadWidgetTimeSpan, new_timespan unless new_timespan > 0
45
- @timespan = new_timespan
46
- end
47
-
48
- def values_label(new_label)
49
- @values_label = new_label.to_s
50
- end
51
-
52
- def title(new_title)
53
- @title = new_title.to_s || ''
54
- end
55
-
56
- def width(new_width)
57
- raise BadWidgetWidth, new_width unless new_width.respond_to?(:to_i)
58
- w = new_width.to_i
59
- raise BadWidgetWidth, new_width unless w > 0 && w <= 10
60
- @width = new_width.to_i
61
- end
62
-
63
- def gchart_options(options = {})
64
- @gchart_options.merge!(options)
65
- end
66
-
67
- def sensor(name, sensor_args = nil)
68
- s = PulseMeter::Visualize::DSL::Sensor.new(name)
69
- s.process_args(sensor_args) if sensor_args
70
- @sensors << s
71
- end
72
-
73
- def method_missing(name, value)
74
- @gchart_options[name] = value
75
- end
76
-
77
- def to_widget
78
- args = {
79
- title: @title,
80
- type: @type,
81
- values_label: @values_label,
82
- width: @width,
83
- sensors: @sensors.map(&:to_sensor),
84
- redraw_interval: @redraw_interval,
85
- show_last_point: @show_last_point,
86
- timespan: @timespan,
87
- gchart_options: @gchart_options
88
- }
89
- PulseMeter::Visualize::Widget.new(args)
90
- end
91
- end
92
- end
93
- end
94
- end
95
-