refinerycms-stats 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/admin/stats_controller.rb +5 -0
- data/app/models/stat.rb +7 -0
- data/app/views/admin/stats/index.html.erb +93 -0
- data/config/locales/en.yml +16 -0
- data/config/locales/fr.yml +16 -0
- data/config/routes.rb +11 -0
- data/lib/generators/refinerycms_stats_generator.rb +6 -0
- data/lib/refinerycms-stats.rb +20 -0
- data/lib/tasks/stats.rake +13 -0
- data/public/javascripts/refinerycms-stat-translate.js +33 -0
- data/public/javascripts/refinerycms-stat.js +446 -0
- data/public/stylesheets/stat.css +14 -0
- metadata +65 -0
data/app/models/stat.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
<% content_for :after_javascript_libraries do %>
|
2
|
+
<%= javascript_include_tag 'https://www.google.com/jsapi' %>
|
3
|
+
<%= javascript_include_tag 'refinerycms-stat' %>
|
4
|
+
<%= javascript_include_tag 'refinerycms-stat-translate' %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<div id="login" style="visibility:hidden;">
|
8
|
+
<form action='#'>
|
9
|
+
<span id='status'><%= t('.login_analytics_html', :analytics_page_code => RefinerySetting.find_or_set(:analytics_page_code, "UA-0000000-0")) %></span>
|
10
|
+
<br/>
|
11
|
+
<input id='buttonLogin' type='button' value='<%= t(".button_login_text") %>'/>
|
12
|
+
</form>
|
13
|
+
</div>
|
14
|
+
<div id="logged" style="visibility:hidden;">
|
15
|
+
<%= t('.link_analytics_html') %>
|
16
|
+
<div id="tabs">
|
17
|
+
<ul>
|
18
|
+
<li><a href="#tabs-1"><%= t('.title_page_views') %></a></li>
|
19
|
+
<li><a href="#tabs-2"><%= t('.title_visits') %></a></li>
|
20
|
+
<li><a href="#tabs-3"><%= t('.title_visitors') %></a></li>
|
21
|
+
<li><a href="#tabs-4"><%= t('.title_average_time') %></a></li>
|
22
|
+
<li><a href="#tabs-5"><%= t('.title_source') %></a></li>
|
23
|
+
<li><a href="#tabs-6"><%= t('.title_keywords') %></a></li>
|
24
|
+
</ul>
|
25
|
+
<div id="page_part_editors">
|
26
|
+
<div id="tabs-1" class="stat">
|
27
|
+
<div id='pageViewsTableDiv'></div>
|
28
|
+
<div id='pageViewsColumnChartDiv' style="display:inline"></div>
|
29
|
+
<div id='pageViewsChartDiv' style="display:inline"></div>
|
30
|
+
</div>
|
31
|
+
<div id="tabs-2" class="stat">
|
32
|
+
<table>
|
33
|
+
<tr>
|
34
|
+
<td><div id='visitsTableDiv' style="display:inline"></div></td>
|
35
|
+
<td><div id='visitsLineChartDiv' style="display:inline"></div></td>
|
36
|
+
</tr>
|
37
|
+
</table>
|
38
|
+
</div>
|
39
|
+
<div id="tabs-3" class="stat">
|
40
|
+
<table>
|
41
|
+
<tr>
|
42
|
+
<td><div id='visitorsTableDiv' style="display:inline"></div></td>
|
43
|
+
<td><div id='visitorsLineChartDiv' style="display:inline"></div></td>
|
44
|
+
</tr>
|
45
|
+
</table>
|
46
|
+
</div>
|
47
|
+
<div id="tabs-4" class="stat">
|
48
|
+
<table>
|
49
|
+
<tr>
|
50
|
+
<td><div id='timesOnSiteTableDiv' style="display:inline"></div></td>
|
51
|
+
<td><div id='timesOnSiteLineChartDiv' style="display:inline"></div></td>
|
52
|
+
</tr>
|
53
|
+
</table>
|
54
|
+
</div>
|
55
|
+
<div id="tabs-5" class="stat">
|
56
|
+
<table>
|
57
|
+
<tr>
|
58
|
+
<td><div id='sourceTableDiv' style="display:inline"></div></td>
|
59
|
+
<td><div id='sourcePieChartDiv' style="display:inline"></div></td>
|
60
|
+
</tr>
|
61
|
+
</table>
|
62
|
+
</div>
|
63
|
+
<div id="tabs-6" class="stat">
|
64
|
+
<table>
|
65
|
+
<tr>
|
66
|
+
<td><div id='keywordTableDiv' style="display:inline"></div></td>
|
67
|
+
<td><div id='keywordPieChartDiv' style="display:inline"></div></td>
|
68
|
+
</tr>
|
69
|
+
</table>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
<!-- For the API to work in IE, an image must be requested from the same
|
73
|
+
domain the script is hosted on. The image doesn't actually have
|
74
|
+
to exist, just be requested. More info here :
|
75
|
+
http://code.google.com/apis/gdata/client-js.html
|
76
|
+
-->
|
77
|
+
<img style='visibility:hidden' src='__utm.gif' alt='required for GData'/>
|
78
|
+
</div>
|
79
|
+
|
80
|
+
<% content_for :javascripts do %>
|
81
|
+
<script>
|
82
|
+
$(document).ready(function(){
|
83
|
+
$(function() {
|
84
|
+
$( "#tabs" ).tabs();
|
85
|
+
});
|
86
|
+
collectStatsData('<%= RefinerySetting.find_or_set(:analytics_page_code, "UA-0000000-0") %>');
|
87
|
+
});
|
88
|
+
</script>
|
89
|
+
<% end %>
|
90
|
+
|
91
|
+
<% content_for :stylesheets do %>
|
92
|
+
<%= stylesheet_link_tag 'stat' %>
|
93
|
+
<% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
en:
|
2
|
+
plugins:
|
3
|
+
stats:
|
4
|
+
title: Stats
|
5
|
+
admin:
|
6
|
+
stats:
|
7
|
+
index:
|
8
|
+
title_keywords: Keywords
|
9
|
+
title_page_views: Page views
|
10
|
+
title_source: Source
|
11
|
+
title_visitors: Visitors
|
12
|
+
title_visits: Visits
|
13
|
+
title_average_time: Average time
|
14
|
+
link_analytics_html: "You can view more data on the Google Analitycs site : <a href='https://www.google.com/analytics/'>https://www.google.com/analytics/</a><br/>."
|
15
|
+
login_analytics_html: "You need access to your Google Analytics data for this site.<br/>The code of your site for Google Analytics is : %{analytics_page_code}.<br/>You can change this code in <a href='/refinery/settings?search=analytics_page_code'>your settings</a>."
|
16
|
+
button_login_text: "Validate"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
fr:
|
2
|
+
plugins:
|
3
|
+
stats:
|
4
|
+
title: Statistiques
|
5
|
+
admin:
|
6
|
+
stats:
|
7
|
+
index:
|
8
|
+
title_keywords: Mot clés
|
9
|
+
title_page_views: Pages Vues
|
10
|
+
title_source: Sources
|
11
|
+
title_visitors: Visiteurs
|
12
|
+
title_visits: Visites
|
13
|
+
title_average_time: Temps moyen
|
14
|
+
link_analytics_html: "Vous pouvez consulter plus de données sur le site de Google Analitycs : <a href='https://www.google.com/analytics/'>https://www.google.com/analytics/</a><br/>."
|
15
|
+
login_analytics_html: "Vous devez autoriser l'accés à vos données Google Analytics pour ce site.<br/>Le code de votre site pour Google Analytics est : %{analytics_page_code}.<br/>Vous pouvez modifier ce code dans <a href='/refinery/settings?search=analytics_page_code'>vos paramètres</a>."
|
16
|
+
button_login_text: "Valider"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
::Refinery::Application.routes.draw do
|
2
|
+
resources :stats, :only => [:index, :show]
|
3
|
+
|
4
|
+
scope(:path => 'refinery', :as => 'admin', :module => 'admin') do
|
5
|
+
resources :stats, :except => :show do
|
6
|
+
collection do
|
7
|
+
post :update_positions
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'refinerycms-base'
|
2
|
+
|
3
|
+
module Refinery
|
4
|
+
module Stats
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
initializer "static assets" do |app|
|
7
|
+
app.middleware.insert_after ::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/public"
|
8
|
+
end
|
9
|
+
|
10
|
+
config.after_initialize do
|
11
|
+
Refinery::Plugin.register do |plugin|
|
12
|
+
plugin.name = "stats"
|
13
|
+
plugin.activity = {
|
14
|
+
:class => Stat
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
var I18n = I18n || {};
|
2
|
+
I18n.translations = {
|
3
|
+
"en":{
|
4
|
+
"admin":{
|
5
|
+
"stats":{
|
6
|
+
"title":"Title",
|
7
|
+
"visits":"Visits",
|
8
|
+
"visitors":"Visitors",
|
9
|
+
"page_views": "Page Views",
|
10
|
+
"keywords": "Keywords",
|
11
|
+
"date": "Date",
|
12
|
+
"average_time": "Average time",
|
13
|
+
"source": "Source",
|
14
|
+
"path": "Path"
|
15
|
+
}
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"fr":{
|
19
|
+
"admin":{
|
20
|
+
"stats":{
|
21
|
+
"title":"Titre",
|
22
|
+
"visits":"Visites",
|
23
|
+
"visitors":"Visiteurs",
|
24
|
+
"page_views": "Pages Vues",
|
25
|
+
"keywords": "Mots clés",
|
26
|
+
"date": "Date",
|
27
|
+
"average_time": "Temps moyen",
|
28
|
+
"source": "Source",
|
29
|
+
"path": "Chemin"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
};
|
@@ -0,0 +1,446 @@
|
|
1
|
+
// Copyright 2009 Google Inc. All Rights Reserved.
|
2
|
+
|
3
|
+
/* Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
* you may not use this file except in compliance with the License.
|
5
|
+
* You may obtain a copy of the License at
|
6
|
+
*
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
*
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
* See the License for the specific language governing permissions and
|
13
|
+
* limitations under the License.
|
14
|
+
*/
|
15
|
+
/**
|
16
|
+
* @fileoverview A very simple demo showing how easy it is to visualize
|
17
|
+
* GA data in the Google Visualization API. In this demo we show the
|
18
|
+
* top 10 tracked pages tiles and page URIs by pageviews in a
|
19
|
+
* Gviz Table, Pie Chart and Column Chart.
|
20
|
+
*/
|
21
|
+
|
22
|
+
var myService;
|
23
|
+
var dataTable;
|
24
|
+
var pageViewsTable;
|
25
|
+
var pageViewsChart;
|
26
|
+
var pageViewsColumnChart;
|
27
|
+
var visitsLineChart;
|
28
|
+
var visitsTable;
|
29
|
+
var timesOnSiteLineChart;
|
30
|
+
var timesOnSiteTable;
|
31
|
+
var sourcePieChart;
|
32
|
+
var sourceTable;
|
33
|
+
var keywordPieChart;
|
34
|
+
var keywordTable;
|
35
|
+
var scope = 'https://www.google.com/analytics/feeds';
|
36
|
+
var profileId;
|
37
|
+
var pageCode;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Initialize all the objects from the Google AJAX Apis once they
|
41
|
+
* have been loaded and are ready to use.
|
42
|
+
*/
|
43
|
+
function collectStatsData(pageCodeArg) {
|
44
|
+
var divLogin = document.getElementById('login');
|
45
|
+
var divLogged = document.getElementById('logged');
|
46
|
+
divLogin.style.visibility = 'hidden';
|
47
|
+
divLogged.style.visibility = 'hidden';
|
48
|
+
|
49
|
+
if (google.accounts.user.checkLogin(scope)) {
|
50
|
+
divLogin.style.visibility = 'hidden';
|
51
|
+
divLogged.style.visibility = 'visible';
|
52
|
+
divLogged.style.position = 'relative';
|
53
|
+
divLogged.style.position = "relative";
|
54
|
+
divLogged.style.top = -divLogin.offsetHeight+"px";
|
55
|
+
|
56
|
+
pageCode = pageCodeArg;
|
57
|
+
myService = new google.gdata.analytics.AnalyticsService('gaExportAPI_gViz_v1.0');
|
58
|
+
pageViewsTable = new google.visualization.Table(document.getElementById('pageViewsTableDiv'));
|
59
|
+
pageViewsChart = new google.visualization.PieChart(document.getElementById('pageViewsChartDiv'));
|
60
|
+
pageViewsColumnChart = new google.visualization.ColumnChart(document.getElementById('pageViewsColumnChartDiv'));
|
61
|
+
|
62
|
+
|
63
|
+
sourcePieChart = new google.visualization.PieChart(document.getElementById('sourcePieChartDiv'));
|
64
|
+
sourceTable = new google.visualization.Table(document.getElementById('sourceTableDiv'));
|
65
|
+
|
66
|
+
keywordPieChart = new google.visualization.PieChart(document.getElementById('keywordPieChartDiv'));
|
67
|
+
keywordTable = new google.visualization.Table(document.getElementById('keywordTableDiv'));
|
68
|
+
|
69
|
+
visitsLineChart = new google.visualization.LineChart(document.getElementById('visitsLineChartDiv'));
|
70
|
+
visitsTable = new google.visualization.Table(document.getElementById('visitsTableDiv'));
|
71
|
+
|
72
|
+
visitorsLineChart = new google.visualization.LineChart(document.getElementById('visitorsLineChartDiv'));
|
73
|
+
visitorsTable = new google.visualization.Table(document.getElementById('visitorsTableDiv'));
|
74
|
+
|
75
|
+
timesOnSiteLineChart = new google.visualization.LineChart(document.getElementById('timesOnSiteLineChartDiv'));
|
76
|
+
timesOnSiteTable = new google.visualization.Table(document.getElementById('timesOnSiteTableDiv'));
|
77
|
+
|
78
|
+
getWebPropertyFeed();
|
79
|
+
} else {
|
80
|
+
divLogin.style.visibility = 'visible';
|
81
|
+
divLogged.style.visibility = 'hidden';
|
82
|
+
document.getElementById('buttonLogin').onclick = handleLogin;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
function handleLogin() {
|
87
|
+
google.accounts.user.login(scope);
|
88
|
+
}
|
89
|
+
|
90
|
+
function getWebPropertyFeed() {
|
91
|
+
var webPropertyFeedUri = [scope, '/datasources/ga/accounts/',pageCode.split('-')[1],'/webproperties/',pageCode,'/profiles'].join('');
|
92
|
+
|
93
|
+
// Send asynchronous request to the Management API.
|
94
|
+
myService.getManagementFeed(webPropertyFeedUri, printWebPropertyFeed,handleError);
|
95
|
+
}
|
96
|
+
|
97
|
+
function printWebPropertyFeed(result) {
|
98
|
+
var feed = result.feed;
|
99
|
+
var entries = feed.getEntries();
|
100
|
+
var GAProfileId;
|
101
|
+
if (entries.length) {
|
102
|
+
for (var i = 0, entry; entry = entries[i]; ++i) {
|
103
|
+
GAProfileId = entry.getPropertyValue('ga:profileId');
|
104
|
+
}
|
105
|
+
}
|
106
|
+
profileId = GAProfileId;
|
107
|
+
getPageViewsDataFeed();
|
108
|
+
getVisitsDataFeed();
|
109
|
+
getVisitorsDataFeed();
|
110
|
+
getTimeOnSiteDataFeed();
|
111
|
+
getSourceDataFeed();
|
112
|
+
getKeywordDataFeed();
|
113
|
+
}
|
114
|
+
|
115
|
+
function getDateConditions() {
|
116
|
+
var toDay = new Date();
|
117
|
+
var lastMonth = new Date (toDay.getFullYear(), toDay.getMonth()-1, toDay.getDate());
|
118
|
+
|
119
|
+
var lastMonthStr;
|
120
|
+
if ((lastMonth.getMonth() + 1) < 10) {
|
121
|
+
lastMonthStr = '0'+(lastMonth.getMonth() + 1);
|
122
|
+
} else {
|
123
|
+
lastMonthStr = (lastMonth.getMonth() + 1);
|
124
|
+
}
|
125
|
+
|
126
|
+
var lastMonthDayStr;
|
127
|
+
if (lastMonth.getDate() < 10) {
|
128
|
+
lastMonthDayStr = '0'+(lastMonth.getDate());
|
129
|
+
} else {
|
130
|
+
lastMonthDayStr = (lastMonth.getDate());
|
131
|
+
}
|
132
|
+
|
133
|
+
var currentMonthStr;
|
134
|
+
if ((toDay.getMonth() + 1) < 10) {
|
135
|
+
currentMonthStr = '0'+(toDay.getMonth() + 1);
|
136
|
+
} else {
|
137
|
+
currentMonthStr = (toDay.getMonth() + 1);
|
138
|
+
}
|
139
|
+
|
140
|
+
var currentMonthDayStr;
|
141
|
+
if (toDay.getDate() < 10) {
|
142
|
+
currentMonthDayStr = '0'+(toDay.getDate());
|
143
|
+
} else {
|
144
|
+
currentMonthDayStr = (toDay.getDate());
|
145
|
+
}
|
146
|
+
|
147
|
+
var conditions = '?start-date='+ lastMonth.getFullYear() + '-' + lastMonthStr + '-' + lastMonthDayStr + '&end-date='+ toDay.getFullYear() + '-' + currentMonthStr + '-' + currentMonthDayStr
|
148
|
+
|
149
|
+
return conditions;
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Request data from GA Export API
|
154
|
+
*/
|
155
|
+
function getPageViewsDataFeed() {
|
156
|
+
var myFeedUri = scope + '/data' + getDateConditions() +
|
157
|
+
'&dimensions=ga:pageTitle,ga:pagePath' +
|
158
|
+
'&metrics=ga:pageviews' +
|
159
|
+
'&sort=-ga:pageviews' +
|
160
|
+
'&max-results=10' +
|
161
|
+
'&ids=ga:' + profileId;
|
162
|
+
|
163
|
+
myService.getDataFeed(myFeedUri, handlePageViewsDataFeed, handleError);
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Request data from GA Export API
|
168
|
+
*/
|
169
|
+
function getVisitsDataFeed() {
|
170
|
+
var myFeedUri = scope + '/data' + getDateConditions() +
|
171
|
+
'&dimensions=ga:day,ga:year,ga:month,ga:date' +
|
172
|
+
'&metrics=ga:visits' +
|
173
|
+
'&sort=ga:date' +
|
174
|
+
'&ids=ga:' + profileId;
|
175
|
+
|
176
|
+
myService.getDataFeed(myFeedUri, handleVisitsDataFeed, handleError);
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Request data from GA Export API
|
181
|
+
*/
|
182
|
+
function getVisitorsDataFeed() {
|
183
|
+
var myFeedUri = scope + '/data' + getDateConditions() +
|
184
|
+
'&dimensions=ga:day,ga:year,ga:month,ga:date' +
|
185
|
+
'&metrics=ga:visitors' +
|
186
|
+
'&sort=ga:date' +
|
187
|
+
'&ids=ga:' + profileId;
|
188
|
+
|
189
|
+
myService.getDataFeed(myFeedUri, handleVisitorsDataFeed, handleError);
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* Request data from GA Export API
|
194
|
+
*/
|
195
|
+
function getTimeOnSiteDataFeed() {
|
196
|
+
var myFeedUri = scope + '/data' + getDateConditions() +
|
197
|
+
'&dimensions=ga:day,ga:year,ga:month,ga:date' +
|
198
|
+
'&metrics=ga:timeOnSite,ga:visits' +
|
199
|
+
'&sort=ga:date' +
|
200
|
+
'&ids=ga:' + profileId;
|
201
|
+
|
202
|
+
myService.getDataFeed(myFeedUri, handleTimeOnSiteDataFeed, handleError);
|
203
|
+
}
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Request data from GA Export API
|
207
|
+
*/
|
208
|
+
function getSourceDataFeed() {
|
209
|
+
var myFeedUri = scope + '/data' + getDateConditions() +
|
210
|
+
'&dimensions=ga:source' +
|
211
|
+
'&metrics=ga:visits' +
|
212
|
+
'&sort=-ga:visits' +
|
213
|
+
'&max-results=20' +
|
214
|
+
'&ids=ga:' + profileId;
|
215
|
+
|
216
|
+
myService.getDataFeed(myFeedUri, handleSourceDataFeed, handleError);
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Request data from GA Export API
|
221
|
+
*/
|
222
|
+
function getKeywordDataFeed() {
|
223
|
+
var myFeedUri = scope + '/data' + getDateConditions() +
|
224
|
+
'&dimensions=ga:keyword' +
|
225
|
+
'&metrics=ga:visits' +
|
226
|
+
'&sort=-ga:visits' +
|
227
|
+
'&max-results=20' +
|
228
|
+
'&ids=ga:' + profileId;
|
229
|
+
|
230
|
+
myService.getDataFeed(myFeedUri, handleKeywordDataFeed, handleError);
|
231
|
+
}
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Handle and display any error that occurs from the API request.
|
235
|
+
* @param {Object} e The error object returned by the Analytics API.
|
236
|
+
*/
|
237
|
+
function handleError(e) {
|
238
|
+
var msg = e.cause ? e.cause.statusText : e.message;
|
239
|
+
msg = 'ERROR: ' + msg;
|
240
|
+
alert(msg);
|
241
|
+
}
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Handle all the data returned by GA Export API.
|
245
|
+
* Delete existing GViz dataTable before creating a new one.
|
246
|
+
* @param {Object} myResultsFeedRoot the feed object
|
247
|
+
* retuned by the data feed.
|
248
|
+
*/
|
249
|
+
function handlePageViewsDataFeed(resultsFeedRoot) {
|
250
|
+
dataTable = new google.visualization.DataTable();
|
251
|
+
fillPageViewsDataTable(dataTable, resultsFeedRoot);
|
252
|
+
pageViewsTable.draw(dataTable);
|
253
|
+
|
254
|
+
// remove the URI column to only graph 1 dimension
|
255
|
+
dataTable.removeColumn(0);
|
256
|
+
pageViewsChart.draw(dataTable, {width: 950, height: 400});
|
257
|
+
pageViewsColumnChart.draw(dataTable, {width: 950, height: 300, legend: 'none'});
|
258
|
+
}
|
259
|
+
|
260
|
+
/**
|
261
|
+
* Handle all the data returned by GA Export API.
|
262
|
+
* Delete existing GViz dataTable before creating a new one.
|
263
|
+
* @param {Object} myResultsFeedRoot the feed object
|
264
|
+
* retuned by the data feed.
|
265
|
+
*/
|
266
|
+
function handleVisitsDataFeed(resultsFeedRoot) {
|
267
|
+
dataTable = new google.visualization.DataTable();
|
268
|
+
fillVisitsDataTable(dataTable, resultsFeedRoot);
|
269
|
+
visitsLineChart.draw(dataTable, {width: 750, height: 500, legend: 'none'});
|
270
|
+
visitsTable.draw(dataTable);
|
271
|
+
}
|
272
|
+
|
273
|
+
function handleVisitorsDataFeed(resultsFeedRoot) {
|
274
|
+
dataTable = new google.visualization.DataTable();
|
275
|
+
fillVisitorsDataTable(dataTable, resultsFeedRoot);
|
276
|
+
visitorsLineChart.draw(dataTable, {width: 750, height: 500, legend: 'none'});
|
277
|
+
visitorsTable.draw(dataTable);
|
278
|
+
}
|
279
|
+
|
280
|
+
function handleTimeOnSiteDataFeed(resultsFeedRoot) {
|
281
|
+
dataTable = new google.visualization.DataTable();
|
282
|
+
fillTimesOnSiteDataTable(dataTable, resultsFeedRoot);
|
283
|
+
timesOnSiteLineChart.draw(dataTable, {width: 750, height: 500, legend: 'none'});
|
284
|
+
timesOnSiteTable.draw(dataTable);
|
285
|
+
}
|
286
|
+
|
287
|
+
function handleSourceDataFeed(resultsFeedRoot) {
|
288
|
+
dataTable = new google.visualization.DataTable();
|
289
|
+
fillSourceDataTable(dataTable, resultsFeedRoot);
|
290
|
+
sourcePieChart.draw(dataTable, {width: 600, height: 400, legend: 'none'});
|
291
|
+
sourceTable.draw(dataTable);
|
292
|
+
}
|
293
|
+
|
294
|
+
function handleKeywordDataFeed(resultsFeedRoot) {
|
295
|
+
dataTable = new google.visualization.DataTable();
|
296
|
+
fillKeywordDataTable(dataTable, resultsFeedRoot);
|
297
|
+
keywordPieChart.draw(dataTable, {width: 600, height: 400, legend: 'none'});
|
298
|
+
keywordTable.draw(dataTable);
|
299
|
+
}
|
300
|
+
|
301
|
+
/**
|
302
|
+
* Put the feed result into a GViz Data Table.
|
303
|
+
* @param {Object} dataTable the GViz dataTable
|
304
|
+
dataTable.setCell(0, 1, '0'); object to put data into.
|
305
|
+
* @param {Object} myResultsFeedRoot the feed returned by the GA Export API.
|
306
|
+
* @return {Objcet} GViz DataTable object.
|
307
|
+
*/
|
308
|
+
function fillPageViewsDataTable(dataTable, myResultsFeedRoot) {
|
309
|
+
var entries = myResultsFeedRoot.feed.getEntries();
|
310
|
+
|
311
|
+
var datas = getGADatasInTable(entries, ['pageTitle','pagePath','pageviews']);
|
312
|
+
fillDataTable(entries, dataTable, [['string', I18n.t('admin.stats.title'), datas[0]],['string', I18n.t('admin.stats.path'), datas[1]],['number', I18n.t('admin.stats.page_views'), datas[2]]]);
|
313
|
+
}
|
314
|
+
|
315
|
+
function fillKeywordDataTable(dataTable, myResultsFeedRoot) {
|
316
|
+
var entries = myResultsFeedRoot.feed.getEntries();
|
317
|
+
var keywords = new Array();
|
318
|
+
var visits = new Array();
|
319
|
+
|
320
|
+
for (var i = 0, entry; entry = entries[i]; ++i) {
|
321
|
+
|
322
|
+
if (entry.getValueOf('ga:keyword') != '(not set)') {
|
323
|
+
keywords.push(entry.getValueOf('ga:keyword'));
|
324
|
+
visits.push(entry.getValueOf('ga:visits'));
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
fillDataTable(entries, dataTable, [['string', I18n.t('admin.stats.keywords'), keywords],['number', I18n.t('admin.stats.visits'), visits]]);
|
329
|
+
dataTable.removeRow(dataTable.getNumberOfRows()-1);
|
330
|
+
}
|
331
|
+
|
332
|
+
function fillSourceDataTable(dataTable, myResultsFeedRoot) {
|
333
|
+
var entries = myResultsFeedRoot.feed.getEntries();
|
334
|
+
var dates = new Array();
|
335
|
+
|
336
|
+
var datas = getGADatasInTable(entries, ['source', 'visits']);
|
337
|
+
fillDataTable(entries, dataTable, [['string', I18n.t('admin.stats.source'), datas[0]],['number', I18n.t('admin.stats.visits'), datas[1]]]);
|
338
|
+
}
|
339
|
+
|
340
|
+
function fillVisitsDataTable(dataTable, myResultsFeedRoot) {
|
341
|
+
var entries = myResultsFeedRoot.feed.getEntries();
|
342
|
+
var dates = new Array();
|
343
|
+
|
344
|
+
var datas = getGADatasInTable(entries, ['visits'], dates);
|
345
|
+
fillDataTable(entries, dataTable, [['string', I18n.t('admin.stats.date'), dates],['number', I18n.t('admin.stats.visits'), datas[0]]]);
|
346
|
+
}
|
347
|
+
|
348
|
+
function fillVisitorsDataTable(dataTable, myResultsFeedRoot) {
|
349
|
+
var entries = myResultsFeedRoot.feed.getEntries();
|
350
|
+
var dates = new Array();
|
351
|
+
|
352
|
+
var datas = getGADatasInTable(entries, ['visitors'], dates);
|
353
|
+
fillDataTable(entries, dataTable, [['string',I18n.t('admin.stats.date'), dates],['number', I18n.t('admin.stats.visitors'), datas[0]]]);
|
354
|
+
}
|
355
|
+
|
356
|
+
function fillTimesOnSiteDataTable(dataTable, myResultsFeedRoot) {
|
357
|
+
var entries = myResultsFeedRoot.feed.getEntries();
|
358
|
+
var timesOnSite = new Array();
|
359
|
+
var dates = new Array();
|
360
|
+
|
361
|
+
for (var i = 0, entry; entry = entries[i]; ++i) {
|
362
|
+
fillDate(entry, timesOnSite, dates);
|
363
|
+
|
364
|
+
if (entry.getValueOf('ga:visits') != 0) {
|
365
|
+
timesOnSite.push(entry.getValueOf('ga:timeOnSite') / entry.getValueOf('ga:visits'));
|
366
|
+
} else {
|
367
|
+
timesOnSite.push(0);
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
for (var i = 0; i < timesOnSite.length ; ++i) {
|
372
|
+
timesOnSite[i] = Math.round(timesOnSite[i] / 60);
|
373
|
+
}
|
374
|
+
|
375
|
+
convertDates(dates);
|
376
|
+
|
377
|
+
fillDataTable(entries, dataTable, [['string',I18n.t('admin.stats.date'), dates],['number', I18n.t('admin.stats.average_time'), timesOnSite]]);
|
378
|
+
}
|
379
|
+
|
380
|
+
function fillDate(entry, dataTable, dateTable) {
|
381
|
+
var date = new Date(entry.getValueOf('ga:year'), entry.getValueOf('ga:month')-1, entry.getValueOf('ga:day'));
|
382
|
+
|
383
|
+
// At the beginning of each day, check if data was missing for previous
|
384
|
+
// day. Insert "0" in appropriate visitor's array as necessary, using
|
385
|
+
// fillToSameSize helper method.
|
386
|
+
var lastDate = dateTable[dateTable.length - 1];
|
387
|
+
if(lastDate) {
|
388
|
+
while (Date.parse(date) != Date.parse(lastDate)) {
|
389
|
+
lastDate = new Date (lastDate.getFullYear(), lastDate.getMonth(), lastDate.getDate() + 1);
|
390
|
+
if (Date.parse(date) != Date.parse(lastDate)) {
|
391
|
+
dataTable.push(0);
|
392
|
+
dateTable.push(lastDate);
|
393
|
+
}
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
dateTable.push(date);
|
398
|
+
}
|
399
|
+
|
400
|
+
function fillDataTable(entries, dataTable, datas) {
|
401
|
+
for (var i = 0; i < datas.length; i++) {
|
402
|
+
dataTable.addColumn(datas[i][0], datas[i][1]);
|
403
|
+
}
|
404
|
+
|
405
|
+
dataTable.addRows(entries.length);
|
406
|
+
|
407
|
+
for (var idx = 0; idx < entries.length; idx++) {
|
408
|
+
var entry = entries[idx];
|
409
|
+
for (var i = 0; i < datas.length; i++) {
|
410
|
+
dataTable.setCell(idx, i, datas[i][2][idx]);
|
411
|
+
}
|
412
|
+
}
|
413
|
+
}
|
414
|
+
|
415
|
+
function getGADatasInTable(entries, datas, dates) {
|
416
|
+
var results = new Array();
|
417
|
+
|
418
|
+
for (var idx = 0; idx < entries.length; idx++) {
|
419
|
+
var entry = entries[idx];
|
420
|
+
for (var i = 0; i <datas.length; i++) {
|
421
|
+
results.push(new Array());
|
422
|
+
var GAData = entry.getValueOf('ga:'+datas[i]);
|
423
|
+
if (dates) {
|
424
|
+
fillDate(entry, results[i], dates);
|
425
|
+
}
|
426
|
+
results[i].push(GAData);
|
427
|
+
}
|
428
|
+
}
|
429
|
+
|
430
|
+
convertDates(dates);
|
431
|
+
return results
|
432
|
+
}
|
433
|
+
|
434
|
+
function convertDates(dates) {
|
435
|
+
if (dates) {
|
436
|
+
for (var i = 0; i < dates.length; i++) {
|
437
|
+
dates[i] = dates[i].toLocaleString().substr(0,10);
|
438
|
+
}
|
439
|
+
}
|
440
|
+
}
|
441
|
+
|
442
|
+
// Load the Google Visualization API client Libraries
|
443
|
+
google.load('visualization', '1', {packages: ['piechart', 'table', 'columnchart', 'linechart']});
|
444
|
+
|
445
|
+
// Load the Google data JavaScript client library
|
446
|
+
google.load('gdata', '2.x', {packages: ['analytics']});
|
@@ -0,0 +1,14 @@
|
|
1
|
+
div.stat {
|
2
|
+
width: 958px;
|
3
|
+
opacity: 1;
|
4
|
+
border-bottom-left-radius: 5px;
|
5
|
+
border-bottom-right-radius: 5px;
|
6
|
+
border-top-right-radius: 5px;
|
7
|
+
background: none repeat scroll 0 0 #FFFFFF;
|
8
|
+
border: 1px solid #CCCCCC;
|
9
|
+
padding: 5px 5px 0;
|
10
|
+
}
|
11
|
+
|
12
|
+
.ui-tabs-nav {
|
13
|
+
padding: 0px;
|
14
|
+
}
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: refinerycms-stats
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: "0.1"
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Guirec CORBEL
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-06-26 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby on Rails Stats engine for Refinery CMS
|
17
|
+
email: guirec.corbel@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/generators/refinerycms_stats_generator.rb
|
26
|
+
- lib/refinerycms-stats.rb
|
27
|
+
- lib/tasks/stats.rake
|
28
|
+
- config/locales/en.yml
|
29
|
+
- config/locales/fr.yml
|
30
|
+
- config/routes.rb
|
31
|
+
- app/controllers/admin/stats_controller.rb
|
32
|
+
- app/models/stat.rb
|
33
|
+
- app/views/admin/stats/index.html.erb
|
34
|
+
- public/javascripts/refinerycms-stat-translate.js
|
35
|
+
- public/javascripts/refinerycms-stat.js
|
36
|
+
- public/stylesheets/stat.css
|
37
|
+
homepage: http://www.guirec-corbel.com
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.5
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Stats engine for Refinery CMS
|
64
|
+
test_files: []
|
65
|
+
|