rails_local_analytics 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43724c92d8b23ef42ba4b5334f39f500d69075ea86e732ec405a3c5157200058
4
- data.tar.gz: e1572a460c8bbee78956a431c55afdc208d2cbbc952562b6982a07cc2c24a9c3
3
+ metadata.gz: fbb7ca6534efc3666a302a04271092906ec9b2fc65a316d82b7811e2dd962668
4
+ data.tar.gz: 2cc17d06cf61d6b4b7fd2c559079965fa6c250b12d901b05a3317389e2d5b69d
5
5
  SHA512:
6
- metadata.gz: ba528aae30b5ba1b5bdf814f78ca87d0b4d6bea70e15b2a5fc036a2ac9f27c529af5d59ea66595e15436edb025c71b10af308546fd38bf32155150175d158599
7
- data.tar.gz: 11575f750a08aaafcf066fb9719209c3e5d3a8dd1447210bee1476786d5e48a88b3a070e350de04eaffcfee04fcfe1e56365483b6c28cc94f91d0a90e81ffe4e
6
+ metadata.gz: 8d9e8dea022bb79486a8bc6395106841a4f9d056accf68f3534072bac1e205f40df426664ed071abd54715d38b0a00ef879aaa080b4a0a51f6ba3e31b207101e
7
+ data.tar.gz: e249aa1fb3f83ccaf75ffdb774192de6549889b8fb38b3537f5a99cc5edc6aab7da7d1b01caccb6012ffa4c4695739f2a15107ecbc49804db9e3e61c856ea13f
data/README.md CHANGED
@@ -27,6 +27,8 @@ It is fully customizable to store more details if desired.
27
27
 
28
28
  ![Screenshot 3](/screenshot_3.png)
29
29
 
30
+ ![Screenshot 4](/screenshot_4.png)
31
+
30
32
  ## Installation
31
33
 
32
34
  ```ruby
@@ -118,8 +120,8 @@ class ApplicationController < ActionController::Base
118
120
  RailsLocalAnalytics.record_request(
119
121
  request: request,
120
122
  custom_attributes: { # optional
121
- TrackedRequestsByDaySite.name => site_based_attrs,
122
- #TrackedRequestsByDayPage.name => {},
123
+ site: site_based_attrs,
124
+ page: {},
123
125
  },
124
126
  )
125
127
  end
@@ -136,6 +138,9 @@ Some examples of additional things you may want to track:
136
138
  * You may not need to store this in a new column, one example pattern could be to store this data in the existing `platform` database field
137
139
  - Country detection
138
140
  * Country detection is difficult. As such we dont try to include it by default.
141
+ * Consider using language detection instead
142
+ - Language detection
143
+ * You can gather the language from the `request.env["HTTP_ACCEPT_LANGUAGE"]` or `browser.accept_language.first.full`
139
144
  - Users or organizations
140
145
  * You may want to track your users or another model which is a core tenant to your particular application
141
146
 
@@ -183,8 +188,8 @@ class ApplicationController
183
188
  def record_page_view
184
189
  # perform other logic and call RailsLocalAnalytics.record_request
185
190
 
186
- TrackedRequestsByDayPage.where("date < ?", 3.months.ago).delete_all
187
- TrackedRequestsByDaySite.where("date < ?", 3.months.ago).delete_all
191
+ TrackedRequestsByDayPage.where("day < ?", 3.months.ago).delete_all
192
+ TrackedRequestsByDaySite.where("day < ?", 3.months.ago).delete_all
188
193
  end
189
194
  end
190
195
  ```
