active_analytics 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +105 -15
  3. data/app/controllers/active_analytics/application_controller.rb +24 -4
  4. data/app/controllers/active_analytics/assets_controller.rb +29 -0
  5. data/app/controllers/active_analytics/pages_controller.rb +9 -10
  6. data/app/controllers/active_analytics/referrers_controller.rb +9 -5
  7. data/app/controllers/active_analytics/sites_controller.rb +4 -4
  8. data/app/models/active_analytics/views_per_day.rb +22 -3
  9. data/app/views/active_analytics/assets/_charts.css +249 -0
  10. data/app/{assets/stylesheets/active_analytics/style.css → views/active_analytics/assets/_style.css} +37 -38
  11. data/app/views/active_analytics/assets/application.css.erb +2 -0
  12. data/app/{assets/javascripts/active_analytics → views/active_analytics/assets}/application.js +1 -3
  13. data/app/views/active_analytics/assets/ariato.css +875 -0
  14. data/app/views/active_analytics/assets/ariato.js +322 -0
  15. data/app/views/active_analytics/pages/_table.html.erb +6 -17
  16. data/app/views/active_analytics/pages/index.html.erb +1 -1
  17. data/app/views/active_analytics/pages/show.html.erb +7 -8
  18. data/app/views/active_analytics/referrers/_table.html.erb +9 -2
  19. data/app/views/active_analytics/referrers/index.html.erb +2 -2
  20. data/app/views/active_analytics/referrers/show.html.erb +6 -3
  21. data/app/views/active_analytics/sites/_histogram.html.erb +9 -2
  22. data/app/views/active_analytics/sites/_histogram_header.html.erb +10 -0
  23. data/app/views/active_analytics/sites/show.html.erb +2 -2
  24. data/app/views/layouts/active_analytics/_footer.html.erb +3 -3
  25. data/app/views/layouts/active_analytics/_header.html.erb +1 -3
  26. data/app/views/layouts/active_analytics/application.html.erb +5 -3
  27. data/config/routes.rb +2 -1
  28. data/lib/active_analytics/version.rb +1 -1
  29. data/lib/active_analytics.rb +47 -4
  30. metadata +15 -11
  31. data/app/assets/javascripts/active_analytics/ariato.js +0 -746
  32. data/app/assets/stylesheets/active_analytics/application.css +0 -15
  33. data/app/assets/stylesheets/active_analytics/ariato.css +0 -3548
  34. data/app/assets/stylesheets/active_analytics/charts.css +0 -424
