active_analytics 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -6
  3. data/app/assets/images/active_analytics.png +0 -0
  4. data/app/assets/images/active_analytics_screenshot.png +0 -0
  5. data/app/assets/javascripts/active_analytics/application.js +13 -0
  6. data/app/assets/stylesheets/active_analytics/application.css +1 -19
  7. data/app/assets/stylesheets/active_analytics/charts.css +80 -2262
  8. data/app/assets/stylesheets/active_analytics/style.css +161 -0
  9. data/app/controllers/active_analytics/application_controller.rb +12 -0
  10. data/app/controllers/active_analytics/pages_controller.rb +10 -7
  11. data/app/controllers/active_analytics/referrers_controller.rb +6 -4
  12. data/app/controllers/active_analytics/sites_controller.rb +5 -2
  13. data/app/helpers/active_analytics/application_helper.rb +3 -0
  14. data/app/helpers/active_analytics/sites_helper.rb +3 -0
  15. data/app/models/active_analytics/views_per_day.rb +26 -7
  16. data/app/views/active_analytics/pages/_table.html.erb +31 -19
  17. data/app/views/active_analytics/pages/index.html.erb +4 -7
  18. data/app/views/active_analytics/pages/show.html.erb +18 -15
  19. data/app/views/active_analytics/referrers/_table.html.erb +21 -14
  20. data/app/views/active_analytics/referrers/index.html.erb +4 -3
  21. data/app/views/active_analytics/referrers/show.html.erb +14 -9
  22. data/app/views/active_analytics/sites/_histogram.html.erb +15 -19
  23. data/app/views/active_analytics/sites/index.html.erb +12 -7
  24. data/app/views/active_analytics/sites/show.html.erb +12 -12
  25. data/app/views/layouts/active_analytics/_footer.html.erb +45 -3
  26. data/app/views/layouts/active_analytics/_header.html.erb +47 -21
  27. data/app/views/layouts/active_analytics/application.html.erb +15 -14
  28. data/db/migrate/20210303094108_create_active_analytics_views_per_days.rb +1 -1
  29. data/lib/active_analytics.rb +2 -4
  30. data/lib/active_analytics/version.rb +1 -1
  31. metadata +7 -4
