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.
- data/.travis.yml +1 -0
- data/VERSION +1 -1
- data/doc/preview1.png +0 -0
- data/doc/preview2.png +0 -0
- data/doc/ulm_stats.rb +622 -0
- data/doc/version +1 -0
- data/fnordmetric.gemspec +16 -38
- data/haml/app.haml +12 -5
- data/lib/fnordmetric.rb +3 -0
- data/lib/fnordmetric/app.rb +19 -10
- data/lib/fnordmetric/bars_widget.rb +26 -0
- data/lib/fnordmetric/context.rb +3 -3
- data/lib/fnordmetric/gauge.rb +20 -0
- data/lib/fnordmetric/gauge_calculations.rb +28 -4
- data/lib/fnordmetric/gauge_modifiers.rb +39 -6
- data/lib/fnordmetric/logger.rb +19 -0
- data/lib/fnordmetric/numbers_widget.rb +5 -15
- data/lib/fnordmetric/pie_widget.rb +23 -0
- data/lib/fnordmetric/standalone.rb +1 -1
- data/lib/fnordmetric/timeline_widget.rb +16 -23
- data/lib/fnordmetric/toplist_widget.rb +25 -0
- data/lib/fnordmetric/widget.rb +3 -3
- data/pub/{fnordmetric/fnordmetric.css → fnordmetric.css} +46 -36
- data/pub/fnordmetric.js +1069 -0
- data/pub/loader.gif +0 -0
- data/pub/{highcharts → vendor}/highcharts.js +0 -0
- data/pub/{jquery-1.6.1.min.js → vendor/jquery-1.6.1.min.js} +0 -0
- data/readme.rdoc +228 -311
- data/spec/app_spec.rb +63 -3
- data/spec/gauge_modifiers_spec.rb +157 -2
- data/spec/gauge_spec.rb +143 -12
- data/spec/widget_spec.rb +18 -18
- metadata +33 -58
- data/.document +0 -5
- data/_spec/app_spec.rb +0 -178
- data/_spec/cache_spec.rb +0 -53
- data/_spec/combine_metric_spec.rb +0 -19
- data/_spec/core_spec.rb +0 -50
- data/_spec/count_metric_spec.rb +0 -32
- data/_spec/dashboard_spec.rb +0 -67
- data/_spec/event_spec.rb +0 -46
- data/_spec/metric_spec.rb +0 -118
- data/_spec/report_spec.rb +0 -87
- data/_spec/sum_metric_spec.rb +0 -33
- data/_spec/widget_spec.rb +0 -107
- data/doc/example_server.rb +0 -56
- data/doc/import_dump.rb +0 -26
- data/pub/fnordmetric/fnordmetric.js +0 -543
- data/pub/fnordmetric/widget_numbers.js +0 -71
- data/pub/fnordmetric/widget_timeline.css +0 -0
- data/pub/fnordmetric/widget_timeline.js +0 -110
- data/pub/highcharts/adapters/mootools-adapter.js +0 -12
- data/pub/highcharts/adapters/mootools-adapter.src.js +0 -243
- data/pub/highcharts/adapters/prototype-adapter.js +0 -14
- data/pub/highcharts/adapters/prototype-adapter.src.js +0 -284
- data/pub/highcharts/highcharts.src.js +0 -11103
- data/pub/highcharts/modules/exporting.js +0 -22
- data/pub/highcharts/modules/exporting.src.js +0 -703
- data/pub/highcharts/themes/dark-blue.js +0 -268
- data/pub/highcharts/themes/dark-green.js +0 -268
- data/pub/highcharts/themes/gray.js +0 -262
- data/pub/highcharts/themes/grid.js +0 -97
- data/pub/raphael-min.js +0 -8
- data/pub/raphael-utils.js +0 -221
- data/ulm_stats.rb +0 -198
data/_spec/sum_metric_spec.rb
DELETED
@@ -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
|
data/_spec/widget_spec.rb
DELETED
@@ -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
|
data/doc/example_server.rb
DELETED
@@ -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)
|
data/doc/import_dump.rb
DELETED
@@ -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
|
-
})();
|