-
Notifications
You must be signed in to change notification settings - Fork 0
/
google.rb
216 lines (185 loc) · 8.31 KB
/
google.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# frozen_string_literal: true
require 'oauth2'
require 'signet/oauth_2/client'
# OVERRIDE: Hyrax hyrax-v3.5.0 to make analytics a bit more dynamic
# TODO(alishaevn): remove once https://github.com/samvera/hyrax/pull/6063 is available in our current hyrax version
module Hyrax
module Analytics
module Google
extend ActiveSupport::Concern
# rubocop:disable Metrics/BlockLength
class_methods do
# Loads configuration options from config/analytics.yml for Hyrax apps only. You only need PRIVATE_KEY_PATH or
# PRIVATE_KEY_VALUE. VALUE takes precedence.
# Expected structure:
# `analytics:`
# ` google:`
# ` app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME']`
# ` app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION']`
# ` privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH']`
# ` privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE']`
# ` privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET']`
# ` client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL']`
# @return [Config]
def config
@config ||= Config.load_from_yaml
end
class Config
def self.load_from_yaml
filename = Rails.root.join('config', 'analytics.yml')
yaml = YAML.safe_load(ERB.new(File.read(filename)).result)
unless yaml
Hyrax.logger.error("Unable to fetch any keys from #{filename}.")
return new({})
end
config = yaml.fetch('analytics')&.fetch('google', nil)
unless config
Deprecation.warn("Deprecated analytics configuration format found. Please update config/analytics.yml.")
config = yaml.fetch('analytics')
# this has to exist here with a placeholder so it can be set in the Hyrax initializer
# it is only for backward compatibility
config['analytics_id'] = '-'
end
new config
end
# OVERRIDE: Hyrax hyrax-v3.5.0 to add the KEYS variable
KEYS = %w[analytics_id app_name app_version privkey_path privkey_value privkey_secret client_email].freeze
REQUIRED_KEYS = %w[analytics_id app_name app_version privkey_secret client_email].freeze
def initialize(config)
@config = config
end
# @return [Boolean] are all the required values present?
def valid?
# OVERRIDE: Hyrax hyrax-v3.5.0 to require either setting
return false unless @config['privkey_value'].present? || @config['privkey_path'].present?
# OVERRIDE: Hyrax hyrax-v3.5.0 to confirm the presence of required values
REQUIRED_KEYS.all? { |required| @config[required].present? }
end
# OVERRIDE: Hyrax hyrax-v3.5.0 to allow setting all analytics config values
KEYS.each do |key|
# rubocop:disable Style/EvalWithLocation
class_eval %{ def #{key}; @config.fetch('#{key}'); end }
class_eval %{ def #{key}=(value); @config['#{key}'] = value; end }
# rubocop:enable Style/EvalWithLocation
end
end
# Generate an OAuth2 token for Google Analytics
# @return [OAuth2::AccessToken] An OAuth2 access token for GA
def token(scope = 'https://www.googleapis.com/auth/analytics.readonly')
access_token = auth_client(scope).fetch_access_token!
OAuth2::AccessToken.new(oauth_client, access_token['access_token'], expires_in: access_token['expires_in'])
end
def oauth_client
OAuth2::Client.new('', '', authorize_url: 'https://accounts.google.com/o/oauth2/auth',
token_url: 'https://accounts.google.com/o/oauth2/token')
end
def auth_client(scope)
# OVERRIDE: Hyrax hyrax-v3.5.0 to derive the private_key from config.privkey_value
private_key = Base64.decode64(config.privkey_value) if config.privkey_value.present?
if private_key.blank?
raise "Private key file for Google analytics was expected at '#{config.privkey_path}', but no file was found." unless File.exist?(config.privkey_path)
private_key = File.read(config.privkey_path)
end
Signet::OAuth2::Client.new token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: scope,
issuer: config.client_email,
signing_key: OpenSSL::PKCS12.new(private_key, config.privkey_secret).key,
sub: config.client_email
end
# Return a user object linked to a Google Analytics account
# @return [Legato::User] A user account with GA access
def user
Legato::User.new(token)
end
# Return a Google Analytics profile matching specified ID
# @ return [Legato::Management::Profile] A user profile associated with GA
def profile
return unless config.valid?
@profile = user.profiles.detect do |profile|
profile.web_property_id == config.analytics_id
end
raise 'User does not have access to this property' unless @profile
@profile
end
def to_date_range(period)
case period
when "day"
start_date = Time.zone.today
end_date = Time.zone.today
when "week"
start_date = Time.zone.today - 7.days
end_date = Time.zone.today
when "month"
start_date = Time.zone.today - 1.month
end_date = Time.zone.today
when "year"
start_date = Time.zone.today - 1.year
end_date = Time.zone.today
end
[start_date, end_date]
end
def keyword_conversion(date)
case date
when "last12"
start_date = Time.zone.today - 11.months
end_date = Time.zone.today
[start_date, end_date]
else
date.split(",")
end
end
def date_period(period, date)
if period == "range"
date.split(",")
else
to_date_range(period)
end
end
# Configure analytics_start_date in ENV file
def default_date_range
"#{Hyrax.config.analytics_start_date},#{Time.zone.today + 1.day}"
end
# The number of events by day for an action
def daily_events(action, date = default_date_range)
date = date.split(",")
EventsDaily.summary(profile, date[0], date[1], action)
end
# The number of events by day for an action and ID
def daily_events_for_id(id, action, date = default_date_range)
date = date.split(",")
EventsDaily.by_id(profile, date[0], date[1], id, action)
end
# A list of events sorted by highest event count
def top_events(action, date = default_date_range)
date = date.split(",")
Events.send('list', profile, date[0], date[1], action)
end
def unique_visitors(date = default_date_range); end
def unique_visitors_for_id(id, date = default_date_range); end
def new_visitors(period = 'month', date = default_date_range)
date = date_period(period, date)
Visits.new_visits(profile, date[0], date[1])
end
def new_visits_by_day(date = default_date_range, _period = 'day')
date = date.split(",")
VisitsDaily.new_visits(profile, date[0], date[1])
end
def returning_visitors(period = 'month', date = default_date_range)
date = date_period(period, date)
Visits.return_visits(profile, date[0], date[1])
end
def returning_visits_by_day(date = default_date_range, _period = 'day')
date = date.split(",")
VisitsDaily.return_visits(profile, date[0], date[1])
end
def total_visitors(period = 'month', date = default_date_range)
date = date_period(period, date)
Visits.total_visits(profile, date[0], date[1])
end
end
# rubocop:enable Metrics/BlockLength
end
end
end
# rubocop:enable Metrics/ModuleLength