@@ -0,0 +1,161 @@
1
+ body {
2
+ padding: 0;
3
+ margin: 0;
4
+ width: 100%;
5
+ }
6
+
7
+ body > header nav {
8
+ width: 100%;
9
+ max-width: 1280px;
10
+ margin: 0 auto;
11
+ padding: 24px;
12
+ display: flex;
13
+ flex-wrap: wrap;
14
+ align-items: center;
15
+ min-height: 88px;
16
+ }
17
+
18
+ header nav .menubutton {
19
+ margin-left: auto;
20
+ }
21
+
22
+ header .logo {
23
+ margin-right: 8px;
24
+ }
25
+
26
+ header .logo svg {
27
+ width: 24px;
28
+ height: 24px;
29
+ fill: none;
30
+ stroke: rgba(var(--color-blue-500), 1);
31
+ stroke-width: 1;
32
+ }
33
+
34
+ header .is-link {
35
+ text-transform: none;
36
+ text-decoration: none;
37
+ font-size: 1rem;
38
+ letter-spacing: 0;
39
+ font-weight: 400;
40
+ padding: 0 16px;
41
+ margin: 0;
42
+ min-height: 24px;
43
+ color: rgba(var(--color-grey-700), 1);
44
+ }
45
+
46
+ header .is-link:hover {
47
+ background: rgba(var(--color-grey-50), 1);
48
+ }
49
+
50
+ main {
51
+ width: 100%;
52
+ max-width: 1280px;
53
+ margin: 0 auto;
54
+ padding: 0 24px;
55
+ }
56
+
57
+ main h2 {
58
+ word-break: break-word;
59
+ }
60
+
61
+ section {
62
+ margin-bottom: var(--space-4x);
63
+ }
64
+
65
+ .grid-auto {
66
+ --col-min-width: calc(var(--space) * 38);
67
+ }
68
+
69
+ .tooltip-date {
70
+ color: rgba(var(--color-grey-100), 1);
71
+ }
72
+
73
+ .card {
74
+ position: relative;
75
+ overflow: visible;
76
+ }
77
+
78
+ .card h3 {
79
+ display: flex;
80
+ }
81
+
82
+ .card h3 a {
83
+ margin-left: auto;
84
+ font-weight: 400;
85
+ font-size: 1rem;
86
+ }
87
+
88
+ .card table {
89
+ margin: 0 -24px;
90
+ width: calc(100% + 48px);
91
+ }
92
+
93
+ .card table.charts-css {
94
+ margin-bottom: -24px;
95
+ }
96
+
97
+ tr td:first-of-type {
98
+ padding: 12px 0px 12px 24px;
99
+ }
100
+
101
+ tr td:last-of-type {
102
+ padding: 12px 24px 12px 0;
103
+ }
104
+
105
+ td a {
106
+ word-break: break-word;
107
+ }
108
+
109
+ td.number {
110
+ text-align: right;
111
+ font-variant-numeric: tabular-nums;
112
+ }
113
+
114
+ .referer-favicon {
115
+ width: 16px;
116
+ height: 16px;
117
+ display: inline-block;
118
+ position: relative;
119
+ top: 3px;
120
+ margin-right: 4px;
121
+ }
122
+
123
+ div.is-empty {
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ height: calc(100% - 72px);
128
+ width: 100%;
129
+ color: rgba(var(--color-grey-300), 1);
130
+ }
131
+
132
+ body > footer {
133
+ max-width: 1280px;
134
+ margin: 48px auto 0;
135
+ padding: 24px;
136
+ }
137
+
138
+ body > footer ul {
139
+ margin: 0;
140
+ list-style-type: none;
141
+ padding: 0;
142
+ }
143
+
144
+ body > footer .card {
145
+ margin: 0;
146
+ background: rgba(var(--color-grey-50), 1);
147
+ }
148
+ body > footer .card p {
149
+ padding: 0 0 2px;
150
+ color: rgba(var(--color-grey-400), 1);
151
+ }
152
+
153
+ @media (max-width: 480px) {
154
+ header nav .menubutton {
155
+ margin-top: 24px;
156
+ }
157
+
158
+ [role="dialog"] {
159
+ min-width: 280px;
160
+ }
161
+ }
@@ -1,4 +1,16 @@
1
1
  module ActiveAnalytics
2
2
  class ApplicationController < ActionController::Base
3
+
4
+ private
5
+
6
+ def require_date_range
7
+ if params[:from].blank? || params[:to].blank?
8
+ redirect_to(params.to_unsafe_hash.merge(from: 7.days.ago.to_date, to: Date.today))
9
+ end
10
+ end
11
+
12
+ def current_views_per_days
13
+ ViewsPerDay.where(site: params[:site]).between_dates(params[:from], params[:to])
14
+ end
3
15
  end
4
16
  end
@@ -4,19 +4,22 @@ module ActiveAnalytics
4
4
  class PagesController < ApplicationController
5
5
  include PagesHelper
6
6
 
7
+ before_action :require_date_range
8
+
7
9
  def index
8
- scope = ViewsPerDay.where(site: params[:site]).after(30.days.ago)
9
- @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date)
10
+ scope = ViewsPerDay.where(site: params[:site]).between_dates(params[:from], params[:to])
11
+ @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date, params[:from], params[:to])
10
12
  @pages = scope.top(100).group_by_page
11
13
  end
12
14
 
13
15
  def show
14
- scope = ViewsPerDay.where(site: params[:site], page: page_from_params).after(30.days.ago)
15
- @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date)
16
- @referrers = scope.top.group_by_referrer_site
16
+ dates_scopes = ViewsPerDay.between_dates(params[:from], params[:to])
17
+ page_scope = dates_scopes.where(site: params[:site], page: page_from_params)
18
+ @histogram = ViewsPerDay::Histogram.new(page_scope.order_by_date.group_by_date, params[:from], params[:to])
19
+ @referrers = page_scope.top.group_by_referrer_site
17
20
 
18
- @next_pages = ViewsPerDay.where(referrer_host: params[:site], referrer_path: page_from_params).top.group_by_page
19
- @previous_pages = ViewsPerDay.where(site: params[:site], page: page_from_params).top.group_by_referrer_page
21
+ @next_pages = dates_scopes.where(referrer_host: params[:site], referrer_path: page_from_params).top(100).group_by_page
22
+ @previous_pages = dates_scopes.where(site: params[:site], page: page_from_params).where.not(referrer_path: nil).top(100).group_by_referrer_page
20
23
  end