@@ -0,0 +1,322 @@
1
+ Ariato = {}
2
+
3
+ Ariato.launchWhenDomIsReady = function(root) {
4
+ if (document.readyState != "loading") {
5
+ Ariato.launch()
6
+ Ariato.launch(document, "aria-roledescription")
7
+ Ariato.launch(document, "data-ariato")
8
+ }
9
+ else
10
+ document.addEventListener("DOMContentLoaded", function() { Ariato.launchWhenDomIsReady(root) } )
11
+ }
12
+
13
+ Ariato.launch = function(root, attribute, parent) {
14
+ attribute || (attribute = "role")
15
+ var elements = (root || document).querySelectorAll("[" + attribute + "]")
16
+ for (var i = 0; i < elements.length; i++)
17
+ Ariato.start(elements[i], attribute, parent)
18
+ }
19
+
20
+ Ariato.mount = function() {
21
+ }
22
+
23
+ Ariato.start = function(element, attribute, parent) {
24
+ var names = element.getAttribute(attribute).split(" ")
25
+ for (var i = 0; i < names.length; i++) {
26
+ var name = names[i].charAt(0).toUpperCase() + names[i].slice(1) // Capitalize
27
+ var func = Ariato.stringToFunction("Ariato." + name) || Ariato.stringToFunction(name)
28
+ if (func instanceof Function)
29
+ Ariato.instanciate(func, element, parent)
30
+ }
31
+ }
32
+
33
+ Ariato.instanciate = function(func, element, parent) {
34
+ try {
35
+ controller = Object.create(func.prototype)
36
+ controller.parent = parent
37
+ controller.node = element
38
+ Ariato.initialize(controller, element)
39
+ func.call(controller, element)
40
+ } catch (ex) {
41
+ console.error(ex)
42
+ }
43
+ }
44
+
45
+ Ariato.stringToFunction = function(fullName) {
46
+ var func = window, names = fullName.split(".")
47
+ for (var i = 0; i < names.length; i++)
48
+ if (!(func = func[names[i]]))
49
+ return null
50
+ return func
51
+ }
52
+
53
+ Ariato.initialize = function(controller, container) {
54
+ Ariato.listenEvents(container, controller)
55
+ Ariato.assignRoles(container, controller)
56
+ }
57
+
58
+ Ariato.listenEvents = function(root, controller) {
59
+ var elements = root.querySelectorAll("[data-event]")
60
+ for (var i = 0; i < elements.length; i++) {
61
+ elements[i].getAttribute("data-event").split(" ").forEach(function(eventAndAction) {
62
+ var array = eventAndAction.split("->")
63
+ Ariato.listenEvent(controller, elements[i], array[0], array[1])
64
+ })
65
+ }
66
+ }
67
+
68
+ Ariato.listenEvent = function(controller, element, event, action) {
69
+ if (controller[action] instanceof Function)
70
+ element.addEventListener(event, controller[action].bind(controller))
71
+ }
72
+
73
+ Ariato.findRoles = function(container) {
74
+ var roles = {}, elements = container.querySelectorAll("[data-role]")
75
+ for (var i = 0; i < elements.length; i++) {
76
+ var name = elements[i].getAttribute("data-role")
77
+ roles[name] ? roles[name].push(elements[i]) : roles[name] = [elements[i]]
78
+ }
79
+ return roles
80
+ }
81
+
82
+ Ariato.assignRoles = function(container, controller) {
83
+ controller.roles = Ariato.findRoles(container)
84
+ for (var name in controller.roles)
85
+ if (controller.roles[name].length == 1)
86
+ controller[name] = controller.roles[name][0]
87
+ }
88
+
89
+ Ariato.Dialog = function(node) {
90
+ node.setAttribute("hidden", true)
91
+ node.addEventListener("open", this.open.bind(this))
92
+ node.addEventListener("close", this.close.bind(this))
93
+ node.addEventListener("keydown", this.keydown.bind(this))
94
+ }
95
+
96
+ Ariato.Dialog.open = function(elementOrId) {
97
+ var dialog = elementOrId instanceof Element ? elementOrId : document.getElementById(elementOrId)
98
+ dialog && dialog.dispatchEvent(new CustomEvent("open"))
99
+ }
100
+
101
+ Ariato.Dialog.close = function(button) {
102
+ var dialog = Ariato.Dialog.current()
103
+ if (dialog && dialog.node.contains(button))
104
+ dialog.close()
105
+ }
106
+
107
+ Ariato.Dialog.closeCurrent = function() {
108
+ var dialog = Ariato.Dialog.current()
109
+ dialog && dialog.close()
110
+ }
111
+
112
+ Ariato.Dialog.replace = function(elementOrId) {
113
+ Ariato.Dialog.closeCurrent()
114
+ Ariato.Dialog.open(elementOrId)
115
+ }
116
+
117
+ Ariato.Dialog.close = function(button) {
118
+ var dialog = Ariato.Dialog.current()
119
+ if (dialog && dialog.node.contains(button))
120
+ dialog.close()
121
+ }
122
+
123
+ Ariato.Dialog.list = []
124
+
125
+ Ariato.Dialog.current = function() {
126
+ return this.list[this.list.length - 1]
127
+ }
128
+
129
+ Ariato.Dialog.prototype.open = function(event) {
130
+ Ariato.Dialog.list.push(this)
131
+ document.addEventListener("focus", this.bindedLimitFocusScope = this.limitFocusScope.bind(this), true)
132
+ this.initiator = document.activeElement
133
+ this.node.removeAttribute("hidden")
134
+
135
+ this.lockScrolling()
136
+ this.createBackdrop()
137
+ this.createFocusStoppers()
138
+ this.focusFirstDescendant(this.node)
139
+ }
140
+
141
+ Ariato.Dialog.prototype.close = function(event) {
142
+ document.removeEventListener("focus", this.bindedLimitFocusScope, true)
143
+ this.node.setAttribute("hidden", true)
144
+ this.removeFocusStoppers()
145
+ this.removeBackdrop()
146
+ this.unlockScrolling()
147
+ this.initiator.focus()
148
+ Ariato.Dialog.list.pop()
149
+ }
150
+
151
+ Ariato.Dialog.prototype.keydown = function(event) {
152
+ if (event.key == "Escape")
153
+ this.close()
154
+ }
155
+
156
+ Ariato.Dialog.prototype.focusFirstDescendant = function(parent) {
157
+ var focusable = ["A", "BUTTON", "INPUT", "SELECT", "TEXTAREA"]
158
+
159
+ for (var i = 0; i < parent.children.length; i++) {
160
+ var child = parent.children[i]
161
+ if (focusable.indexOf(child.nodeName) != -1 && !child.disabled && child.type != "hidden") {
162
+ child.focus()
163
+ return child
164
+ }
165
+ else {
166
+ var focus = this.focusFirstDescendant(child)
167
+ if (focus) return focus
168
+ }
169
+ }
170
+ }
171
+
172
+ Ariato.Dialog.prototype.limitFocusScope = function(event) {
173
+ if (this == Ariato.Dialog.current())
174
+ if (!this.node.contains(event.target))
175
+ this.focusFirstDescendant(this.node)
176
+ }
177
+
178
+ Ariato.Dialog.prototype.lockScrolling = function() {
179
+ document.body.style.position = "fixed";
180
+ document.body.style.top = "-" + window.scrollY + "px";
181
+ }
182
+
183
+ Ariato.Dialog.prototype.unlockScrolling = function() {
184
+ var scrollY = document.body.style.top
185
+ document.body.style.position = ""
186
+ document.body.style.top = ""
187
+ window.scrollTo(0, parseInt(scrollY || "0") * -1)
188
+ }
189
+
190
+ Ariato.Dialog.prototype.createFocusStoppers = function() {
191
+ this.node.parentNode.insertBefore(this.focusStopper1 = document.createElement("div"), this.node)
192
+ this.focusStopper1.tabIndex = 0
193
+
194
+ this.node.parentNode.insertBefore(this.focusStopper2 = document.createElement("div"), this.node.nextSibling)
195
+ this.focusStopper2.tabIndex = 0
196
+ }
197
+
198
+ Ariato.Dialog.prototype.removeFocusStoppers = function() {
199
+ this.focusStopper1 && this.focusStopper1.parentNode.removeChild(this.focusStopper1)
200
+ this.focusStopper2 && this.focusStopper2.parentNode.removeChild(this.focusStopper2)
201
+ }
202
+
203
+ Ariato.Dialog.prototype.createBackdrop = function() {
204
+ this.backdrop = document.createElement("div")
205
+ this.backdrop.classList.add("dialog-backdrop")
206
+ this.node.parentNode.insertBefore(this.backdrop, this.node)
207
+ this.backdrop.appendChild(this.node)
208
+ }
209
+
210
+ Ariato.Dialog.prototype.removeBackdrop = function() {
211
+ this.backdrop.parentNode.insertBefore(this.node, this.backdrop)
212
+ this.backdrop.parentNode.removeChild(this.backdrop)
213
+ this.backdrop = null
214
+ }
215
+
216
+ Ariato.Alertdialog = Ariato.Dialog
217
+
218
+ Ariato.MenuButton = function(node) {
219
+ this.node = this.button = node
220
+ this.menu = document.getElementById(this.button.getAttribute("aria-controls"))
221
+
222
+ this.menu.addEventListener("keydown", this.keydown.bind(this))
223
+ this.button.addEventListener("keydown", this.keydown.bind(this))
224
+
225
+ this.button.addEventListener("click", this.clicked.bind(this))
226
+ window.addEventListener("click", this.windowClicked.bind(this), true)
227
+ }
228
+
229
+ Ariato.MenuButton.prototype.clicked = function(event) {
230
+ this.node.getAttribute("aria-expanded") == "true" ? this.close() : this.open()
231
+ }
232
+
233
+ Ariato.MenuButton.prototype.windowClicked = function() {
234
+ if (!this.node.contains(event.target) && this.node.getAttribute("aria-expanded") == "true")
235
+ this.close()
236
+ }
237
+
238
+ Ariato.MenuButton.prototype.open = function() {
239
+ this.button.setAttribute("aria-expanded", "true")
240
+ this.menu.style.display = "block"
241
+ }
242
+
243
+ Ariato.MenuButton.prototype.close = function() {
244
+ this.button.setAttribute("aria-expanded", "false")
245
+ this.menu.style.display = null
246
+ }
247
+
248
+ Ariato.MenuButton.prototype.keydown = function(event) {
249
+ switch(event.key) {
250
+ case "Escape":
251
+ this.close()
252
+ break
253
+ case "ArrowDown":
254
+ event.preventDefault()
255
+ this.focusNextItem()
256
+ break
257
+ case "ArrowUp":
258
+ event.preventDefault()
259
+ this.focusPreviousItem()
260
+ break
261
+ case "Tab":
262
+ this.close()
263
+ case "Home":
264
+ case "PageUp":
265
+ event.preventDefault()
266
+ this.items()[0].focus()
267
+ break
268
+ case "End":
269
+ case "PageDown":
270
+ event.preventDefault()
271
+ var items = this.items()
272
+ items[items.length-1].focus()
273
+ break
274
+ }
275
+ }
276
+
277
+ Ariato.MenuButton.prototype.items = function() {
278
+ return this.menu.querySelectorAll("[role=menuitem]")
279
+ }
280
+
281
+ Ariato.MenuButton.prototype.currentItem = function() {
282
+ return this.menu.querySelector("[role=menuitem]:focus")
283
+ }
284
+
285
+ Ariato.MenuButton.prototype.nextItem = function() {
286
+ var items = this.items()
287
+ var current = this.currentItem()
288
+ if (!current) return items[0]
289
+ for (var i = 0; i < items.length; i++) {
290
+ if (items[i] == current)
291
+ return items[i+1]
292
+ }
293
+ }
294
+
295
+ Ariato.MenuButton.prototype.previousItem = function() {
296
+ var items = this.items()
297
+ var current = this.currentItem()
298
+ if (!current) return items[0]
299
+ for (var i = 0; i < items.length; i++) {
300
+ if (items[i] == current)
301
+ return items[i-1]
302
+ }
303
+ }
304
+
305
+ Ariato.MenuButton.prototype.focusNextItem = function() {
306
+ var item = this.nextItem()
307
+ item && item.focus()
308
+ }
309
+
310
+ Ariato.MenuButton.prototype.focusPreviousItem = function() {
311
+ var item = this.previousItem()
312
+ item && item.focus()
313
+ }
314
+
315
+ Ariato.Menu = function(node) {
316
+ var button = this.labelledBy()
317
+ button && new Ariato.MenuButton(button)
318
+ }
319
+
320
+ Ariato.Menu.prototype.labelledBy = function() {
321
+ return document.getElementById(this.node.getAttribute("aria-labelledby"))
322
+ }
@@ -1,4 +1,3 @@
1
-
2
1
  <% if pages.empty? %>
