fnordmetric 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.travis.yml +1 -0
  2. data/VERSION +1 -1
  3. data/doc/preview1.png +0 -0
  4. data/doc/preview2.png +0 -0
  5. data/doc/ulm_stats.rb +622 -0
  6. data/doc/version +1 -0
  7. data/fnordmetric.gemspec +16 -38
  8. data/haml/app.haml +12 -5
  9. data/lib/fnordmetric.rb +3 -0
  10. data/lib/fnordmetric/app.rb +19 -10
  11. data/lib/fnordmetric/bars_widget.rb +26 -0
  12. data/lib/fnordmetric/context.rb +3 -3
  13. data/lib/fnordmetric/gauge.rb +20 -0
  14. data/lib/fnordmetric/gauge_calculations.rb +28 -4
  15. data/lib/fnordmetric/gauge_modifiers.rb +39 -6
  16. data/lib/fnordmetric/logger.rb +19 -0
  17. data/lib/fnordmetric/numbers_widget.rb +5 -15
  18. data/lib/fnordmetric/pie_widget.rb +23 -0
  19. data/lib/fnordmetric/standalone.rb +1 -1
  20. data/lib/fnordmetric/timeline_widget.rb +16 -23
  21. data/lib/fnordmetric/toplist_widget.rb +25 -0
  22. data/lib/fnordmetric/widget.rb +3 -3
  23. data/pub/{fnordmetric/fnordmetric.css → fnordmetric.css} +46 -36
  24. data/pub/fnordmetric.js +1069 -0
  25. data/pub/loader.gif +0 -0
  26. data/pub/{highcharts → vendor}/highcharts.js +0 -0
  27. data/pub/{jquery-1.6.1.min.js → vendor/jquery-1.6.1.min.js} +0 -0
  28. data/readme.rdoc +228 -311
  29. data/spec/app_spec.rb +63 -3
  30. data/spec/gauge_modifiers_spec.rb +157 -2
  31. data/spec/gauge_spec.rb +143 -12
  32. data/spec/widget_spec.rb +18 -18
  33. metadata +33 -58
  34. data/.document +0 -5
  35. data/_spec/app_spec.rb +0 -178
  36. data/_spec/cache_spec.rb +0 -53
  37. data/_spec/combine_metric_spec.rb +0 -19
  38. data/_spec/core_spec.rb +0 -50
  39. data/_spec/count_metric_spec.rb +0 -32
  40. data/_spec/dashboard_spec.rb +0 -67
  41. data/_spec/event_spec.rb +0 -46
  42. data/_spec/metric_spec.rb +0 -118
  43. data/_spec/report_spec.rb +0 -87
  44. data/_spec/sum_metric_spec.rb +0 -33
  45. data/_spec/widget_spec.rb +0 -107
  46. data/doc/example_server.rb +0 -56
  47. data/doc/import_dump.rb +0 -26
  48. data/pub/fnordmetric/fnordmetric.js +0 -543
  49. data/pub/fnordmetric/widget_numbers.js +0 -71
  50. data/pub/fnordmetric/widget_timeline.css +0 -0
  51. data/pub/fnordmetric/widget_timeline.js +0 -110
  52. data/pub/highcharts/adapters/mootools-adapter.js +0 -12
  53. data/pub/highcharts/adapters/mootools-adapter.src.js +0 -243
  54. data/pub/highcharts/adapters/prototype-adapter.js +0 -14
  55. data/pub/highcharts/adapters/prototype-adapter.src.js +0 -284
  56. data/pub/highcharts/highcharts.src.js +0 -11103
  57. data/pub/highcharts/modules/exporting.js +0 -22
  58. data/pub/highcharts/modules/exporting.src.js +0 -703
  59. data/pub/highcharts/themes/dark-blue.js +0 -268
  60. data/pub/highcharts/themes/dark-green.js +0 -268
  61. data/pub/highcharts/themes/gray.js +0 -262
  62. data/pub/highcharts/themes/grid.js +0 -97
  63. data/pub/raphael-min.js +0 -8
  64. data/pub/raphael-utils.js +0 -221
  65. data/ulm_stats.rb +0 -198
