gricer 0.0.1
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/MIT-LICENSE +20 -0
- data/README.rdoc +84 -0
- data/Rakefile +49 -0
- data/app/assets/images/gricer/fluid/breadcrumb.png +0 -0
- data/app/assets/javascripts/gricer.js.coffee +85 -0
- data/app/assets/javascripts/gricer_backend_jquery.js.coffee +352 -0
- data/app/assets/javascripts/jquery.flot.js +2599 -0
- data/app/assets/javascripts/jquery.flot.pie.js +750 -0
- data/app/assets/javascripts/jquery.flot.resize.js +60 -0
- data/app/assets/javascripts/jquery.flot.symbol.js +70 -0
- data/app/assets/javascripts/worldmap.js +146 -0
- data/app/assets/stylesheets/gricer/fluid-jquery-ui.css.scss +1298 -0
- data/app/assets/stylesheets/gricer/fluid.css.scss +240 -0
- data/app/assets/stylesheets/gricer/helpers/css3.css.scss +21 -0
- data/app/controllers/gricer/base_controller.rb +141 -0
- data/app/controllers/gricer/capture_controller.rb +42 -0
- data/app/controllers/gricer/dashboard_controller.rb +18 -0
- data/app/controllers/gricer/requests_controller.rb +42 -0
- data/app/controllers/gricer/sessions_controller.rb +24 -0
- data/app/helpers/gricer/base_helper.rb +22 -0
- data/app/models/gricer/agent.rb +789 -0
- data/app/models/gricer/request.rb +239 -0
- data/app/models/gricer/session.rb +433 -0
- data/app/views/gricer/capture/index.html.erb +1 -0
- data/app/views/gricer/dashboard/_menu.html.erb +10 -0
- data/app/views/gricer/dashboard/_overview.html.erb +33 -0
- data/app/views/gricer/dashboard/index.html.erb +19 -0
- data/config/routes.rb +51 -0
- data/lib/gricer.rb +36 -0
- data/lib/gricer/action_controller/base.rb +28 -0
- data/lib/gricer/action_controller/track.rb +132 -0
- data/lib/gricer/active_model/statistics.rb +167 -0
- data/lib/gricer/config.rb +125 -0
- data/lib/gricer/engine.rb +9 -0
- data/lib/gricer/localization.rb +3 -0
- data/lib/tasks/gricer_tasks.rake +92 -0
- data/spec/controllers/gricer/base_controller_spec.rb +207 -0
- data/spec/controllers/gricer/capture_controller_spec.rb +44 -0
- data/spec/controllers/gricer/dashboard_controller_spec.rb +44 -0
- data/spec/controllers/gricer/requests_controller_spec.rb +36 -0
- data/spec/controllers/gricer/sessions_controller_spec.rb +37 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/assets/stylesheets/dashboard.css +4 -0
- data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/spec/dummy/app/assets/stylesheets/sessions.css +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +23 -0
- data/spec/dummy/app/controllers/dashboard_controller.rb +19 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/dashboard_helper.rb +2 -0
- data/spec/dummy/app/views/dashboard/index.html.erb +236 -0
- data/spec/dummy/app/views/layouts/application.html.erb +16 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +48 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/cucumber.yml +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +27 -0
- data/spec/dummy/config/environments/production.rb +51 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +12 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +11 -0
- data/spec/dummy/db/schema.rb +241 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/helpers/gricer/base_helper_spec.rb +28 -0
- data/spec/lib/gricer/action_controller/track_spec.rb +63 -0
- data/spec/models/gricer/agent_spec.rb +829 -0
- data/spec/models/gricer/request_spec.rb +145 -0
- data/spec/models/gricer/session_spec.rb +209 -0
- data/spec/routing/capture_routes_spec.rb +6 -0
- data/spec/routing/dashboard_routes_spec.rb +9 -0
- data/spec/routing/requests_routes_spec.rb +90 -0
- data/spec/routing/sessions_routes_spec.rb +115 -0
- data/spec/spec_helper.rb +23 -0
- metadata +185 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
@import 'helpers/css3';
|
2
|
+
|
3
|
+
.gricer-stat {
|
4
|
+
position: absolute;
|
5
|
+
top: 0;
|
6
|
+
bottom: 0;
|
7
|
+
width: 100%;
|
8
|
+
overflow: none;
|
9
|
+
font-size: 11px;
|
10
|
+
line-height: 15px;
|
11
|
+
font-family: 'Lucida Grande', 'Arial', 'Helvetica', 'Sans-Serif';
|
12
|
+
|
13
|
+
#gricer-header {
|
14
|
+
background: #000;
|
15
|
+
color: #000;
|
16
|
+
line-height: 20px;
|
17
|
+
height: 20px;
|
18
|
+
background: #eee;
|
19
|
+
@include gradient(#eee,#ccc);
|
20
|
+
|
21
|
+
.path {
|
22
|
+
a {
|
23
|
+
display: block;
|
24
|
+
float: left;
|
25
|
+
color: #000;
|
26
|
+
background: image-url("gricer/fluid/breadcrumb.png") right top no-repeat;
|
27
|
+
height: 20px;
|
28
|
+
line-height: 20px;
|
29
|
+
padding: 0 15px 0 8px;
|
30
|
+
text-decoration: none;
|
31
|
+
}
|
32
|
+
|
33
|
+
a:last-child {
|
34
|
+
font-weight: bold;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
.dates {
|
39
|
+
padding: 0 7px;
|
40
|
+
float: right;
|
41
|
+
input {
|
42
|
+
width: 8em;
|
43
|
+
height: 11px;
|
44
|
+
padding: 2px;
|
45
|
+
border: none;
|
46
|
+
background: transparent;
|
47
|
+
}
|
48
|
+
#gricer-from-field {
|
49
|
+
text-align: right;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
#gricer-menu {
|
55
|
+
position: absolute;
|
56
|
+
float: left;
|
57
|
+
width: 182px;
|
58
|
+
padding: 4px 0;
|
59
|
+
background: #dfe4eb;
|
60
|
+
border-right: 1px solid #b4b4b4;
|
61
|
+
top: 20px;
|
62
|
+
|
63
|
+
bottom: 0;
|
64
|
+
overflow: auto;
|
65
|
+
|
66
|
+
a, span {
|
67
|
+
display: block;
|
68
|
+
text-decoration: none;
|
69
|
+
color: #000;
|
70
|
+
border-top: 1px solid transparent;
|
71
|
+
padding: 2px 8px;
|
72
|
+
&.active {
|
73
|
+
border-top: 1px solid #a3b1cc;
|
74
|
+
background: #94a6c5;
|
75
|
+
@include gradient(#b0bed7, #8195b8);
|
76
|
+
color: #fff;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
span {
|
81
|
+
color: #999;
|
82
|
+
font-weight: bold;
|
83
|
+
}
|
84
|
+
|
85
|
+
div a {
|
86
|
+
padding-left: 16px;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
#gricer-container
|
91
|
+
{
|
92
|
+
position: absolute;
|
93
|
+
float: left;
|
94
|
+
left: 183px;
|
95
|
+
top: 20px;
|
96
|
+
right: 0;
|
97
|
+
bottom: 0;
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
#gricer-container {
|
102
|
+
|
103
|
+
.overview {
|
104
|
+
position: absolute;
|
105
|
+
top: 0;
|
106
|
+
bottom: 0;
|
107
|
+
width: 100%;
|
108
|
+
overflow: auto;
|
109
|
+
padding: 4px 8px;
|
110
|
+
font-size: 30px;
|
111
|
+
line-height: 45px;
|
112
|
+
}
|
113
|
+
|
114
|
+
.flot-chart {
|
115
|
+
position: absolute;
|
116
|
+
background: #666f83;
|
117
|
+
color: #999;
|
118
|
+
top: 0;
|
119
|
+
left: 0;
|
120
|
+
right: 0;
|
121
|
+
padding: 0 2.5%;
|
122
|
+
height: 250px;
|
123
|
+
#flot-line-chart, #flot-pie-chart {
|
124
|
+
width: 100%;
|
125
|
+
height: 250px;
|
126
|
+
}
|
127
|
+
|
128
|
+
}
|
129
|
+
|
130
|
+
.type-selector {
|
131
|
+
position: absolute;
|
132
|
+
right: 4px;
|
133
|
+
top: 4px;
|
134
|
+
|
135
|
+
span, a {
|
136
|
+
padding: 2px 4px;
|
137
|
+
background: #ccc;
|
138
|
+
@include gradient(#ddd, #bbb);
|
139
|
+
border-right: 1px solid #b4b4b4;
|
140
|
+
color: #000;
|
141
|
+
text-decoration: none;
|
142
|
+
}
|
143
|
+
|
144
|
+
span {
|
145
|
+
background: #666;
|
146
|
+
@include gradient(#555, #777);
|
147
|
+
color: #fff;
|
148
|
+
}
|
149
|
+
|
150
|
+
span:first-child,
|
151
|
+
a:first-child {
|
152
|
+
@include border-radius(9px 0 0 9px);
|
153
|
+
padding-left: 9px;
|
154
|
+
}
|
155
|
+
|
156
|
+
span:last-child,
|
157
|
+
a:last-child {
|
158
|
+
@include border-radius(0 9px 9px 0);
|
159
|
+
border-right: none;
|
160
|
+
padding-right: 9px;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
.data {
|
165
|
+
position: absolute;
|
166
|
+
top: 250px;
|
167
|
+
bottom: 0;
|
168
|
+
width: 100%;
|
169
|
+
overflow: auto;
|
170
|
+
}
|
171
|
+
table {
|
172
|
+
width: 100%;
|
173
|
+
padding: 0;
|
174
|
+
border-spacing: 0;
|
175
|
+
empty-cells: show;
|
176
|
+
|
177
|
+
th, td {
|
178
|
+
width: auto;
|
179
|
+
padding: 2px 4px;
|
180
|
+
font-size: 11px;
|
181
|
+
|
182
|
+
a {
|
183
|
+
color: #009;
|
184
|
+
text-decoration: none;
|
185
|
+
}
|
186
|
+
|
187
|
+
.badge {
|
188
|
+
float: left;
|
189
|
+
display: block;
|
190
|
+
@include border-radius(5px);
|
191
|
+
margin: 3px;
|
192
|
+
width: 10px;
|
193
|
+
height: 10px;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
tr:nth-child(even) {
|
198
|
+
th, td {
|
199
|
+
background-color: #f3f5fa;
|
200
|
+
white-space: nowrap;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
tr:first-child {
|
205
|
+
th {
|
206
|
+
background: #eee;
|
207
|
+
@include gradient(#ccc,#eee);
|
208
|
+
border-bottom: 1px solid #b4b4b4;
|
209
|
+
border-right: 1px solid #b4b4b4;
|
210
|
+
overflow: hidden;
|
211
|
+
white-space: nowrap;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
th.number,
|
216
|
+
td.number {
|
217
|
+
text-align: right;
|
218
|
+
}
|
219
|
+
|
220
|
+
th.boolean,
|
221
|
+
td.boolean {
|
222
|
+
max-width: 30px;
|
223
|
+
}
|
224
|
+
|
225
|
+
td.boolean {
|
226
|
+
text-align: center;
|
227
|
+
}
|
228
|
+
|
229
|
+
&.process {
|
230
|
+
tr:nth-child(1) {
|
231
|
+
th {
|
232
|
+
text-align: center;
|
233
|
+
font-size: 9px;
|
234
|
+
lin-height: 11px;
|
235
|
+
}
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
@mixin border-radius($radius) {
|
2
|
+
-webkit-border-radius: $radius;
|
3
|
+
-moz-border-radius: $radius;
|
4
|
+
-o-border-radius: $radius;
|
5
|
+
-ms-border-radius: $radius;
|
6
|
+
-khtml-border-radius: $radius;
|
7
|
+
border-radius: $radius;
|
8
|
+
}
|
9
|
+
|
10
|
+
@mixin gradient($from, $to) {
|
11
|
+
background-image: -webkit-gradient(linear, left top, left bottom, from($from), to($to));
|
12
|
+
background-image: -moz-linear-gradient($from, $to);
|
13
|
+
background-image: linear-gradient($from, $to);
|
14
|
+
}
|
15
|
+
|
16
|
+
@mixin rotate($degree: -90deg) {
|
17
|
+
-moz-transform:rotate($degree);
|
18
|
+
-webkit-transform:rotate($degree);
|
19
|
+
-o-transform:rotate($degree);
|
20
|
+
transform:rotate($degree);
|
21
|
+
}
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Gricer
|
2
|
+
# This is the base controller which is used by Gricer's basic statistics controllers
|
3
|
+
#
|
4
|
+
# You can use this to make your own statistics controllers from a scaffold.
|
5
|
+
class BaseController < ::ApplicationController
|
6
|
+
before_filter :guess_from_thru
|
7
|
+
helper BaseHelper
|
8
|
+
|
9
|
+
layout ::Gricer.config.admin_layout if ::Gricer.config.admin_layout
|
10
|
+
|
11
|
+
# This action generates a JSON for a process statistics.
|
12
|
+
def process_stats
|
13
|
+
@items = basic_collection
|
14
|
+
|
15
|
+
handle_special_fields
|
16
|
+
|
17
|
+
data = {
|
18
|
+
alternatives: [
|
19
|
+
{
|
20
|
+
type: 'spread',
|
21
|
+
uri: url_for(action: "spread_stats", field: params[:field], filters: params[:filters], only_path: true)
|
22
|
+
},
|
23
|
+
{
|
24
|
+
type: 'process'
|
25
|
+
}
|
26
|
+
],
|
27
|
+
from: @stat_from.to_time.utc.to_i * 1000,
|
28
|
+
thru: @stat_thru.to_time.utc.to_i * 1000,
|
29
|
+
step: @stat_step.to_i * 1000,
|
30
|
+
data: @items.stat(params[:field], @stat_from, @stat_thru, @stat_step)
|
31
|
+
}
|
32
|
+
|
33
|
+
if further_details.keys.include? params[:field]
|
34
|
+
filters = (params[:filters] || {})
|
35
|
+
filters[params[:field]] = '%{self}'
|
36
|
+
|
37
|
+
data[:detail_uri] = url_for(action: "process_stats", field: further_details[params[:field]], filters: filters, only_path: true)
|
38
|
+
end
|
39
|
+
|
40
|
+
render json: data
|
41
|
+
end
|
42
|
+
|
43
|
+
# This action generates a JSON for a spread statistics.
|
44
|
+
def spread_stats
|
45
|
+
@items = basic_collection.between_dates(@stat_from, @stat_thru)
|
46
|
+
|
47
|
+
handle_special_fields
|
48
|
+
|
49
|
+
data = {
|
50
|
+
alternatives: [
|
51
|
+
{
|
52
|
+
type: 'spread'
|
53
|
+
},
|
54
|
+
{
|
55
|
+
type: 'process',
|
56
|
+
uri: url_for(action: "process_stats", field: params[:field], filters: params[:filters], only_path: true)
|
57
|
+
}
|
58
|
+
],
|
59
|
+
from: @stat_from.to_time.utc.to_i * 1000,
|
60
|
+
thru: @stat_thru.to_time.utc.to_i * 1000,
|
61
|
+
total: @items.count(:id),
|
62
|
+
data: @items.count_by(params[:field])
|
63
|
+
}
|
64
|
+
|
65
|
+
if further_details.keys.include? params[:field]
|
66
|
+
filters = (params[:filters] || {})
|
67
|
+
filters[params[:field]] = '%{self}'
|
68
|
+
|
69
|
+
data[:detail_uri] = url_for(action: "spread_stats", field: further_details[params[:field]], filters: filters, only_path: true)
|
70
|
+
end
|
71
|
+
|
72
|
+
render json: data
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
# @abstract
|
77
|
+
# Define which is the basic collection to be processed by the controller.
|
78
|
+
# This value has to be overwritten to use the process_stats or spread_stats actions.
|
79
|
+
def basic_collection
|
80
|
+
raise 'basic_collection must be defined'
|
81
|
+
end
|
82
|
+
|
83
|
+
# Define how to handle special fields
|
84
|
+
def handle_special_fields
|
85
|
+
if params[:filters]
|
86
|
+
params[:filters].each do |filter_attr, filter_value|
|
87
|
+
@items = @items.filter_by(filter_attr, filter_value)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Define how to handle further details
|
93
|
+
def further_details
|
94
|
+
{}
|
95
|
+
end
|
96
|
+
|
97
|
+
# Guess for which time range to display statistics
|
98
|
+
def guess_from_thru
|
99
|
+
begin
|
100
|
+
@stat_thru = Time.parse(params[:thru]).to_date
|
101
|
+
rescue
|
102
|
+
end
|
103
|
+
|
104
|
+
begin
|
105
|
+
@stat_from = Time.parse(params[:from]).to_date
|
106
|
+
rescue
|
107
|
+
end
|
108
|
+
|
109
|
+
if @stat_from.nil?
|
110
|
+
if @stat_thru.nil?
|
111
|
+
@stat_thru = Time.now.localtime.to_date - 1.day
|
112
|
+
end
|
113
|
+
@stat_from = @stat_thru - 1.week + 1.day
|
114
|
+
else
|
115
|
+
if @stat_thru.nil?
|
116
|
+
@stat_thru = @stat_from + 1.week - 1.day
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
@stat_step = 1.day
|
121
|
+
|
122
|
+
duration = @stat_thru - @stat_from
|
123
|
+
|
124
|
+
if duration < 90
|
125
|
+
@stat_step = 12.hours
|
126
|
+
end
|
127
|
+
|
128
|
+
if duration < 30
|
129
|
+
@stat_step = 6.hour
|
130
|
+
end
|
131
|
+
|
132
|
+
if duration < 10
|
133
|
+
@stat_step = 1.hour
|
134
|
+
end
|
135
|
+
|
136
|
+
#if @stat_thru - @stat_from > 12.month
|
137
|
+
# @stat_step = 4.week
|
138
|
+
#end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Gricer
|
2
|
+
# This is the controller to capture additional values from the Javascript track tag.
|
3
|
+
#
|
4
|
+
# @see Gricer::TrackHelper#gricer_track_tag
|
5
|
+
class CaptureController < ::ApplicationController
|
6
|
+
# This action stores the data submitted by the Javascript.
|
7
|
+
def index
|
8
|
+
gricer_request = Request.find_by_id(params[:id])
|
9
|
+
gricer_session = Session.find_by_id(session[:gricer_session])
|
10
|
+
|
11
|
+
if gricer_session
|
12
|
+
gricer_session.javascript = true
|
13
|
+
gricer_session.java = params[:j]
|
14
|
+
gricer_session.flash_version = params[:f] unless params[:f] == 'false'
|
15
|
+
gricer_session.silverlight_version = params[:sl] unless params[:sl] == 'false'
|
16
|
+
gricer_session.screen_width = params[:sx]
|
17
|
+
gricer_session.screen_height = params[:sy]
|
18
|
+
gricer_session.screen_size = "#{params[:sx]}x#{params[:sy]}" unless params[:sx].blank? or params[:sy].blank?
|
19
|
+
gricer_session.screen_depth = params[:sd]
|
20
|
+
gricer_session.save
|
21
|
+
|
22
|
+
if gricer_request and gricer_request.session == gricer_session
|
23
|
+
|
24
|
+
gricer_request.javascript = true
|
25
|
+
gricer_request.window_width = params[:wx]
|
26
|
+
gricer_request.window_height = params[:wy]
|
27
|
+
|
28
|
+
if gricer_request.save
|
29
|
+
render text: 'ok'
|
30
|
+
else
|
31
|
+
render text: 'session only', status: 500
|
32
|
+
end
|
33
|
+
return
|
34
|
+
else
|
35
|
+
render text: 'session only'
|
36
|
+
return
|
37
|
+
end
|
38
|
+
end
|
39
|
+
render text: 'failed', status: 500
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gricer
|
2
|
+
# This controller contains the dashboard for the administration view
|
3
|
+
class DashboardController < BaseController
|
4
|
+
# This action renders the frame for the statistics tool
|
5
|
+
def index
|
6
|
+
@sessions = Session.browsers.between_dates(@stat_from, @stat_thru)
|
7
|
+
@requests = Request.browsers.between_dates(@stat_from, @stat_thru)
|
8
|
+
end
|
9
|
+
|
10
|
+
# This action renderes the overview of some data in the statistics tool
|
11
|
+
def overview
|
12
|
+
@sessions = Session.browsers.between_dates(@stat_from, @stat_thru)
|
13
|
+
@requests = Request.browsers.between_dates(@stat_from, @stat_thru)
|
14
|
+
|
15
|
+
render partial: 'overview.html', locals: {sessions: @sessions, requests: @requests}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|