3
2
  <div class="is-empty">
4
3
  <span>no data</span>
@@ -8,26 +7,16 @@
8
7
  <% for page in pages %>
9
8
  <tr>
10
9
  <td>
11
- <% if page.host == params[:site] %>
12
- <% if page.path.present? %>
13
- <%= link_to page.path, page_path(site: page.host, page: page_to_params(page.path), from: params[:from], to: params[:to]) %>
14
- <% else %>
15
- <%= site_icon page.host %>
16
- <%= link_to page.host, site_path(site: page.host, from: params[:from], to: params[:to]) %>
17
- <small>(page not provided <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin">?</a>)</small>
18
- <% end %>
19
- <% elsif page.host.present? && page.path.present? %>
20
- <%= link_to(referrer_path(site: params[:site], referrer: page.host, from: params[:from], to: params[:to])) { site_icon page.host } %>
21
- <%= link_to page.url, referrer_path(site: params[:site], referrer: page.host, from: params[:from], to: params[:to]) %>
22
- <% elsif page.host.present? %>
23
- <%= site_icon page.host %>
24
- <%= page.host %>
10
+ <% if page.path.present? %>
11
+ <%= link_to page.path, page_path(site: page.host, page: page_to_params(page.path), from: params[:from], to: params[:to]) %>
25
12
  <% else %>
26
- (None or direct)
13
+ <%= site_icon page.host %>
14
+ <%= link_to page.host, site_path(site: page.host, from: params[:from], to: params[:to]) %>
15
+ <small>(page not provided <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin">?</a>)</small>
27
16
  <% end %>
28
17
  </td>
29
18
  <td class="number"><%= format_view_count page.total %></td>
30
19
  </tr>
31
20
  <% end %>
32
21
  </table>
33
- <% end %>
22
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <section class="card">
2
- <h3><%= format_view_count @histogram.total %> views</h3>
2
+ <%= render "/active_analytics/sites/histogram_header", histogram: @histogram, previous_histogram: @previous_histogram %>
3
3
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
4
4
  </section>
5
5
 
@@ -1,21 +1,20 @@
1
1
  <section>
2
- <h2><%= page_from_params %></h2>
2
+ <h2>
3
+ <%= page_from_params %>
4
+ <%= link_to "", File.join("https://", params[:site], page_from_params), target: "_blank" %>
5
+ </h2>
3
6
  </section>
4
7
 
5
8
  <section class="card">
6
- <h3><%= format_view_count @histogram.total %> views</h3>
9
+ <%= render "/active_analytics/sites/histogram_header", histogram: @histogram, previous_histogram: @previous_histogram %>
7
10
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
8
11
  </section>
9
12
 
10
13
  <ul class="grid-auto">
11
14
  <li class="card">
12
- <!-- <section>
13
- <h3>Top sources</h3>
14
- <%= render "/active_analytics/referrers/table", referrers: @referrers %>
15
- </section> -->
16
15
  <section>
17
16
  <h3>Previous page</h3>
18
- <%= render "table", pages: @previous_pages %>
17
+ <%= render "/active_analytics/referrers/table", referrers: @previous_pages %>
19
18
  </section>
20
19
  </li>
21
20
 
@@ -23,4 +22,4 @@
23
22
  <h3>Next page</h3>
24
23
  <%= render "table", pages: @next_pages %>
25
24
  </li>
26
- </ul>
25
+ </ul>
@@ -7,11 +7,18 @@
7
7
  <% for referrer in referrers %>
8
8
  <tr>
9
9
  <td>
10
- <% if referrer.host %>
10
+ <% if referrer.try(:path) %>
11
+ <%= site_icon referrer.host %>
12
+ <% if referrer.host == params[:site] %>
13
+ <%= link_to referrer.path, page_path(site: referrer.host, page: page_to_params(referrer.path), from: params[:from], to: params[:to]) %>
14
+ <% else %>
15
+ <%= link_to referrer.url, referrer_path(site: params[:site], referrer: referrer.url.chomp("/"), from: params[:from], to: params[:to]) %>
16
+ <% end %>
17
+ <% elsif referrer.host %>
11
18
  <%= site_icon referrer.host %>
12
19
  <%= link_to referrer.host, referrer_path(site: params[:site], referrer: referrer.host, from: params[:from], to: params[:to]) %>
13
20
  <% else %>
14
- <%= referrer.host %>
21
+ (None or direct)
15
22
  <% end %>
16
23
  </td>
17
24
  <td class="number"><%= format_view_count referrer.total %></td>
@@ -1,9 +1,9 @@
1
1
  <section class="card">
2
- <h3><%= format_view_count @histogram.total %> views</h3>
2
+ <%= render "/active_analytics/sites/histogram_header", histogram: @histogram, previous_histogram: @previous_histogram %>
3
3
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
4
4
  </section>
5
5
 
6
6
  <section class="card">
7
7
  <h3>Top sources</h3>
8
8
  <%= render "table", referrers: @referrers %>
9
- </section>
9
+ </section>
@@ -1,15 +1,18 @@
1
1
  <section>
2
- <h2>Source: <%= params[:referrer] %></h2>
2
+ <h2>
3
+ Source: <%= params[:referrer] %>
4
+ <%= link_to "", File.join("https://", params[:referrer]), target: "_blank" %>
5
+ </h2>
3
6
  </section>
4
7
 
5
8
  <section class="card">
6
- <h3><%= format_view_count @histogram.total %> views</h3>
9
+ <%= render "/active_analytics/sites/histogram_header", histogram: @histogram, previous_histogram: @previous_histogram %>
7
10
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
8
11
  </section>
9
12
  <ul class="grid-auto">
10
13
  <li class="card">
11
14
  <h3>Source page</h3>
12
- <%= render "/active_analytics/pages/table", pages: @previous_pages %>
15
+ <%= render "/active_analytics/referrers/table", referrers: @previous_pages %>
13
16
  </li>
14
17
 
15
18
  <li class="card">
@@ -3,11 +3,18 @@
3
3
  <tbody>
4
4
  <% for bar in histogram.bars %>
5
5
  <tr>
6
- <th scope="row"><%= bar.label.day %></td>
7
- <td style="--size: <%= bar.height %>; --color: rgba(var(--color-grey-100), 1);">
6
+ <th scope="row">
7
+ <% if bar.height > 0 %>
8
+ <%= link_to bar.label.day, url_for(params.merge(from: bar.label.to_date, to: bar.label.to_date).to_unsafe_hash) %>
9
+ <% else %>
10
+ <%= bar.label.day %>
11
+ <% end %>
12
+ </th>
13
+ <td style="--size: <%= bar.height %>; --color: rgba(var(--color-grey-100), 1);">
8
14
  <span class="data"><%= bar.value.to_i %></span>
9
15
  <span class="tooltip"><small class="tooltip-date"><%= l bar.label, format: :long%></small><br/><%= format_view_count bar.value %> views</span>
10
16
  </td>
17
+ </a>
11
18
  </tr>
12
19
  <% end %>
13
20
  </tbody>
@@ -0,0 +1,10 @@
1
+ <h3>
2
+ <%= format_view_count current_total = histogram.total %> views
3
+ <% if (previous_total = previous_histogram.total) > 0 %>
4
+ <% if (diff = current_total - previous_total) > 0 %>
5
+ &nbsp;<small class="is-success">+<%= number_to_percentage diff * 100.0 / previous_total, precision: 0 %></small>
6
+ <% elsif (diff = current_total - previous_total) < 0 %>
7
+ &nbsp;<small class="is-danger"><%= number_to_percentage diff * 100.0 / previous_total, precision: 0 %></small>
8
+ <% end %>
9
+ <% end %>
10
+ </h3>
@@ -1,5 +1,5 @@
1
1
  <section class="card">
2
- <h3><%= format_view_count @histogram.total %> views</h3>
2
+ <%= render "/active_analytics/sites/histogram_header", histogram: @histogram, previous_histogram: @previous_histogram %>
3
3
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
4
4
  </section>
5
5
 
@@ -13,4 +13,4 @@
13
13
  <h3>Top pages <%= link_to "view all", pages_path(from: params[:from], to: params[:to]) %></h3>
14
14
  <%= render "/active_analytics/pages/table", pages: @pages %>
15
15
  </li>
16
- </ul>
16
+ </ul>
@@ -37,12 +37,12 @@
37
37
  <header id="custom-range-title">Custom range</header>
38
38
  <%= form_tag url_for(params.except(:from, :to).to_unsafe_hash), method: "get", id: "dateRangeForm" do %>
39
39
  <div role="group" class="is-block">
40
- <label>From <%= text_field_tag :from, params[:from] %></label>
41
- <label>To <%= text_field_tag :to, params[:to] %></label>
40
+ <label>From <%= date_field_tag :from, params[:from] %></label>
41
+ <label>To <%= date_field_tag :to, params[:to] %></label>
42
42
  </div>
43
43
  <% end %>
44
44
  <footer>
45
45
  <%= submit_tag "Change date range", form: "dateRangeForm" %>
46
46
  <button type="reset" onclick="Ariato.Dialog.close(this)">Close</button>
47
47
  </footer>
48
- </div>
48
+ </div>
@@ -1,7 +1,5 @@
1
1
  <header data-ariato="ActiveAnalytics.Header">
2
- <nav aria-label="site"> <!-- implicit role="navigation", aria label "site" is announced = "site navigation" -->
3
-
4
- <%#= link_to "Analytics", active_analytics_path, role: "button" %>
2
+ <nav aria-label="site">
5
3
  <%= link_to active_analytics_path, class: 'logo' do %>
6
4
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11">
7
5
  <path class="a" d="M.5,5.5s0,2,1,2,1-2,1-2,0-2,1-2,1,2,1,2,0,2,1,2,1-2,1-2,0-2,1-2,1,2,1,2,0,2,1,2,1-2,1-2"/>
@@ -1,13 +1,15 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html class="active-analytics">
3
3
  <head>
4
4
  <title>Active analytics</title>
5
5
  <meta name=viewport content="width=device-width, initial-scale=1">
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
8
8
 
9
- <%= stylesheet_link_tag "active_analytics/application", media: "all" %>
10
- <%= javascript_include_tag "active_analytics/application" %>
9
+ <%= tag.link rel: "stylesheet", href: asset_path(:ariato, format: :css) %>
10
+ <%= tag.link rel: "stylesheet", href: asset_path(:application, format: :css) %>
11
+ <%= tag.script "", src: asset_path(:ariato, format: :js) %>
12
+ <%= tag.script "", src: asset_path(:application, format: :js) %>
11
13
  </head>
12
14
 
13
15
  <body>
data/config/routes.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  ActiveAnalytics::Engine.routes.draw do
2
+ resources :assets, only: [:show]
2
3
  get "/:site", to: "sites#show", as: :site, constraints: {site: /[^\/]+/}
3
4
 
4
5
  # Referrers
5
6
  get "/:site/referrers", to: "referrers#index", constraints: {site: /[^\/]+/}, as: :referrers
6
- get "/:site/referrers/:referrer", to: "referrers#show", constraints: {site: /[^\/]+/, referrer: /[^\/]+/}, as: :referrer
7
+ get "/:site/referrers/*referrer", to: "referrers#show", as: :referrer, constraints: {site: /[^\/]+/, referrer: /.+/}
7
8
 
8
9
  # Pages
9
10
  get "/:site/pages", to: "pages#index", constraints: {site: /[^\/]+/}, as: :pages
@@ -1,3 +1,3 @@
1
1
  module ActiveAnalytics
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -2,6 +2,22 @@ require "active_analytics/version"
2
2
  require "active_analytics/engine"
3
3
 
4
4
  module ActiveAnalytics
5
+ def self.redis_url=(string)
6
+ @redis_url = string
7
+ end
8
+
9
+ def self.redis_url
10
+ @redis_url ||= ENV["ACTIVE_ANALYTICS_REDIS_URL"] || ENV["REDIS_URL"] || "redis://localhost"
11
+ end
12
+
13
+ def self.redis=(connection)
14
+ @redis = connection
15
+ end
16
+
17
+ def self.redis
18
+ @redis ||= Redis.new(url: redis_url)
19
+ end
20
+
5
21
  def self.record_request(request)
6
22
  params = {
7
23
  site: request.host,
@@ -9,12 +25,39 @@ module ActiveAnalytics
9
25
  date: Date.today,
10
26
  }
11
27
  if request.referrer.present?
12
- referrer_uri = URI(request.referrer)
13
- params[:referrer_host] = referrer_uri.host
14
- params[:referrer_path] = referrer_uri.path
28
+ params[:referrer_host], params[:referrer_path] = ViewsPerDay.split_referrer(request.referrer)
15
29
  end
16
30
  ViewsPerDay.append(params)
17
31
  rescue => ex
18
- raise if Rails.env.development? || Rails.env.test?
32
+ if Rails.env.development? || Rails.env.test?
33
+ raise ex
34
+ else
35
+ Rails.logger.error(ex.inspect)
36
+ Rails.logger.error(ex.backtrace.join("\n"))
37
+ end
38
+ end
39
+
40
+ SEPARATOR = "|"
41
+ QUEUE = "ActiveAnalytics::Queue"
42
+ OLD_QUEUE = "ActiveAnalytics::OldQueue"
43
+
44
+ def self.queue_request(request)
45
+ keys = [request.host, request.path]
46
+ if request.referrer.present?
47
+ keys.concat(ViewsPerDay.split_referrer(request.referrer))
48
+ end
49
+ redis.hincrby(QUEUE, keys.join(SEPARATOR).downcase, 1)
50
+ end
51
+
52
+ def self.flush_queue
53
+ return if !redis.exists?(QUEUE)
54
+ cursor = 0
55
+ date = Date.today
56
+ redis.rename(QUEUE, OLD_QUEUE)
57
+ redis.hscan_each(OLD_QUEUE) do |key, count|
58
+ site, page, referrer_host, referrer_path = key.split(SEPARATOR)
59
+ ViewsPerDay.append(date: date, site: site, page: page, referrer_host: referrer_host, referrer_path: referrer_path, total: count.to_i)
60
+ end
61
+ redis.del(OLD_QUEUE)
19
62
  end
20
63
  end