@@ -1,33 +0,0 @@
1
- require ::File.expand_path('../spec_helper.rb', __FILE__)
2
-
3
- describe FnordMetric::SumMetric do
4
-
5
- before(:each) do
6
- FnordMetric::Event.destroy_all
7
- FnordMetric.track('a_event_type', :time => 33.hours.ago, :myval => 4)
8
- FnordMetric.track('a_event_type', :time => 32.hours.ago, :myval => 2)
9
- FnordMetric.track('a_event_type', :time => 28.hours.ago, :myval => 9)
10
- FnordMetric.track('a_event_type', :time => 27.hours.ago, :myval => 1)
11
- FnordMetric.track('a_event_type', :time => 26.hours.ago, :myval => 6)
12
- FnordMetric.track('a_event_type', :time => 13.hours.ago, :myval => 3)
13
- FnordMetric.track('a_event_type', :time => 12.hours.ago, :myval => 8)
14
- FnordMetric.track('a_event_type', :time => 11.hours.ago, :myval => 7)
15
- end
16
-
17
- it "should sum events until now" do
18
- metric = FnordMetric.metric('a_event_count', :sum => :myval, :types => [:a_event_type])
19
- metric.current.should == 40
20
- end
21
-
22
- it "should sum events until 18 hours ago" do
23
- metric = FnordMetric.metric('a_event_count', :sum => :myval, :types => [:a_event_type])
24
- metric.at(18.hours.ago).should == 22
25
- end
26
-
27
- it "should sum events from 30 to 20 hours ago" do
28
- metric = FnordMetric.metric('a_event_count', :sum => :myval, :types => [:a_event_type])
29
- metric.at(30.hours.ago..20.hours.ago).should == 16
30
- end
31
-
32
-
33
- end
@@ -1,107 +0,0 @@
1
- require ::File.expand_path('../spec_helper.rb', __FILE__)
2
-
3
- describe FnordMetric::Widget do
4
-
5
- before(:each) do
6
- FnordMetric::Event.destroy_all
7
- end
8
-
9
- it "should remembe it's own title" do
10
- widget = FnordMetric::Widget.new(:title => "My Widget")
11
- widget.title.should == "My Widget"
12
- end
13
-
14
- it "should add the report on init" do
15
- FnordMetric.metric(:my_metric, :sum => :my_field)
16
- report = FnordMetric.report(:range => (4.days.ago..Time.now))
17
- widget = FnordMetric::Widget.new(:report => report)
18
- widget.report.should == report
19
- end
20
-
21
- it "should add the report after init" do
22
- FnordMetric.metric(:my_metric, :sum => :my_field)
23
- report = FnordMetric.report(:range => (4.days.ago..Time.now))
24
- widget = FnordMetric::Widget.new
25
- widget.report.should be_nil
26
- widget.add_report(report)
27
- widget.report.should == report
28
- end
29
-
30
- it "should define a new widget when given two metric-token" do
31
- FnordMetric.metric(:first_metric, :count => :true)
32
- FnordMetric.metric(:second_metric, :count => :true)
33
- widget = FnordMetric::Widget.new(:metrics => [:first_metric, :second_metric], :title => "My Widget", :type => :timeline)
34
- widget.metrics.length.should == 2
35
- widget.metrics.first.should be_a(FnordMetric::CountMetric)
36
- widget.metrics.first.token.should == :first_metric
37
- widget.metrics.last.should be_a(FnordMetric::CountMetric)
38
- widget.metrics.last.token.should == :second_metric
39
- end
40
-
41
- it "should define a new widget when given two metrics" do
42
- my_metrics = [
43
- FnordMetric.metric(:first_metric, :count => :true),
44
- FnordMetric.metric(:second_metric, :count => :true)
45
- ]
46
- widget = FnordMetric::Widget.new(:metrics => my_metrics, :title => "My Widget", :type => :timeline)
47
- widget.metrics.length.should == 2
48
- widget.metrics.first.should be_a(FnordMetric::CountMetric)
49
- widget.metrics.first.token.should == :first_metric
50
- widget.metrics.last.should be_a(FnordMetric::CountMetric)
51
- widget.metrics.last.token.should == :second_metric
52
- end
53
-
54
- it "should return the right ticks for 1h intervals" do
55
- t = Time.now
56
- widget = FnordMetric::Widget.new(:range => (t-2.days)..t, :tick => 1.hour)
57
- widget.ticks.length.should == 49
58
- ranges_should_match! widget.ticks.first, ((t-48.hours)..(t-47.hours))
59
- ranges_should_match! widget.ticks.last, (t..(t+1.hour))
60
- end
61
-
62
- it "should generate a default range for daily graphs" do
63
- widget = FnordMetric::Widget.new(:tick => 1.day)
64
- Delorean.time_travel_to(Time.utc(1992,01,13,18,23,23)) do
65
- widget.default_range.first.should == Time.utc(1991,12,15,00,00,00)
66
- widget.default_range.last.should == Time.utc(1992,1,13,23,59,59)
67
- end
68
- end
69
-
70
- it "should generate ticks with default range for daily graphs" do
71
- widget = FnordMetric::Widget.new(:tick => 1.day)
72
- Delorean.time_travel_to(Time.utc(1992,01,13,18,23,23)) do
73
- widget.ticks.length.should == 30
74
- widget.ticks.first.first.utc.should == Time.utc(1991,12,15,00,00,00)
75
- widget.ticks.first.last.utc.should == Time.utc(1991,12,16,00,00,00)
76
- widget.ticks.last.first.utc.should == Time.utc(1992,1,13,0,0,0)
77
- widget.ticks.last.last.utc.should == Time.utc(1992,1,14,0,0,0)
78
- end
79
- end
80
-
81
- it "should generate a default range for hourly graphs" do
82
- widget = FnordMetric::Widget.new(:tick => 1.hour)
83
- Delorean.time_travel_to(Time.utc(1992,01,13,18,23,23)) do
84
- widget.default_range.first.should == Time.utc(1992,1,12,19,00,00)
85
- widget.default_range.last.should == Time.utc(1992,1,13,18,59,59)
86
- end
87
- end
88
-
89
- it "should generate a default range for hourly graphs" do
90
- widget = FnordMetric::Widget.new(:tick => 1.hour)
91
- Delorean.time_travel_to(Time.utc(1992,01,13,18,23,23)) do
92
- widget.ticks.length.should == 24
93
- widget.ticks.first.first.utc.should == Time.utc(1992,1,12,19,00,00)
94
- widget.ticks.first.last.utc.should == Time.utc(1992,1,12,20,00,00)
95
- widget.ticks.last.first.utc.should == Time.utc(1992,1,13,18,0,0)
96
- widget.ticks.last.last.utc.should == Time.utc(1992,1,13,19,00,00)
97
- end
98
- end
99
-
100
- private
101
-
102
- def ranges_should_match!(a, b)
103
- (a.first.to_i - b.first.to_i).should == 0
104
- (a.last.to_i - b.last.to_i).should == 0
105
- end
106
-
107
- end
@@ -1,56 +0,0 @@
1
- $: << ::File.expand_path('../../lib', __FILE__)
2
-
3
- require "rubygems"
4
- require "fnordmetric"
5
- require "thin"
6
-
7
- FnordMetric.metric(:passengers_total, :count => true, :types => [:car_seen])
8
- FnordMetric.metric(:blubbs, :sum => :passengers, :types => [:car_seen])
9
- FnordMetric.metric(:passengers_red_car, :sum => :passengers, :filter => { :colors => :red }, :types => [:car_seen])
10
- FnordMetric.metric(:passengers_blue_car, :sum => :passengers, :filter => { :colors => :blue }, :types => [:car_seen])
11
-
12
- FnordMetric.metric(:blue_to_red_ratio, :combine => lambda{ |x|
13
- x.passengers_blue_car / x.passengers_red_car
14
- })
15
-
16
-
17
- FnordMetric.dashboard 'Passengers' do |passengers|
18
-
19
- passengers.add_widget FnordMetric.widget(:passengers_total_timeline,
20
- :metrics => :passengers_total,
21
- :tick => 4.minutes,
22
- :title => "Passengers total",
23
- :type => :timeline
24
- )
25
-
26
- passengers.add_widget FnordMetric.widget(:passenger_blue_red_timeline,
27
- :metrics => [:passengers_blue_car, :passengers_red_car],
28
- :title => "Passengers (red/blue)",
29
- :type => :timeline
30
- )
31
-
32
- passengers.add_widget FnordMetric.widget(:passenger_br_ratio_timeline,
33
- :metrics => :blue_to_red_ratio,
34
- :title => "Passenger blue/red Ratio",
35
- :type => :timeline
36
- )
37
-
38
- end
39
-
40
- FnordMetric.dashboard 'Cars & Bikes' do |cars|
41
-
42
- cars.add_widget FnordMetric.widget(:passenger_br_ratio_timeline,
43
- :metrics => :blue_to_red_ratio,
44
- :title => "Passenger blue/red Ratio",
45
- :type => :timeline
46
- )
47
-
48
- end
49
-
50
-
51
- Mongoid.configure do |c|
52
- c.master = Mongo::Connection.new.db("myfnordmetric")
53
- end
54
-
55
- app = FnordMetric::App.new
56
- Thin::Server.start('127.0.0.1', 2323, app)
@@ -1,26 +0,0 @@
1
- require "rubygems"
2
- require "redis"
3
- require "json"
4
-
5
- if ARGV.length != 1
6
- puts "usage: #{$0} /path/to/dump.json"
7
- exit!
8
- end
9
-
10
- file_path = ARGV[0]
11
-
12
- puts "opening #{file_path}"
13
- file = File.open(file_path)
14
-
15
- puts "reading file..."
16
- events = file.read.split("\n")
17
-
18
- puts "pushing #{events.length} events to redis"
19
- redis = Redis.new
20
- total_events = events.length
21
- events.each_with_index do |event,n|
22
- my_uuid = rand(9999999999999999999).to_s # generate a unique event id ;)
23
- redis.lpush("fnordmetric-queue", my_uuid)
24
- redis.set("fnordmetric-event-#{my_uuid}", event)
25
- puts "#{n}/#{total_events}" if (n%100==0)
26
- end
@@ -1,543 +0,0 @@
1
- var FnordMetric = (function(){
2
-
3
- var canvasElem = false;
4
-
5
- var currentNamespace = false;
6
- var currentView = false;
7
-
8
- function decPrint(val){
9
- return (val < 10 ? '0'+val : val);
10
- }
11
-
12
- function formatTimeOfDay(_time){
13
- var time = new Date();
14
- time.setTime(_time*1000);
15
- return decPrint(time.getHours()) + ':' +
16
- decPrint(time.getMinutes()) + ':' +
17
- decPrint(time.getSeconds());
18
- }
19
-
20
- function formatTimeSince(time){
21
- var now = new Date().getTime()/1000;
22
- var since = now - time;
23
- if(since < 60){
24
- return parseInt(since) + 's';
25
- } else if(since<3600){
26
- return parseInt(since/60) + 'm';
27
- } else if(since<(3600*24)){
28
- return parseInt(since/3600) + 'h';
29
- } else {
30
- return ">1d"
31
- }
32
- }
33
-
34
- var numbersWidget = function(opts){
35
-
36
- function render(){
37
- //console.log(opts);
38
- }
39
-
40
- return {
41
- render: render
42
- };
43
-
44
- };
45
-
46
- var timelineWidget = function(opts){
47
-
48
- function render(){
49
-
50
- var labels = opts.labels;
51
- var series = opts.series;
52
-
53
- var elem_id = "fm_graph_"+parseInt(Math.random()*99999);
54
- var elem_inner = $('.inner', opts.elem);
55
- elem_inner.append($('<div id="'+elem_id+'"></div>'));
56
-
57
- var width = elem_inner.width();
58
- var height = 240;
59
- var canvas = Raphael(elem_id, width, height+30);
60
- var xtick = width / (labels.length-1);
61
-
62
- var label_mod = Math.ceil((labels.length/10));
63
-
64
- if(opts.independent_y_axis){
65
- var max = false;
66
- } else {
67
- var amax = [];
68
- $(series).each(function(n,_series){
69
- amax.push(Math.max.apply(Math, _series.data));
70
- });
71
- var max = Math.max.apply(Math, amax);
72
- }
73
-
74
- $(series).each(function(n,_series){
75
-
76
- //var path_string = "M0,"+height;
77
- var path_string = "";
78
- var _max = max;
79
-
80
- if(!_max){ _max = Math.max.apply(Math, _series.data); }
81
-
82
- _max = _max * 1.1;
83
-
84
- $(_series.data).each(function(i,v){
85
-
86
- var p_x = (i*xtick);
87
- var p_y = (height-((v/_max)*height));
88
-
89
- path_string += ( ( i == 0 ? "M" : "L" ) + p_x + ',' + p_y );
90
-
91
- if(i%label_mod==0){
92
- canvas.text(p_x, height+10, labels[i]).attr({
93
- font: '10px Helvetica, Arial',
94
- fill: "#777"
95
- });
96
- }
97
-
98
- canvas.circle(p_x, p_y, 4).attr({
99
- fill: _series.color,
100
- stroke: '#fff',
101
- "stroke-width": 1,
102
- }).toBack();
103
-
104
-
105
- var htrgt = canvas.rect(p_x - 20, p_y - 20, 40, 40).attr({
106
- stroke: "none",
107
- fill: "#fff",
108
- opacity: 0
109
- }).toFront();
110
-
111
- (function(htrgt){
112
-
113
- var t_y = p_y + 9;
114
- var ttt = canvas.text(p_x, t_y+10, v).attr({
115
- font: '12px Helvetica, Arial',
116
- fill: "#fff",
117
- opacity: 0
118
- });
119
-
120
- var tttb = ttt.getBBox();
121
- var ttw = tttb.width+20;
122
- var tt = canvas.rect(p_x-(ttw/2), t_y, ttw, 22, 5).attr({
123
- stroke: "none",
124
- fill: "#000",
125
- opacity: 0
126
- }).toBack();
127
-
128
-
129
- $(htrgt[0]).hover(function(){
130
- tt.animate({ opacity: 0.8 }, 300);
131
- ttt.animate({ opacity: 0.8 }, 300);
132
- }, function(){
133
- tt.animate({ opacity: 0 }, 300);
134
- ttt.animate({ opacity: 0 }, 300);
135
- });
136
-
137
- })(htrgt);
138
-
139
- });
140
-
141
- if(_max>0){
142
-
143
- canvas.path(path_string).attr({
144
- stroke: _series.color,
145
- "stroke-width": 3,
146
- "stroke-linejoin": 'round'
147
- }).toBack();
148
-
149
- path_string += "L"+width+","+height+" L0,"+height+" Z";
150
-
151
- canvas.path(path_string).attr({
152
- stroke: "none",
153
- fill: _series.color,
154
- opacity: 0.1
155
- }).toBack();
156
-
157
- }
158
-
159
-
160
- });
161
-
162
- canvas.drawGrid(0, 0, width, height, 1, 6, "#ececec");
163
-
164
- }
165
-
166
- return {
167
- render: render
168
- };
169
-
170
- };
171
-
172
- var sessionView = (function(){
173
-
174
- var listElem = $('<ul class="session_list"></ul>');
175
- var feedInnerElem = $('<ul class="feed_inner"></ul>');
176
- var typeListElem = $('<ul class="event_type_list"></ul>');
177
- var filterElem = $('<div class="events_sidebar"></div>').html(
178
- $('<div class="headbar"></div>').html('Event Types')
179
- ).append(typeListElem);
180
- var feedElem = $('<div class="sessions_feed"></div>').html(
181
- $('<div class="headbar"></div>').html('Event Feed')
182
- ).append(feedInnerElem);
183
- var sideElem = $('<div class="sessions_sidebar"></div>').html(
184
- $('<div class="headbar"></div>').html('Active Users')
185
- ).append(listElem);
186
-
187
- var eventsPolledUntil = false;
188
- var eventsFilter = [];
189
- var sessionData = {};
190
- var pollRunning = true;
191
-
192
- function load(elem){
193
- eventsPolledUntil = parseInt(new Date().getTime()/10000);
194
- elem.html('')
195
- .append(filterElem)
196
- .append(feedElem)
197
- .append(sideElem);
198
- startPoll();
199
- loadEventTypes();
200
- };
201
-
202
- function resize(_width, _height){
203
- $('.sessions_feed').width(_width-452);
204
- };
205
-
206
- function startPoll(){
207
- (doSessionPoll())();
208
- (doEventsPoll())();
209
- sessionView.session_poll = window.setInterval(doSessionPoll(), 1000);
210
- };
211
-
212
- function stopPoll(){
213
- pollRunning = false;
214
- window.clearInterval(sessionView.session_poll);
215
- }
216
-
217
- function doSessionPoll(){
218
- return (function(){
219
- $.ajax({
220
- url: '/'+currentNamespace+'/sessions',
221
- success: callbackSessionPoll()
222
- });
223
- });
224
- };
225
-
226
- function loadEventHistory(event_type){
227
- feedInnerElem.html('');
228
- $.ajax({
229
- url: '/'+currentNamespace+'/events?type='+event_type,
230
- success: function(_data, _status){
231
- var data = JSON.parse(_data).events;
232
- for(var n=data.length; n >= 0; n--){
233
- if(data[n]){ renderEvent(data[n]); }
234
- }
235
- }
236
- });
237
- }
238
-
239
- function callbackSessionPoll(){
240
- return (function(_data, _status){
241
- $.each(JSON.parse(_data).sessions, function(i,v){
242
- updateSession(v);
243
- });
244
- sortSessions();
245
- });
246
- };
247
-
248
- function loadEventTypes(){
249
- $.ajax({
250
- url: '/'+currentNamespace+'/event_types',
251
- success: function(_data){
252
- var data = JSON.parse(_data);
253
- $(data.types).each(function(i,v){
254
- if(v.slice(0,5)!='_set_'){ addEventType(v,v); }
255
- });
256
- }
257
- });
258
- };
259
-
260
- function addEventType(type, display){
261
- typeListElem.append(
262
- $('<li class="event_type"></li>').append(
263
- $('<span class="history"></span>').html('history')
264
- .click(function(){
265
- $('.event_type_list .event_type input').attr('checked', false);
266
- $('input', $(this).parent()).attr('checked', true);
267
- updateEventFilter(); loadEventHistory(type);
268
- })
269
- ).append(
270
- $('<input type="checkbox" />').attr('checked', true)
271
- .click(function(){ updateEventFilter(); })
272
- ).append(
273
- $('<span></span>').html(display)
274
- ).attr('rel', type)
275
- );
276
- }
277
-
278
- function updateEventFilter(){
279
- var _unchecked_types = [];
280
- $('ul.event_type_list li.event_type').each(function(i,v){
281
- if(!$('input', v).attr('checked')){
282
- _unchecked_types.push($(v).attr('rel'));
283
- }
284
- });
285
- eventsFilter = _unchecked_types;
286
- }
287
-
288
- function doEventsPoll(){
289
- return (function(){
290
- $.ajax({
291
- url: '/'+currentNamespace+'/events?since='+eventsPolledUntil,
292
- success: callbackEventsPoll()
293
- });
294
- });
295
- };
296
-
297
- function callbackEventsPoll(){
298
- return (function(_data, _status){
299
- var data = JSON.parse(_data)
300
- var events = data.events;
301
- var timout = 1000;
302
- var maxevents = 200;
303
- if(events.length > 0){
304
- timeout = 200;
305
- eventsPolledUntil = parseInt(events[0]._time)-1;
306
- }
307
- for(var n=events.length-1; n >= 0; n--){
308
- var v = events[n];
309
- if(eventsFilter.indexOf(v._type) == -1){
310
- if(parseInt(v._time)<=eventsPolledUntil){
311
- renderEvent(v);
312
- }
313
- }
314
- };
315
- var elems = $("p", feedInnerElem);
316
- for(var n=maxevents; n < elems.length; n++){
317
- $(elems[n]).remove();
318
- }
319
- if(pollRunning){
320
- window.setTimeout(doEventsPoll(), timout);
321
- }
322
- });
323
- };
324
-
325
- function updateSession(session_data){
326
- sessionData[session_data.session_key] = session_data;
327
- renderSession(session_data);
328
- }
329
-
330
- function sortSessions(){
331
- console.log("fixme: sort and splice to 100");
332
- }
333
-
334
- function renderSession(session_data){
335
-
336
- var session_name = session_data["_name"];
337
- var session_time = formatTimeSince(session_data["_updated_at"]);
338
- var session_elem = $('li[data-session='+session_data["session_key"]+']:first');
339
-
340
- if(session_elem.length>0){
341
-
342
- if(session_data["_picture"] && (session_data["_picture"].length > 1)){
343
- $('.picture img', session_elem).attr('src', session_data["_picture"])
344
- }
345
-
346
- if(session_name){
347
- $('.name', session_elem).html(session_name);
348
- }
349
-
350
- $('.time', session_elem).html(session_time);
351
-
352
- } else {
353
-
354
- var session_picture = $('<img width="18" />');
355
-
356
- if(!session_name){
357
- session_name = session_data["session_key"].substr(0,15)
358
- };
359
-
360
- if(session_data["_picture"]){
361
- session_picture.attr('src', session_data["_picture"]);
362
- };
363
-
364
- listElem.append(
365
- $('<li class="session"></li>').append(
366
- $('<div class="picture"></div>').html(session_picture)
367
- ).append(
368
- $('<span class="name"></span>').html(session_name)
369
- ).append(
370
- $('<span class="time"></span>').html(session_time)
371
- ).attr('data-session', session_data["session_key"])
372
- );
373
-
374
- }
375
- };
376
-
377
- function renderEvent(event_data){
378
- var event_time = $('<span class="time"></span>');
379
- var event_message = $('<span class="message"></span>');
380
- var event_props = $('<span class="properties"></span>');
381
- var event_picture = $('<div class="picture"></picture>');
382
-
383
- var event_type = event_data._type;
384
-
385
- if(!event_type){ return true; }
386
-
387
- if(event_data._message){
388
- event_message.html(event_data._message);
389
- } else if(event_type=="_pageview"){
390
- event_message.html("Pageview: " + event_data.url);
391
- } else if(event_type.substr(0,5) == '_set_'){
392
- return true; /* dont render */
393
- } else {
394
- event_message.html(event_type);
395
- }
396
-
397
- event_time.html(formatTimeOfDay(event_data._time));
398
-
399
- if(event_data._session_key && event_data._session_key.length > 0){
400
- if(session_data=sessionData[event_data._session_key]){
401
- if(session_data._name){
402
- event_props.append(
403
- $('<strong></strong>').html(session_data._name)
404
- );
405
- }
406
- if(session_data._picture){
407
- event_picture.append(
408
- $('<img width="40" />').attr('src', session_data._picture)
409
- )
410
- }
411
- }
412
- }
413
-
414
- feedInnerElem.prepend(
415
- $('<li class="feed_event"></li>')
416
- .append(event_time)
417
- .append(event_picture)
418
- .append(event_message)
419
- .append(event_props)
420
- );
421
- }
422
-
423
- function close(){
424
- stopPoll();
425
- };
426
-
427
- return {
428
- load: load,
429
- resize: resize,
430
- close: close
431
- };
432
-
433
- });
434
-
435
-
436
- var dashboardView = (function(dashboard_name){
437
-
438
- var widgets = [];
439
- var viewport = null;
440
-
441
- function load(_viewport){
442
- viewport = _viewport.html('');
443
- /*alert('yay, new dashboard view loaded: ' + dashboard_name);*/
444
- $.ajax({
445
- url: '/'+currentNamespace+'/dashboard/'+dashboard_name,
446
- success: function(resp, status){
447
- var conf = JSON.parse(resp);
448
- renderWidgets(conf.widgets);
449
- }
450
- });
451
- };
452
-
453
- function renderWidgets(_widgets){
454
- for(wkey in _widgets){
455
- var widget = _widgets[wkey];
456
- widget["elem"] = $('<div class="widget"></div>').append(
457
- $('<div class="headbar"></div>').html(widget.title)
458
- ).append(
459
- $('<div class="inner"></div>')
460
- );
461
- widgets[wkey] = widget;
462
- viewport.append(widget.elem);
463
- resizeWidget(wkey);
464
- renderWidget(wkey);
465
- };
466
- resize();
467
- };
468
-
469
- function renderWidget(wkey){
470
- var widget = widgets[wkey];
471
- /* argh... */
472
- if(widget.klass=='TimelineWidget'){ timelineWidget(widget).render(); }
473
- if(widget.klass=='NumbersWidget'){ numbersWidget(widget).render(); }
474
- };
475
-
476
- function resizeWidget(wkey){
477
- var widget = widgets[wkey];
478
- var wwperc = widgets[wkey].width;
479
- if(!wwperc){ wwperc = 100; }
480
- var wwidth = viewport.width() * (wwperc/100.0);
481
- if(wwperc==100){
482
- widgets[wkey].elem.addClass('full_width');
483
- } else { wwidth -= 1; }
484
- widget.elem.width(wwidth);
485
- }
486
-
487
- function resize(){
488
- for(wkey in widgets){
489
- resizeWidget(wkey);
490
- };
491
- };
492
-
493
- function close(){
494
-
495
- };
496
-
497
- return {
498
- load: load,
499
- resize: resize,
500
- close: close
501
- };
502
-
503
- });
504
-
505
-
506
- function renderDashboard(_dash){
507
- loadView(dashboardView(_dash));
508
- };
509
-
510
- function renderSessionView(){
511
- loadView(sessionView());
512
- }
513
-
514
- function loadView(_view){
515
- if(currentView){ currentView.close(); }
516
- canvasElem.html('loading!');
517
- currentView = _view;
518
- currentView.load(canvasElem);
519
- resizeView();
520
- };
521
-
522
- function resizeView(){
523
- currentView.resize(
524
- canvasElem.innerWidth(),
525
- canvasElem.innerHeight()
526
- );
527
- };
528
-
529
- function init(_namespace, _canvasElem){
530
- canvasElem = _canvasElem;
531
- currentNamespace = _namespace;
532
- loadView(sessionView());
533
- };
534
-
535
- return {
536
- p: '/fnordmetric/',
537
- renderDashboard: renderDashboard,
538
- renderSessionView: renderSessionView,
539
- resizeView: resizeView,
540
- init: init
541
- };
542
-
543
- })();