gaapi 0.0.0alpha1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
5
+ require "gaapi"
6
+
7
+ Main.call
data/lib/gaapi.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # http://www.rubydoc.info/github/google/google-api-ruby-client/
4
+ require "google/apis/analyticsreporting_v4"
5
+ require "googleauth"
6
+ require "gaapi/main.rb"
7
+ require "gaapi/query.rb"
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.
@@ -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: []