21
24
  end
22
25
  end
@@ -2,15 +2,17 @@ require_dependency "active_analytics/application_controller"
2
2
 
3
3
  module ActiveAnalytics
4
4
  class ReferrersController < ApplicationController
5
+ before_action :require_date_range
6
+
5
7
  def index
6
- scope = ViewsPerDay.where(site: params[:site])
8
+ scope = ViewsPerDay.where(site: params[:site]).between_dates(params[:from], params[:to])
7
9
  @referrers = scope.top(100).group_by_referrer_site
8
- @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date)
10
+ @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date, params[:from], params[:to])
9
11
  end
10
12
 
11
13
  def show
12
- scope = ViewsPerDay.where(site: params[:site], referrer_host: params[:referrer])
13
- @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date)
14
+ scope = ViewsPerDay.where(site: params[:site], referrer_host: params[:referrer]).between_dates(params[:from], params[:to])
15
+ @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date, params[:from], params[:to])
14
16
  @previous_pages = scope.top(100).group_by_referrer_page
15
17
  @next_pages = scope.top(100).group_by_page
16
18
  end
@@ -2,13 +2,16 @@ require_dependency "active_analytics/application_controller"
2
2
 
3
3
  module ActiveAnalytics
4
4
  class SitesController < ApplicationController
5
+ before_action :require_date_range, only: :show
6
+
5
7
  def index
6
8
  @sites = ViewsPerDay.after(30.days.ago).order_by_totals.group_by_site
9
+ redirect_to(site_path(@sites.first.host)) if @sites.size == 1
7
10
  end
8
11
 
9
12
  def show
10
- scope = ViewsPerDay.where(site: params[:site]).after(30.days.ago)
11
- @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date)
13
+ scope = current_views_per_days
14
+ @histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date, params[:from], params[:to])
12
15
  @referrers = scope.top.group_by_referrer_site
13
16
  @pages = scope.top.group_by_page
14
17
  end
@@ -1,4 +1,7 @@
1
1
  module ActiveAnalytics
2
2
  module ApplicationHelper
3
+ def format_view_count(number)
4
+ number_with_delimiter(number.to_i)
5
+ end
3
6
  end
4
7
  end
@@ -1,4 +1,7 @@
1
1
  module ActiveAnalytics
2
2
  module SitesHelper
3
+ def site_icon(host)
4
+ image_tag("https://icons.duckduckgo.com/ip3/#{host}.ico", referrerpolicy: "no-referrer", class: "referer-favicon")
5
+ end
3
6
  end
4
7
  end
@@ -2,6 +2,7 @@ module ActiveAnalytics
2
2
  class ViewsPerDay < ApplicationRecord
3
3
  validates_presence_of :site, :page, :date
4
4
 
5
+ scope :between_dates, -> (from, to) { where("date BETWEEN ? AND ?", from, to) }
5
6
  scope :after, -> (date) { where("date > ?", date) }
6
7
  scope :order_by_totals, -> { order(Arel.sql("SUM(total) DESC")) }
7
8
  scope :order_by_date, -> { order(:date) }
@@ -33,21 +34,37 @@ module ActiveAnalytics
33
34
  end
34
35
 
35
36
  class Histogram
36
- attr_reader :bars
37
+ attr_reader :bars, :from_date, :to_date
37
38
 
38
- def initialize(scope)
39
+ def initialize(scope, from_date, to_date)
39
40
  @bars = scope.map { |day| Bar.new(day.day, day.total, self) }
41
+ fill_missing_days(@bars, Date.parse(from_date.to_s), Date.parse(to_date.to_s))
42
+ end
43
+
44
+ def fill_missing_days(bars, from, to)
45
+ i = 0
46
+ while (day = from + i) <= to
47
+ if !@bars[i] || @bars[i].label != day
48
+ @bars.insert(i, Bar.new(day, 0, self))
49
+ end
50
+ i += 1
51
+ end
52
+ @bars
40
53
  end
41
54
 
42
55
  def max_value
43
56
  @max_total ||= bars.map(&:value).max
44
57
  end
45
58
 
59
+ def total
60
+ @bars.reduce(0) { |sum, bar| sum += bar.value }
61
+ end
62
+
46
63
  class Bar
