fnordmetric 0.5.1 → 0.5.2

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 (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
- })();