@@ -1,3 +1,11 @@
1
+ body{
2
+ padding-bottom: 20px;
3
+ }
4
+
5
+ table td{
6
+ overflow-wrap: anywhere;
7
+ }
8
+
1
9
  input, select {
2
10
  border-radius: 5px;
3
11
  border: 1px solid #333;
@@ -5,6 +13,10 @@ input, select {
5
13
  padding: 2px 5px;
6
14
  }
7
15
 
8
- .tablesorter-header{
9
- background-image: none !important;
16
+ button.is-link{
17
+ border: none;
18
+ background: none;
19
+ color: #2780e3;
20
+ text-decoration: underline;
21
+ cursor: pointer;
10
22
  }
@@ -1,5 +1,9 @@
1
1
  module RailsLocalAnalytics
2
2
  class ApplicationController < ActionController::Base
3
3
 
4
+ def root
5
+ redirect_to tracked_requests_path(type: :page)
6
+ end
7
+
4
8
  end
5
9
  end
@@ -1,22 +1,24 @@
1
1
  module RailsLocalAnalytics
2
2
  class DashboardController < ApplicationController
3
+ PER_PAGE_LIMIT = 1000
4
+
5
+ helper_method :pagination_page_number
3
6
 
4
7
  def index
5
- params[:type] ||= "Site"
8
+ params[:type] ||= "page"
6
9
 
7
10
  case params[:type]
8
- when "Site"
11
+ when "site"
9
12
  @klass = TrackedRequestsByDaySite
10
- when "Page"
13
+ when "page"
11
14
  @klass = TrackedRequestsByDayPage
12
15
  else
13
- raise ArgumentError
16
+ head 404
17
+ return
14
18
  end
15
19
 
16
20
  if params[:group_by].present? && !@klass.display_columns.include?(params[:group_by])
17
- params[:group_by] = "All"
18
- else
19
- params[:group_by] ||= "All"
21
+ raise ArgumentError
20
22
  end
21
23
 
22
24
  if params[:start_date].present?
@@ -35,27 +37,119 @@ module RailsLocalAnalytics
35
37
  @end_date = @start_date
36
38
  end
37
39
 
38
- @tracked_requests = fetch_records(@start_date, @end_date)
40
+ @results = fetch_records(@start_date, @end_date)
41
+
42
+ if @results.size < PER_PAGE_LIMIT
43
+ prev_start_date, prev_end_date = get_prev_dates(@start_date, @end_date)
44
+
45
+ @prev_period_results = fetch_records(prev_start_date, prev_end_date)
46
+
47
+ if @prev_period_results.size >= PER_PAGE_LIMIT
48
+ @prev_period_results = nil
49
+ end
50
+ end
51
+ end
52
+
53
+ def difference
54
+ case params.require(:type)
55
+ when "site"
56
+ @klass = TrackedRequestsByDaySite
57
+ when "page"
58
+ @klass = TrackedRequestsByDayPage
59
+ end
60
+
61
+ start_date = Date.parse(params.require(:start_date))
62
+ end_date = Date.parse(params.require(:end_date))
63
+
64
+ prev_start_date, prev_end_date = get_prev_dates(start_date, end_date)
65
+
66
+ where_conditions = params.require(:conditions).permit(*@klass.display_columns)
67
+
68
+ current_total = fetch_records(
69
+ start_date,
70
+ end_date,
71
+ where_conditions: where_conditions,
72
+ pluck_columns: ["SUM(total)"],
73
+ ).first
74
+
75
+ prev_total = fetch_records(
76
+ prev_start_date,
77
+ prev_end_date,
78
+ where_conditions: where_conditions,
79
+ pluck_columns: ["SUM(total)"],
80
+ ).first
39
81
 
40
- prev_start_date = @start_date - (@end_date - @start_date)
41
- prev_end_date = @end_date - (@end_date - @start_date)
82
+ if prev_total
83
+ diff = current_total - prev_total
84
+ else
85
+ diff = current_total
86
+ end
42
87
 
43
- @prev_period_tracked_requests = fetch_records(prev_start_date, prev_end_date)
88
+ render json: {difference: diff}
44
89
  end
45
90
 
46
91
  private
47
92
 
48
- def fetch_records(start_date, end_date)
93
+ def fetch_records(start_date, end_date, where_conditions: nil, pluck_columns: nil)
49
94
  tracked_requests = @klass
50
- .where("day >= ?", @start_date)
51
- .where("day <= ?", @end_date)
95
+ .where("day >= ?", start_date)
96
+ .where("day <= ?", end_date)
52
97
  .order(total: :desc)
53
98
 
99
+ if where_conditions.nil? && pluck_columns.nil?
100
+ tracked_requests = tracked_requests
101
+ .limit(PER_PAGE_LIMIT)
102
+ .offset(PER_PAGE_LIMIT * (pagination_page_number-1))
103
+
104
+ if params[:filter].present?
105
+ col, val = params[:filter].split("==")
106
+
107
+ if @klass.display_columns.include?(col)
108
+ tracked_requests = tracked_requests.where(col => val)
109
+ else
110
+ raise ArgumentError
111
+ end
112
+ end
113
+ end
114
+
115
+ if where_conditions
116
+ tracked_requests = tracked_requests.where(where_conditions)
117
+ end
118
+
54
119
  if params[:search].present?
55
120
  tracked_requests = tracked_requests.multi_search(params[:search])
56
121
  end
57
122
 
58
- return tracked_requests
123
+ if params[:group_by].present?
124
+ group_by_columns = [params[:group_by]]
125
+ pluck_columns = [params[:group_by], "SUM(total)"]
126
+ else
127
+ group_by_columns = @klass.display_columns
128
+ pluck_columns ||= @klass.display_columns + ["SUM(total)"]
129
+ end
130
+
131
+ tracked_requests
132
+ .group(*group_by_columns)
133
+ .pluck(*pluck_columns)
59
134
  end
135
+
136
+ def pagination_page_number
137
+ page = params[:page].presence.to_i || 1
138
+ page = 1 if page.zero?
139
+ page
140
+ end
141
+
142
+ def get_prev_dates(start_date, end_date)
143
+ if start_date == end_date
144
+ prev_start_date = start_date - 1.day
145
+ prev_end_date = prev_start_date
146
+ else
147
+ duration = end_date - start_date
148
+ prev_start_date = start_date - duration
149
+ prev_end_date = end_date - duration
150
+ end
151
+ return [prev_start_date, prev_end_date]
152
+ end
153
+
60
154
  end
61
155
  end
@@ -1,6 +1,5 @@
1
1
  module RailsLocalAnalytics
2
2
  class RecordRequestJob < ApplicationJob
3
- MODELS = [TrackedRequestsByDaySite, TrackedRequestsByDayPage].freeze
4
3
  def perform(json)
5
4
  if json.is_a?(String)
6
5
  json = JSON.parse(json)
@@ -8,49 +7,56 @@ module RailsLocalAnalytics
8
7
 
9
8
  request_hash = json.fetch("request_hash")
10
9
 
11
- custom_attributes_by_model = json.fetch("custom_attributes")
10
+ custom_attributes_by_type = json.fetch("custom_attributes")
12
11
 
13
- MODELS.each do |model|
14
- custom_attrs = custom_attributes_by_model && custom_attributes_by_model[model.name]
12
+ ["site", "page"].each do |type|
13
+ case type
14
+ when "site"
15
+ klass = TrackedRequestsByDaySite
16
+ when "page"
17
+ klass = TrackedRequestsByDayPage
18
+ end
19
+
20
+ custom_attrs = custom_attributes_by_type && custom_attributes_by_type[type]
15
21
 
16
- attrs = build_attrs(model, custom_attrs, request_hash)
22
+ attrs = build_attrs(klass, custom_attrs, request_hash)
17
23
 
18
24
  attrs["day"] = json.fetch("day")
19
25
 
20
- existing_record = model.find_by(attrs)
26
+ existing_record = klass.find_by(attrs)
21
27
 
22
28
  if existing_record
23
29
  existing_record.increment!(:total, 1)
24
30
  else
25
- model.create!(attrs)
31
+ klass.create!(attrs)
26
32
  end
27
33
  end
28
34
  end
29
35
 
30
36
  private
31
37
 
32
- def build_attrs(model, attrs, request_hash)
38
+ def build_attrs(klass, attrs, request_hash)
33
39
  attrs ||= {}
34
40
 
35
41
  field = "url_hostname"
36
- if !skip_field?(field, attrs, model)
37
- attrs[field] = request_hash.fetch("host").downcase
42
+ if !skip_field?(field, attrs, klass)
43
+ attrs[field] = request_hash.fetch("host")
38
44
  end
39
45
 
40
46
  field = "url_path"
41
- if !skip_field?(field, attrs, model)
42
- attrs[field] = request_hash.fetch("path").downcase
47
+ if !skip_field?(field, attrs, klass)
48
+ attrs[field] = request_hash.fetch("path")
43
49
  end
44
50
 
45
51
  if request_hash.fetch("referrer").present?
46
52
  field = "referrer_hostname"
47
- if !skip_field?(field,attrs, model)
53
+ if !skip_field?(field,attrs, klass)
48
54
  referrer_hostname, referrer_path = split_referrer(request_hash.fetch("referrer"))
49
55
  attrs[field] = referrer_hostname
50
56
  end
51
57
 
52
58
  field = "referrer_path"
53
- if !skip_field?(field, attrs, model)
59
+ if !skip_field?(field, attrs, klass)
54
60
  if referrer_path.nil?
55
61
  referrer_hostname, referrer_path = split_referrer(request_hash.fetch("referrer"))
56
62
  end
@@ -60,13 +66,13 @@ module RailsLocalAnalytics
60
66
 
61
67
  if request_hash.fetch("user_agent").present?
62
68
  field = "platform"
63
- if !skip_field?(field, attrs, model)
69
+ if !skip_field?(field, attrs, klass)
64
70
  browser ||= create_browser_object(request_hash)
65
71
  attrs[field] = browser.platform.name
66
72
  end
67
73
 
68
74
  field = "browser_engine"
69
- if !skip_field?(field, attrs, model)
75
+ if !skip_field?(field, attrs, klass)
70
76
  browser ||= create_browser_object(request_hash)
71
77
  attrs[field] = get_browser_engine(browser)
72
78
  end
@@ -76,8 +82,6 @@ module RailsLocalAnalytics
76
82
  end
77
83
 
78
84
  def split_referrer(referrer)
79
- referrer = referrer.downcase
80
-
81
85
  uri = URI(referrer)
82
86
 
83
87
  if uri.host.present?
@@ -114,8 +118,8 @@ module RailsLocalAnalytics
114
118
  )
115
119
  end
116
120
 
117
- def skip_field?(field, attrs, model)
118
- attrs&.has_key?(field) || !model.column_names.include?(field)
121
+ def skip_field?(field, attrs, klass)
122
+ attrs&.has_key?(field) || !klass.column_names.include?(field)
119
123
  end
120
124
 
121
125
  end
@@ -15,7 +15,7 @@ module RailsLocalAnalytics
15
15
  sql_conditions << "(#{col} #{like} :search)"
16
16
  end
17
17
 
18
- relation = self.where(sql_conditions.join(" OR "), search: "%#{str}%")
18
+ relation = relation.where(sql_conditions.join(" OR "), search: "%#{sanitize_sql_like(str)}%")
19
19
  end
20
20
 
21
21
  next relation
@@ -26,9 +26,5 @@ module RailsLocalAnalytics
26
26
  column_names - ["id", "created_at", "updated_at", "total", "day"]
27
27
  end
28
28
 
29
- def matches?(other_record)
30
- day == other_record.day && self.class.display_columns.all?{|col_name| self.send(col_name) == other_record.send(col_name) }
31
- end
32
-
33
29
  end
34
30
  end
@@ -9,12 +9,7 @@
9
9
  <%= csp_meta_tag %>
10
10
 
11
11
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.4.0/cosmo/bootstrap.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.32.0/css/theme.default.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
13
12
  <%= stylesheet_link_tag "rails_local_analytics/application" %>
14
-
15
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
16
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.0/js/jquery.tablesorter.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
17
- <%= javascript_include_tag "rails_local_analytics/application" %>
18
13
  </head>
19
14
 
20
15
  <body>
@@ -23,6 +18,16 @@
23
18
  <div class="navbar-header">
24
19
  <%= link_to title , root_path, class: "navbar-brand" %>
25
20
  </h1>
21
+
22
+ <ul class="nav navbar-nav navbar-right">
23
+ <li class="<%= 'active' if params[:type] == "page" %>">
24
+ <%= link_to "Requests By Page", tracked_requests_path(type: "page") %>
25
+ </li>
26
+
27
+ <li class="<%= 'active' if params[:type] == "site" %>">
28
+ <%= link_to "Requests By Site", tracked_requests_path(type: "site") %>
29
+ </li>
30
+ </ul>
26
31
  </div>
27
32
  </nav>
28
33
 
@@ -1,81 +1,117 @@
1
- <% page_group_by_options = options_for_select([["All", "All"]] + TrackedRequestsByDayPage.display_columns.map{|x| [x.titleize.sub("Url ", "URL "), x]}) %>
2
- <% site_group_by_options = options_for_select([["All", "All"]] + TrackedRequestsByDaySite.display_columns.map{|x| [x.titleize.sub("Url ", "URL "), x]}) %>
1
+ <% data_columns = params[:group_by].present? ? [params[:group_by]] : @klass.display_columns %>
3
2
 
4
- <%= form_tag url_for(params.except(:start_date, :to).to_unsafe_hash), method: "get", class: "well well-sm" do %>
5
- <div>
6
- <label style="margin-right: 10px;">Type: <%= select_tag :type, options_for_select(["Site", "Page"], params[:type]) %></label>
3
+ <div class="well well-sm">
4
+ <%= form_tag url_for(params.except(:start_date, :to).to_unsafe_hash), method: "get", id: "search-form" do %>
5
+ <div>
6
+ <label style="margin-right: 10px;">Group By: <%= select_tag :group_by, options_for_select([["All", nil]] + @klass.display_columns.map{|x| [x.titleize.sub("Url ", "URL "), x]} , params[:group_by]) %></label>
7
7
 
8
- <label style="margin-right: 10px;">Group By: <%= select_tag :group_by, options_for_select([["All", "All"]] + @klass.display_columns.map{|x| [x.titleize.sub("Url ", "URL "), x]} , params[:group_by]) %></label>
9
- <label style="margin-right: 10px;">Search: <%= text_field_tag :search, params[:search] %></label>
8
+ <label style="margin-right: 10px;">From: <%= date_field_tag :start_date, @start_date %></label>
9
+ <label style="margin-right: 10px;">To: <%= date_field_tag :end_date, @end_date %></label>
10
10
 
11
- <label style="margin-right: 10px;">From: <%= date_field_tag :start_date, @start_date %></label>
12
- <label style="margin-right: 10px;">To: <%= date_field_tag :end_date, @end_date %></label>
13
- <button type="submit">Filter</button>
14
- </div>
11
+ <label style="margin-right: 10px;">Search: <%= text_field_tag :search, params[:search] %></label>
12
+ <% end %>
13
+
14
+ <% if params[:filter] %>
15
+ <div>
16
+ <label>Active Filter:</label>
17
+ <div class="badge badge-primary" style="margin-top: 5px; margin-bottom: 10px;">
18
+ <% filter_col, filter_val = params[:filter].split("==") %>
19
+ <%= filter_col.titleize.sub("Url ", "URL ") %> = "<%= filter_val %>"
20
+ </div>
21
+ <%= link_to "Remove Filter", url_for(params.to_unsafe_h.merge(filter: nil)) %>
22
+ </div>
23
+ <% end %>
15
24
 
16
25
  <div>
17
- <%= link_to "Today", url_for(params.merge(start_date: Date.today, end_date: Date.today).to_unsafe_hash) %>
26
+ <%= link_to "Today", url_for(params.merge(start_date: Date.today, end_date: Date.today).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == Date.today && @end_date == Date.today) %>
27
+ |
28
+ <% yesterday = Date.today - 1.day %>
29
+ <%= link_to "Yesterday", url_for(params.merge(start_date: yesterday, end_date: yesterday).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == yesterday && @end_date == yesterday) %>
18
30
  |
19
- <%= link_to "Yesterday", url_for(params.merge(start_date: (Date.today - 1.day), end_date: (Date.today - 1.day)).to_unsafe_hash) %>
31
+ <%= link_to "Last 7 days", url_for(params.merge(start_date: 7.days.ago.to_date, end_date: Date.today).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == 7.days.ago.to_date && @end_date == Date.today) %>
20
32
  |
21
- <%= link_to "Last 7 days", url_for(params.merge(start_date: 7.days.ago.to_date, end_date: Date.today).to_unsafe_hash) %>
33
+ <%= link_to "Last 30 days", url_for(params.merge(start_date: 30.days.ago.to_date, end_date: Date.today).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == 30.days.ago.to_date && @end_date == Date.today) %>
22
34
  |
23
- <%= link_to "Last 30 days", url_for(params.merge(start_date: 30.days.ago.to_date, end_date: Date.today).to_unsafe_hash) %>
35
+ <%= link_to "Last 3 Months", url_for(params.merge(start_date: 3.months.ago.to_date, end_date: Date.today).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == 3.months.ago.to_date && @end_date == Date.today) %>
36
+ |
37
+ <%= link_to "Last 6 Months", url_for(params.merge(start_date: 6.months.ago.to_date, end_date: Date.today).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == 6.months.ago.to_date && @end_date == Date.today) %>
38
+ |
39
+ <%= link_to "Last Year", url_for(params.merge(start_date: 1.year.ago.to_date, end_date: Date.today).to_unsafe_hash), style: ("font-weight: bold;" if @start_date == 1.year.ago.to_date && @end_date == Date.today) %>
24
40
  </div>
25
- <% end %>
41
+ </div>
26
42
 
27
- <h2>Tracked Requests - <%= @klass.name.titleize.split(" ").last %></h2>
43
+ <h2>Requests by <%= params[:type].titleize %></h2>
28
44
 
29
- <table class="table table-striped table-condensed table-sortable">
45
+ <table class="table table-striped table-condensed">
30
46
  <thead>
31
- <% if params[:group_by] == "All" %>
32
- <% @klass.display_columns.each do |col_name| %>
33
- <th>
34
- <%= col_name.titleize.sub("Url ", "URL ") %>
35
- <span title="Sort column">&varr;</span>
36
- </th>
37
- <% end %>
38
- <% else %>
39
- <td>
40
- <%= params[:group_by].titleize.sub("Url ", "URL ") %>
41
- <span title="Sort column">&varr;</span>
42
- </td>
47
+ <% data_columns.each do |header| %>
48
+ <th>
49
+ <%= header.titleize.sub("Url ", "URL ") %>
50
+ </th>
43
51
  <% end %>
44
52
 
45
53
  <th>
46
- Total <span title="Difference compared to previous period">(Difference)</span>
47
- <span title="Sort column">&varr;</span>
54
+ Total
55
+ </th>
56
+
57
+ <th>
58
+ Prev Period Difference
48
59
  </th>
49
60
  </thead>
50
61
 
51
62
  <tbody>
52
- <% @tracked_requests.group_by{|x| params[:group_by] != "All" ? x.send(params[:group_by]) : @klass.display_columns.map{|col| x.send(col)}}.each do |_grouping, records| %>
53
- <% first_record = records.first %>
54
-
63
+ <% @results.each_with_index do |row, row_index| %>
55
64
  <tr>
56
- <% if params[:group_by] == "All" %>
57
- <% @klass.display_columns.each do |col_name| %>
58
- <td>
59
- <%= first_record.send(col_name) %>
60
- </td>
61
- <% end %>
62
- <% else %>
65
+ <% row[0..-2].each_with_index do |value, col_index| %>
63
66
  <td>
64
- <%= first_record.send(params[:group_by]) %>
67
+ <% filter_param = "#{data_columns[col_index]}==#{value}" %>
68
+ <%= link_to (value || ""), url_for(params.to_unsafe_h.merge(filter: filter_param)), title: "Filter" %>
65
69
  </td>
66
70
  <% end %>
67
71
 
68
72
  <td>
69
- <% total = records.sum(&:total) %>
70
- <%= total %>
73
+ <% total = row.last %>
74
+ <%= number_with_delimiter(total) %>
75
+ </td>
76
+
77
+ <td>
78
+ <% if @prev_period_results.nil? %>
79
+ <%
80
+ diff_params = {
81
+ format: :json,
82
+ type: params[:type],
83
+ start_date: @start_date,
84
+ end_date: @end_date,
85
+ conditions: {},
86
+ }
87
+
88
+ data_columns.each_with_index do |col, col_index|
89
+ diff_params[:conditions][col] = row[col_index]
90
+ end
91
+
92
+ placeholder_id = "diff-placeholder-#{row_index}"
93
+ %>
71
94
 
72
- <% _grouping, prev_period_records = @prev_period_tracked_requests.select{|x| first_record.matches?(x) }.group_by{|x| params[:group_by] != "All" ? x.send(params[:group_by]) : @klass.display_columns.map{|col| x.send(col)}}.first %>
73
- <% diff = total - prev_period_records.sum(&:total) %>
95
+ <button type="button" class="load-difference is-link" data-url="<%= difference_tracked_requests_path(diff_params) %>" data-placeholder-id="<%= placeholder_id %>">Load Difference</button>
74
96
 
75
- <% if diff >= 0 %>
76
- (+<%= diff %>)
97
+ <div id="<%= placeholder_id %>"></div>
77
98
  <% else %>
78
- (<%= diff %>)
99
+ <% prev_period_row_index = @prev_period_results.index{|prev_period_row| row[0..-2] == prev_period_row[0..-2] } %>
100
+
101
+ <% if prev_period_row_index.nil? %>
102
+ +<%= number_with_delimiter(total) %>
103
+ <% else %>
104
+ <% prev_period_row = @prev_period_results.delete_at(prev_period_row_index) %>
105
+
106
+ <% prev_period_total = prev_period_row.last %>
107
+ <% diff = total - prev_period_total %>
108
+
109
+ <% if diff >= 0 %>
110
+ +<%= number_with_delimiter(diff) %>
111
+ <% else %>
112
+ <%= number_with_delimiter(diff) %>
113
+ <% end %>
114
+ <% end %>
79
115
  <% end %>
80
116
  </td>
81
117
  </tr>
@@ -83,19 +119,62 @@
83
119
  </tbody>
84
120
  </table>
85
121
 
122
+ <div style="text-align: center;">
123
+ <% if params[:page].present? && params[:page].to_i > 1 %>
124
+ <%= link_to "Prev", url_for(params.to_unsafe_h.merge(page: pagination_page_number-1)) %>
125
+ |
126
+ <% end %>
127
+
128
+ <% if @results.size >= RailsLocalAnalytics::DashboardController::PER_PAGE_LIMIT %>
129
+ <%= link_to "Next", url_for(params.to_unsafe_h.merge(page: pagination_page_number+1)) %>
130
+ <% end %>
131
+ </div>
132
+
86
133
  <script>
87
- $(function(){
88
- var siteGroupByOptions = "<%= j site_group_by_options %>";
89
- var pageGroupByOptions = "<%= j page_group_by_options %>";
90
-
91
- $(document).on("change", "select[name='type']", function(){
92
- var group_by_select = $("select[name='group_by']");
93
-
94
- if($(this).val() == "Site"){
95
- group_by_select.html(siteGroupByOptions);
96
- }else{
97
- group_by_select.html(pageGroupByOptions);
98
- }
134
+ document.addEventListener("DOMContentLoaded", function(){
135
+ var form = document.querySelector("form#search-form")
136
+
137
+ document.querySelectorAll("form#search-form select, form#search-form input").forEach(function(el){
138
+ el.addEventListener("change", function(){
139
+ form.submit();
140
+ });
141
+ });
142
+
143
+ document.querySelectorAll("button.load-difference").forEach(function(el){
144
+ el.addEventListener("click", async function(){
145
+ el.disabled = true;
146
+
147
+ var csrf_token = document.querySelector('meta[name="csrf-token"]').content;
148
+
149
+ var request = new Request(
150
+ el.getAttribute("data-url"),
151
+ {
152
+ method: "GET",
153
+ headers: {
154
+ "Content-Type": "application/json",
155
+ Accept: "application/json",
156
+ "X-CSRF-Token": csrf_token,
157
+ },
158
+ },
159
+ );
160
+
161
+ var response = await fetch(request)
162
+ .then(function(response){
163
+ response.json().then(function(parsed_response){
164
+ el.style.display = "none";
165
+
166
+ var placeholderEl = document.querySelector("#" + el.getAttribute("data-placeholder-id"));
167
+
168
+ if(parsed_response.difference >= 1){
169
+ placeholderEl.innerHTML = "+" + parsed_response.difference.toLocaleString();
170
+ }else{
171
+ placeholderEl.innerHTML = "" + parsed_response.difference.toLocaleString();
172
+ }
173
+ });
174
+
175
+ });
176
+
177
+ });
99
178
  });
100
179
  });
101
180
  </script>
@@ -0,0 +1,19 @@
1
+ if Browser::VERSION.to_f < 6.0
2
+ Browser::Base.class_eval do
3
+ def chromium_based?
4
+ false
5
+ end
6
+ end
7
+
8
+ Browser::Chrome.class_eval do
9
+ def chromium_based?
10
+ true
11
+ end
12
+ end
13
+
14
+ Browser::Edge.class_eval do
15
+ def chromium_based?
16
+ match? && ua.match?(/\bEdg\b/)
17
+ end
18
+ end
19
+ end
data/config/routes.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  RailsLocalAnalytics::Engine.routes.draw do
2
- get "/tracked_requests", to: "dashboard#index"
2
+ get "/tracked_requests/:type", to: "dashboard#index", as: :tracked_requests
3
+ get "/tracked_requests/:type/difference", to: "dashboard#difference", as: :difference_tracked_requests, constraints: {format: :json}
3
4
 
4
- root to: "dashboard#index"
5
+ root to: "application#root"
5
6
  end
@@ -1,3 +1,3 @@
1
1
  module RailsLocalAnalytics
2
- VERSION = "0.1.0".freeze
2
+ VERSION = "0.2.1".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_local_analytics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Weston Ganger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-03 00:00:00.000000000 Z
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -92,18 +92,17 @@ files:
92
92
  - README.md
93
93
  - Rakefile
94
94
  - app/assets/config/rails_local_analytics_manifest.js
95
- - app/assets/javascripts/rails_local_analytics/application.js
96
95
  - app/assets/stylesheets/rails_local_analytics/application.css
97
96
  - app/controllers/rails_local_analytics/application_controller.rb
98
97
  - app/controllers/rails_local_analytics/dashboard_controller.rb
99
98
  - app/jobs/rails_local_analytics/application_job.rb
100
99
  - app/jobs/rails_local_analytics/record_request_job.rb
101
- - app/lib/rails_local_analytics/histogram.rb
102
100
  - app/models/rails_local_analytics/application_record.rb
103
101
  - app/models/tracked_requests_by_day_page.rb
104
102
  - app/models/tracked_requests_by_day_site.rb
105
103
  - app/views/layouts/rails_local_analytics/application.html.erb
106
104
  - app/views/rails_local_analytics/dashboard/index.html.erb
105
+ - config/initializers/browser_monkey_patches.rb
107
106
  - config/routes.rb
108
107
  - lib/rails_local_analytics.rb
109
108
  - lib/rails_local_analytics/engine.rb
@@ -1,7 +0,0 @@
1
- $(function(){
2
- $('table.table-sortable').tablesorter();
3
-
4
- $(document).on("click", "tr[data-url]", function(){
5
- window.location = $(this).data("url");
6
- });
7
- });
@@ -1,47 +0,0 @@
1
- module RailsLocalAnalytics
2
- class Histogram
3
- attr_reader :bars, :from_date, :to_date
4
-
5
- def initialize(scope, from_date, to_date)
6
- @scope = scope
7
- @from_date, @to_date = from_date, to_date
8
- @bars = scope.map { |record| Bar.new(record.date, record.total, self) }
9
- fill_missing_days(@bars, @from_date, @to_date)
10
- end
11
-
12
- def fill_missing_days(bars, from, to)
13
- i = 0
14
- while (day = from + i) <= to
15
- if !@bars[i] || @bars[i].label != day
16
- @bars.insert(i, Bar.new(day, 0, self))
17
- end
18
- i += 1
19
- end
20
- @bars
21
- end
22
-
23
- def max_value
24
- @max_total ||= bars.map(&:value).max
25
- end
26
-
27
- def total
28
- @bars.reduce(0) { |sum, bar| sum += bar.value }
29
- end
30
-
31
- class Bar
32
- attr_reader :label, :value, :histogram
33
-
34
- def initialize(label, value, histogram)
35
- @label, @value, @histogram = label, value, histogram
36
- end
37
-
38
- def height
39
- if histogram.max_value > 0
40
- (value.to_f / histogram.max_value).round(2)
41
- else
42
- 0
43
- end
44
- end
45
- end
46
- end
47
- end