47
64
  attr_reader :label, :value, :histogram
48
65
 
49
66
  def initialize(label, value, histogram)
50
- @label, @value,@histogram = label, value, histogram
67
+ @label, @value, @histogram = label, value, histogram
51
68
  end
52
69
 
53
70
  def height
@@ -91,10 +108,12 @@ module ActiveAnalytics
91
108
  end
92
109
 
93
110
  def self.append(params)
94
- vpd = find_or_initialize_by(params)
95
- vpd.referrer_path = nil if vpd.referrer_path?
96
- vpd.total += 1 if vpd.persisted?
97
- vpd.save!
111
+ params[:site] = params[:site].downcase if params[:site]
112
+ params[:page] = params[:page].downcase if params[:page]
113
+ params[:referrer_path] = nil if params[:referrer_path].blank?
114
+ params[:referrer_path] = params[:referrer_path].downcase if params[:referrer_path]
115
+ params[:referrer_host] = params[:referrer_host].downcase if params[:referrer_host]
116
+ find_or_create_by!(params) if where(params).update_all("total = total + 1") == 0
98
117
  end
99
118
  end
100
119
  end
@@ -1,21 +1,33 @@
1
- <table>
2
- <% for page in pages %>
3
- <tr>
4
- <td>
5
- <% if page.host == params[:site] %>
6
- <% if page.path.present? %>
7
- <%= link_to page.path, page_path(site: page.host, page: page_to_params(page.path)) %>
1
+
2
+ <% if pages.empty? %>
3
+ <div class="is-empty">
4
+ <span>no data</span>
5
+ </div>
6
+ <% else %>
7
+ <table>
8
+ <% for page in pages %>
9
+ <tr>
10
+ <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 %>
8
25
  <% else %>
9
- <%= link_to page.host, site_path(site: page.host) %>
10
- <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>
26
+ (None or direct)
11
27
  <% end %>
12
- <% elsif page.host.present? && page.path.present? %>
13
- <%= page.url %>
14
- <% else %>
15
- (None or direct)
16
- <% end %>
17
- </td>
18
- <td><%= page.total %></td>
19
- </tr>
20
- <% end %>
21
- </table>
28
+ </td>
29
+ <td class="number"><%= format_view_count page.total %></td>
30
+ </tr>
31
+ <% end %>
32
+ </table>
33
+ <% end %>
@@ -1,12 +1,9 @@
1
- <section>
2
- <h2>Pages</h2>
3
- </section>
4
-
5
- <section>
1
+ <section class="card">
2
+ <h3><%= format_view_count @histogram.total %> views</h3>
6
3
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
7
4
  </section>
8
5
 
9
- <section>
10
- <h3>Pages</h3>
6
+ <section class="card">
7
+ <h3>Top pages</h3>
11
8
  <%= render "/active_analytics/pages/table", pages: @pages %>
12
9
  </section>
@@ -2,22 +2,25 @@
2
2
  <h2><%= page_from_params %></h2>
3
3
  </section>
4
4
 
5
- <section>
6
- <h3>Views per day</h3>
5
+ <section class="card">
6
+ <h3><%= format_view_count @histogram.total %> views</h3>
7
7
  <%= render "/active_analytics/sites/histogram", histogram: @histogram %>
8
8
  </section>
9
9
 
10
- <section>
11
- <h3>Top referrers</h3>
12
- <%= render "/active_analytics/referrers/table", referrers: @referrers %>
13
- </section>
14
-
15
- <section>
16
- <h3>Previous Pages</h3>
17
- <%= render "table", pages: @previous_pages %>
18
- </section>
10
+ <ul class="grid-auto">
11
+ <li class="card">
12
+ <!-- <section>
13
+ <h3>Top sources</h3>
14
+ <%= render "/active_analytics/referrers/table", referrers: @referrers %>
15
+ </section> -->
16
+ <section>
17
+ <h3>Previous page</h3>
18
+ <%= render "table", pages: @previous_pages %>
19
+ </section>
20
+ </li>
19
21
 
20
- <section>
21
- <h3>Next Pages</h3>
22
- <%= render "table", pages: @next_pages %>
23
- </section>
22
+ <li class="card">
23
+ <h3>Next page</h3>
24
+ <%= render "table", pages: @next_pages %>
25
+ </li>
26
+ </ul>