gaapi 0.0.0alpha1
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 +7 -0
- data/bin/gaapi +7 -0
- data/lib/gaapi.rb +7 -0
- data/lib/gaapi/main.rb +107 -0
- data/lib/gaapi/query.rb +174 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ed1eec8428af3796678e4689d89342be081a5d65
|
4
|
+
data.tar.gz: d2053bd9f3239fefdaf3f61b60006a7ed6812a8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 77e6411840c67a025016f15dd1f42222be5dcc57e5fde8470178236b4a7fc7fbe854ff56d49366535756f5af14009b6dcf9dc1fff2c49d38aab0bab9307805b7
|
7
|
+
data.tar.gz: 0f09fa129edc4792e5e3d8d30a705b410b16051ba75585560524d67b2a18c7c5bd13d88cd9a67354e97e584f49ff46b384ffffbb1450ad78b293ab807fc874ec
|
data/bin/gaapi
ADDED
data/lib/gaapi.rb
ADDED
data/lib/gaapi/main.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
##
|
6
|
+
# Basic runner for nginx config file generation.
|
7
|
+
module Main
|
8
|
+
class << self
|
9
|
+
def call
|
10
|
+
options = process_options
|
11
|
+
|
12
|
+
puts "options: #{options.inspect}" if options[:debug]
|
13
|
+
|
14
|
+
# return unless (result = Query.call(options))
|
15
|
+
query = Query.new((options[:query_file] || STDIN).read, options)
|
16
|
+
|
17
|
+
return if options[:dry_run]
|
18
|
+
|
19
|
+
result = query.execute
|
20
|
+
|
21
|
+
if result.code != "200"
|
22
|
+
puts "Request failed #{result.code}"
|
23
|
+
puts Query.pp(result.body)
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
case options[:output_format]
|
28
|
+
when :csv
|
29
|
+
puts Query.csv(result.body)
|
30
|
+
else
|
31
|
+
puts Query.pp(result.body)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_options
|
36
|
+
options = { credentials: File.join(Dir.home, ".gaapi/ga-api-key") }
|
37
|
+
opts = OptionParser.new do |opts|
|
38
|
+
opts.banner = "Usage: [options] VIEW_ID"
|
39
|
+
opts.accept(Date)
|
40
|
+
|
41
|
+
opts.on("-a TOKEN",
|
42
|
+
"--access-token TOKEN",
|
43
|
+
"An access token obtained from https://developers.google.com/oauthplayground.") do |access_token|
|
44
|
+
options[:access_token] = access_token
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--csv",
|
48
|
+
"Output result as a csv file.") do
|
49
|
+
options[:output_format] = :csv
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-c CREDENTIALS",
|
53
|
+
"--credentials CREDENTIALS",
|
54
|
+
"Location of the credentials file. Default: #{options[:credentials]}.") do |credentials|
|
55
|
+
options[:credentials] = credentials
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on("-d", "--debug", "Print debugging information.") do
|
59
|
+
options[:debug] = true
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on("-e",
|
63
|
+
"--end-date END_DATE",
|
64
|
+
Date,
|
65
|
+
"Report including END_DATE.") do |end_date|
|
66
|
+
options[:end_date] = end_date
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("-n", "--dry-run", "Don't actually send the query to Google.") do
|
70
|
+
options[:dry_run] = true
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on("-q QUERYFILE",
|
74
|
+
"--query-file QUERYFILE",
|
75
|
+
"File containing the query. Default STDIN.") do |query_file|
|
76
|
+
options[:query_file] = File.open(query_file)
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on("-s",
|
80
|
+
"--start-date START_DATE",
|
81
|
+
Date,
|
82
|
+
"Report including START_DATE.") do |start_date|
|
83
|
+
options[:start_date] = start_date
|
84
|
+
end
|
85
|
+
end
|
86
|
+
opts.parse!
|
87
|
+
opts.abort("You must provide a view ID. \n" + opts.to_s) unless ARGV.size == 1
|
88
|
+
options[:view_id] = ARGV[0]
|
89
|
+
|
90
|
+
if options[:end_date].nil? && !options[:start_date].nil?
|
91
|
+
options[:end_date] = options[:start_date]
|
92
|
+
elsif options[:start_date].nil? && !options[:end_date].nil?
|
93
|
+
options[:start_date] = options[:end_date]
|
94
|
+
elsif options[:start_date].nil? && options[:end_date].nil?
|
95
|
+
options[:end_date] = options[:start_date] = (Date.today - 1).to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
options
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Some hints:
|
104
|
+
# https://stackoverflow.com/a/41179467/3109926
|
105
|
+
# http://www.daimto.com/google-service-accounts-with-json-file/
|
106
|
+
# https://github.com/google/google-api-ruby-client/issues/489
|
107
|
+
# The above gave some progress.
|
data/lib/gaapi/query.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
class Query
|
7
|
+
class << self
|
8
|
+
def call(options)
|
9
|
+
# http://www.rubydoc.info/github/google/google-api-ruby-client/toplevel
|
10
|
+
# https://github.com/google/google-auth-library-ruby
|
11
|
+
scopes = %w[
|
12
|
+
https://www.googleapis.com/auth/analytics
|
13
|
+
https://www.googleapis.com/auth/analytics.readonly
|
14
|
+
]
|
15
|
+
|
16
|
+
authorization = Google::Auth::ServiceAccountCredentials.make_creds(
|
17
|
+
json_key_io: File
|
18
|
+
.open(options[:credentials] || File.join(Dir.home,
|
19
|
+
".ga-credentials/ga-api-key")),
|
20
|
+
scope: scopes
|
21
|
+
)
|
22
|
+
|
23
|
+
service = Google::Apis::AnalyticsreportingV4::AnalyticsReportingService.new
|
24
|
+
service.authorization = authorization
|
25
|
+
|
26
|
+
request = Google::Apis::AnalyticsreportingV4::ReportRequest.new(
|
27
|
+
view_id: options[:view_id],
|
28
|
+
date_ranges: [Google::Apis::AnalyticsreportingV4::DateRange.new(
|
29
|
+
start_date: options[:start_date],
|
30
|
+
end_date: options[:end_date]
|
31
|
+
)],
|
32
|
+
# dimensions: [
|
33
|
+
# Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:segment")
|
34
|
+
# # Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:adContent"),
|
35
|
+
# # Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:campaign"),
|
36
|
+
# # Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:keyword"),
|
37
|
+
# # Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:medium"),
|
38
|
+
# # Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:socialNetwork"),
|
39
|
+
# # Google::Apis::AnalyticsreportingV4::Dimension.new(name: "ga:source")
|
40
|
+
# ],
|
41
|
+
metrics: [
|
42
|
+
# Google::Apis::AnalyticsreportingV4::Metric.new(
|
43
|
+
# expression: "ga:bounces"
|
44
|
+
# ),
|
45
|
+
Google::Apis::AnalyticsreportingV4::Metric.new(
|
46
|
+
expression: "ga:newUsers"
|
47
|
+
),
|
48
|
+
Google::Apis::AnalyticsreportingV4::Metric.new(
|
49
|
+
expression: "ga:pageviewsPerSession"
|
50
|
+
),
|
51
|
+
Google::Apis::AnalyticsreportingV4::Metric.new(
|
52
|
+
expression: "ga:sessions"
|
53
|
+
),
|
54
|
+
Google::Apis::AnalyticsreportingV4::Metric.new(
|
55
|
+
expression: "ga:sessionDuration"
|
56
|
+
),
|
57
|
+
# Google::Apis::AnalyticsreportingV4::Metric.new(
|
58
|
+
# expression: "ga:organicSearches"
|
59
|
+
# ),
|
60
|
+
Google::Apis::AnalyticsreportingV4::Metric.new(
|
61
|
+
expression: "ga:users"
|
62
|
+
)
|
63
|
+
],
|
64
|
+
metric_filter_clauses: [
|
65
|
+
Google::Apis::AnalyticsreportingV4::MetricFilterClause.new(
|
66
|
+
filters: [
|
67
|
+
Google::Apis::AnalyticsreportingV4::MetricFilter.new(
|
68
|
+
metric_name: "ga:pageviews",
|
69
|
+
comparison_value: "0",
|
70
|
+
operator: "GREATER_THAN"
|
71
|
+
)
|
72
|
+
]
|
73
|
+
)
|
74
|
+
],
|
75
|
+
include_empty_rows: true
|
76
|
+
)
|
77
|
+
|
78
|
+
# puts "request.to_json: #{JSON.pretty_generate(JSON.parse(request.to_json))}" if options[:debug]
|
79
|
+
get_reports_requests = Google::Apis::AnalyticsreportingV4::GetReportsRequest.new(report_requests: [request])
|
80
|
+
# puts get_reports_requests.to_json if options[:debug]
|
81
|
+
puts JSON.pretty_generate(JSON.parse(get_reports_requests.to_json)) if options[:debug]
|
82
|
+
return nil if options[:dry_run]
|
83
|
+
|
84
|
+
result = service.batch_get_reports(get_reports_requests)
|
85
|
+
puts "result: #{result.inspect}" if options[:debug]
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def csv(result)
|
90
|
+
result = result.to_json if result.is_a?(Google::Apis::AnalyticsreportingV4::GetReportsResponse)
|
91
|
+
result = JSON.parse(result) if result.is_a?(String)
|
92
|
+
CSV.generate do |csv|
|
93
|
+
result["reports"].each do |report|
|
94
|
+
# puts report.column_header.dimensions.inspect
|
95
|
+
# puts report.column_header.metric_header.metric_header_entries.map(&:name).inspect
|
96
|
+
csv << csv_header_row(report)
|
97
|
+
report["data"]["rows"].each do |row|
|
98
|
+
csv << csv_data_row(row["dimensions"], row["metrics"])
|
99
|
+
end
|
100
|
+
csv << csv_data_row("Totals", report["data"]["totals"]) if report["data"]["totals"]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def csv_data_row(row_headers, metrics)
|
106
|
+
(Array(row_headers) || []) + metrics[0]["values"]
|
107
|
+
end
|
108
|
+
|
109
|
+
def csv_header_row(report)
|
110
|
+
(report["columnHeader"]["dimensions"] || []) + report["columnHeader"]["metricHeader"]["metricHeaderEntries"].map do |entry|
|
111
|
+
entry["name"]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def pp(result)
|
116
|
+
JSON.pretty_generate(JSON.parse(result))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def initialize(query_string, options)
|
121
|
+
# puts "query_string: #{query_string}"
|
122
|
+
puts "Initializing query. Options: #{options.inspect}" if options[:debug]
|
123
|
+
|
124
|
+
puts "options[:access_token]: #{options[:access_token]}"
|
125
|
+
@access_token = options[:access_token]
|
126
|
+
puts "options[:credentials]: #{options[:credentials]}"
|
127
|
+
@access_token ||= access_token_from_credentials(options[:credentials])
|
128
|
+
puts "Final access_token: #{access_token}"
|
129
|
+
@dry_run = options[:dry_run]
|
130
|
+
|
131
|
+
query_string = JSON.parse(query_string) unless query_string.is_a?(Hash)
|
132
|
+
@query = {}
|
133
|
+
@query["reportRequests"] = query_string["reportRequests"].map do |report_request|
|
134
|
+
report_request["viewId"] = options[:view_id]
|
135
|
+
report_request["dateRanges"] = [
|
136
|
+
"startDate": options[:start_date],
|
137
|
+
"endDate": options[:end_date]
|
138
|
+
]
|
139
|
+
report_request
|
140
|
+
end
|
141
|
+
# puts "query: #{JSON.pretty_generate(query)}"
|
142
|
+
end
|
143
|
+
|
144
|
+
def execute
|
145
|
+
uri = URI.parse("https://analyticsreporting.googleapis.com/v4/reports:batchGet")
|
146
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
147
|
+
https.use_ssl = true
|
148
|
+
# https.set_debug_output($stdout)
|
149
|
+
request = Net::HTTP::Post.new(uri.request_uri,
|
150
|
+
"Content-Type" => "application/json",
|
151
|
+
"Authorization" => "Bearer #{access_token}")
|
152
|
+
request.body = query.to_json
|
153
|
+
response = https.request(request)
|
154
|
+
response
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
attr_reader :access_token, :dry_run, :query
|
160
|
+
|
161
|
+
def access_token_from_credentials(credential_file_name)
|
162
|
+
stat = File::Stat.new(credential_file_name)
|
163
|
+
if stat.world_readable? || stat.world_writable?
|
164
|
+
raise "#{credential_file_name} must be readable and writable only by you."
|
165
|
+
end
|
166
|
+
authorization = Google::Auth::ServiceAccountCredentials.make_creds(
|
167
|
+
json_key_io: File.open(credential_file_name),
|
168
|
+
scope: "https://www.googleapis.com/auth/analytics.readonly"
|
169
|
+
)
|
170
|
+
token = authorization.fetch_access_token!
|
171
|
+
puts token
|
172
|
+
token["access_token"]
|
173
|
+
end
|
174
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gaapi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0alpha1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Larry Reid
|
8
|
+
- Phil Carrillo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-03-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: google-api-client
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.19'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.19'
|
28
|
+
description: |2
|
29
|
+
Submit queries expressed in JSON to Google Analytics. Can be run
|
30
|
+
from unattended scripts, batch jobs, etc.
|
31
|
+
email: larry.reid@weenhanceit.com
|
32
|
+
executables:
|
33
|
+
- gaapi
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- bin/gaapi
|
38
|
+
- lib/gaapi.rb
|
39
|
+
- lib/gaapi/main.rb
|
40
|
+
- lib/gaapi/query.rb
|
41
|
+
homepage: https://github.com/weenhanceit/gaapi
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">"
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 1.3.1
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.5.2.1
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Query Google Analytics from the command line.
|
65
|
+
test_files: []
|