brainzz 0.0.2 → 0.0.3

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
  SHA1:
3
- metadata.gz: 5593938f8cecb4660e3c474a61dcbf908a74f0a2
4
- data.tar.gz: 2db3a9a659c9241a7501870fb212a57f73accfa7
3
+ metadata.gz: 8b0778b1b4125407b03db2409943f825a0175ed0
4
+ data.tar.gz: a380360a2548603619f847f20cad660367ba70e7
5
5
  SHA512:
6
- metadata.gz: 2387071a7d82e12d58798ccade04c873531aa77335fc1931c4a61f2ec688c3e30b290567ed02805e0f40ad323adeea368edc64e4eda774a3daa81d6bb7df8111
7
- data.tar.gz: 5821bb60c0a98d8c8f04319450d4ac282c609800352781518d776cf4415bc6525facb147d2a6014cc57807dad56b792ea4b2992e4af84cf19dc7706f109544d1
6
+ metadata.gz: 65e2ecf248cbaf6c936f4e26f3fc71a6e6948776dbf4abcfa747489c46a530a776b0f570ab64fe520f4b89edf122159aee295460f3db14e1786f2a612aac8af6
7
+ data.tar.gz: be9d7f05e156220dfa77132b0c688c0daecef19fda6f4abf2298db63f14bed1023ee5fa89f76b34439a23b2a42c00a8c5e1a8ab78182291f56dc60fe01e18b4a
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Brainzz
2
2
  =======
3
3
 
4
+ [![Build Status](https://semaphoreci.com/api/v1/projects/6bcf66fa-7d8c-4027-93ae-13b6bb75325f/346455/badge.png)](https://semaphoreci.com/awesomenesstv/brainzz)
5
+
4
6
  Brainzz explains why youtoobers' brainzz are turned to mush.
5
7
  ... by extracting data from the YouTube API.
6
8
 
@@ -19,24 +21,22 @@ Run Specs
19
21
 
20
22
  ### Regenerating VCR Cassettes
21
23
 
22
- In order to regenerate vcr cassettes,
23
- the google api key is needed.
24
-
25
- Follow the instructions [here][atv-wiki-google-api-key] to retrieve it.
24
+ Check `config/dotenv/test.env` for the required keys.
26
25
 
27
- Place this value in a file named `.env` in the root of the project
26
+ Follow the instructions [here][atv-wiki-google-api-key]
27
+ to find the required values.
28
28
 
29
- The `.env` contents should look something like this:
29
+ `REFRESH_TOKEN` is only required for testing purposes.
30
+ Consumers will not need to set this value,
31
+ as it will be passed in via parameters to methods that require it.
30
32
 
31
- ```
32
- GOOGLE_API_KEY = [API KEY]
33
- ```
33
+ Place them in `config/dotenv/integration.env`.
34
34
 
35
35
 
36
36
  Usage
37
37
  -----
38
38
 
39
- A `GOOGLE_API_KEY` environment variable is expected to be set
39
+ A `BRAINZZ_GOOGLE_API_KEY` environment variable is expected to be set
40
40
  by any application that consumes this gem.
41
41
 
42
42
 
@@ -72,4 +72,70 @@ while response.nil? || !response.data.last_page?
72
72
  end
73
73
  ```
74
74
 
75
+
76
+ ### video_details_for
77
+
78
+ Accepts an array of video ids.
79
+ The maximum length of the array is determined by YouTube.
80
+ It is currently limited to 50 videos.
81
+
82
+ This endpoint returns a hash with video ids as keys
83
+ and video objects as the values.
84
+
85
+
86
+ ### views_for
87
+
88
+ #### Parameters
89
+
90
+ * channel id (String)
91
+ * video id (String)
92
+ * start date (DateTime)
93
+ * end date (DateTime)
94
+ * refresh token (String)
95
+
96
+ Refresh token must be a refresh token for the channel owner
97
+ using the same client id and client secret that are set in
98
+ `BRAINZZ_CLIENT_ID` and `BRAINZZ_CLIENT_SECRET`.
99
+
100
+
101
+ #### Result
102
+
103
+ An array of `Brainzz::ViewCount` objects ordered by date in ascending order.
104
+
105
+ `#source` will be an integer (or nil) value
106
+ corresponding to the following YouTube referrals:
107
+
108
+ ADVERTISING = 1
109
+ ANNOTATION = 2
110
+ EXT_URL = 3
111
+ NO_LINK_EMBEDDED = 4
112
+ NO_LINK_OTHER = 5
113
+ PLAYLIST = 6
114
+ PROMOTED = 7
115
+ RELATED_VIDEO = 8
116
+ SUBSCRIBER = 9
117
+ YT_CHANNEL = 10
118
+ YT_OTHER_PAGE = 11
119
+ YT_SEARCH = 12
120
+
121
+ The constants are defined in `Brainzz::ViewCountEnum`
122
+
123
+
124
+ Factories
125
+ ---------
126
+
127
+ Brainzz includes FactoryGirl factories for your convenience.
128
+ Include them after requiring FactoryGirl:
129
+
130
+ require 'brainzz/factories'
131
+
132
+
133
+ Deployment
134
+ ----------
135
+
136
+ This project makes use of branches to manage deployment.
137
+ Pushing a new commit to the `production` branch
138
+ will also build and push this gem to RubyGems.
139
+
140
+
75
141
  [atv-wiki-google-api-key]: https://www.github.com/awesomenesstv/wiki#google-api-key
data/lib/brainzz.rb CHANGED
@@ -4,23 +4,36 @@ require 'date'
4
4
  require 'faraday'
5
5
 
6
6
  require 'reverb'
7
+ require 'toke'
7
8
 
8
- module Brainzz
9
- end
10
-
11
- require_relative 'brainzz/core'
9
+ require_relative 'brainzz/enums/view_source_enum'
12
10
 
13
11
  require_relative 'brainzz/models/base_model'
14
12
  require_relative 'brainzz/models/video'
13
+ require_relative 'brainzz/models/view_count'
15
14
  require_relative 'brainzz/models/playlist_item'
16
15
  require_relative 'brainzz/models/playlist_items_wrapper'
17
16
 
17
+ require_relative 'brainzz/services/access_token_service'
18
+
19
+ require_relative 'brainzz/params/base_params'
20
+ require_relative 'brainzz/params/analytics_params'
21
+ require_relative 'brainzz/params/video_details_params'
22
+ require_relative 'brainzz/params/view_count_params'
23
+ require_relative 'brainzz/params/playlist_items_params'
24
+
18
25
  require_relative 'brainzz/responses/playlist_items_response'
19
26
  require_relative 'brainzz/responses/video_details_response'
27
+ require_relative 'brainzz/responses/view_count_response'
20
28
 
21
29
  require_relative 'brainzz/commands/base_command'
30
+ require_relative 'brainzz/commands/data_command'
31
+ require_relative 'brainzz/commands/analytics_command'
22
32
  require_relative 'brainzz/commands/playlist_items_command'
23
33
  require_relative 'brainzz/commands/video_details_command'
34
+ require_relative 'brainzz/commands/view_count_command'
35
+
36
+ require_relative 'brainzz/core'
24
37
 
25
38
  module Brainzz
26
39
  extend Core
@@ -0,0 +1,21 @@
1
+ module Brainzz
2
+ class AnalyticsCommand < BaseCommand
3
+ class << self
4
+ private
5
+
6
+ def headers(parameters)
7
+ super.merge({
8
+ 'Authorization' => "Bearer #{access_token(parameters)}",
9
+ })
10
+ end
11
+
12
+ def base_url
13
+ 'https://www.googleapis.com/youtube/analytics/v1'
14
+ end
15
+
16
+ def access_token(parameters)
17
+ AccessTokenService.retrieve_token parameters.refresh_token
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,29 +1,29 @@
1
1
  module Brainzz
2
- module BaseCommand
3
- extend self
2
+ class BaseCommand
3
+ class << self
4
+ private
4
5
 
5
- private
6
+ def get(parameters)
7
+ connection.get endpoint, params(parameters), headers(parameters)
8
+ end
6
9
 
7
- def connection
8
- Faraday.new(:url => base_url)
9
- end
10
+ def connection
11
+ Faraday.new(:url => base_url)
12
+ end
10
13
 
11
- def params
12
- url_params.merge({
13
- :key => api_key,
14
- })
15
- end
14
+ def params(parameters = nil)
15
+ {}
16
+ end
16
17
 
17
- def url_params
18
- {}
19
- end
20
-
21
- def api_key
22
- ENV['GOOGLE_API_KEY']
23
- end
18
+ def headers(parameters = nil)
19
+ {
20
+ 'Content-Type' => 'application/json',
21
+ }
22
+ end
24
23
 
25
- def base_url
26
- 'https://www.googleapis.com/youtube/v3'
24
+ def url(parameters)
25
+ connection.build_url endpoint, params(parameters)
26
+ end
27
27
  end
28
28
  end
29
29
  end
@@ -0,0 +1,19 @@
1
+ module Brainzz
2
+ class DataCommand < BaseCommand
3
+ class << self
4
+ def params(parameters = nil)
5
+ super.merge({
6
+ :key => api_key,
7
+ })
8
+ end
9
+
10
+ def base_url
11
+ 'https://www.googleapis.com/youtube/v3'
12
+ end
13
+
14
+ def api_key
15
+ ENV['BRAINZZ_GOOGLE_API_KEY']
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,28 +1,37 @@
1
1
  module Brainzz
2
- module PlaylistItemsCommand
3
- extend BaseCommand
4
- extend self
2
+ class PlaylistItemsCommand < DataCommand
3
+ class << self
4
+ def execute(playlist_id, response)
5
+ playlist_items_params = PlaylistItemsParams.new(playlist_id, response)
5
6
 
6
- def execute(playlist_id, response)
7
- @response = response
8
- @playlist_id = playlist_id
9
- response = connection.get('playlistItems', params)
10
- PlaylistItemsResponse.new response
11
- end
7
+ if playlist_items_params.valid?
8
+ response = get(playlist_items_params)
9
+ PlaylistItemsResponse.new response
10
+ else
11
+ PlaylistItemsResponse.new
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def endpoint
18
+ 'playlistItems'
19
+ end
12
20
 
13
- private
21
+ def params(playlist_items_params)
22
+ args = {
23
+ 'playlistId' => playlist_items_params.playlist_id,
24
+ 'part' => 'contentDetails',
25
+ 'maxResults' => 50,
26
+ }
14
27
 
15
- def url_params
16
- params = {
17
- 'playlistId' => @playlist_id,
18
- 'part' => 'contentDetails',
19
- 'maxResults' => 50,
20
- }
28
+ if playlist_items_params.next_page_token
29
+ args.merge!({
30
+ 'pageToken' => playlist_items_params.next_page_token
31
+ })
32
+ end
21
33
 
22
- if @response
23
- params.merge({'pageToken' => @response.data.next_page_token})
24
- else
25
- params
34
+ super.merge args
26
35
  end
27
36
  end
28
37
  end
@@ -1,21 +1,29 @@
1
1
  module Brainzz
2
- module VideoDetailsCommand
3
- extend BaseCommand
4
- extend self
2
+ class VideoDetailsCommand < DataCommand
3
+ class << self
4
+ def execute(video_ids)
5
+ video_details_params = VideoDetailsParams.new(video_ids)
5
6
 
6
- def execute(video_ids)
7
- @video_ids = video_ids
8
- response = connection.get('videos', params)
9
- VideoDetailsResponse.new response
10
- end
7
+ if video_details_params.valid?
8
+ response = get(video_details_params)
9
+ VideoDetailsResponse.new response
10
+ else
11
+ VideoDetailsResponse.new
12
+ end
13
+ end
14
+
15
+ private
11
16
 
12
- private
17
+ def endpoint
18
+ 'videos'
19
+ end
13
20
 
14
- def url_params
15
- {
16
- 'id' => @video_ids.join(','),
17
- 'part' => 'snippet',
18
- }
21
+ def params(video_details_params)
22
+ super.merge({
23
+ 'id' => video_details_params.video_ids.join(','),
24
+ 'part' => 'snippet',
25
+ })
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -0,0 +1,39 @@
1
+ module Brainzz
2
+ class ViewCountCommand < AnalyticsCommand
3
+ class << self
4
+ def execute(channel_id, video_id, start_date, end_date, refresh_token)
5
+ view_count_params = ViewCountParams.new({
6
+ :channel_id => channel_id,
7
+ :video_id => video_id,
8
+ :start_date => start_date,
9
+ :end_date => end_date,
10
+ :refresh_token => refresh_token
11
+ })
12
+
13
+ if view_count_params.valid?
14
+ ViewCountResponse.new get(view_count_params)
15
+ else
16
+ ViewCountResponse.new
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def endpoint
23
+ 'reports'
24
+ end
25
+
26
+ def params(view_count_params)
27
+ super.merge({
28
+ 'ids' => "channel==#{view_count_params.channel_id}",
29
+ 'filter' => "video==#{view_count_params.video_id}",
30
+ 'metrics' => 'views',
31
+ 'dimensions' => 'day,insightTrafficSourceType',
32
+ 'start-date' => view_count_params.start_date,
33
+ 'end-date' => view_count_params.end_date,
34
+ 'sort' => 'day',
35
+ })
36
+ end
37
+ end
38
+ end
39
+ end
data/lib/brainzz/core.rb CHANGED
@@ -7,7 +7,12 @@ module Brainzz
7
7
  end
8
8
 
9
9
  def video_details_for(video_ids)
10
- VideoDetailsCommand.execute(video_ids)
10
+ VideoDetailsCommand.execute video_ids
11
+ end
12
+
13
+ def views_for(channel_id, video_id, start_date, end_date, refresh_token)
14
+ ViewCountCommand.execute(
15
+ channel_id, video_id, start_date, end_date, refresh_token)
11
16
  end
12
17
  end
13
18
  end
@@ -0,0 +1,31 @@
1
+ module Brainzz
2
+ module ViewSourceEnum
3
+ ADVERTISING = 1
4
+ ANNOTATION = 2
5
+ EXT_URL = 3
6
+ NO_LINK_EMBEDDED = 4
7
+ NO_LINK_OTHER = 5
8
+ PLAYLIST = 6
9
+ PROMOTED = 7
10
+ RELATED_VIDEO = 8
11
+ SUBSCRIBER = 9
12
+ YT_CHANNEL = 10
13
+ YT_OTHER_PAGE = 11
14
+ YT_SEARCH = 12
15
+
16
+ SOURCES = {
17
+ 'advertising' => ADVERTISING,
18
+ 'annotation' => ANNOTATION,
19
+ 'ext_url' => EXT_URL,
20
+ 'no_link_embedded' => NO_LINK_EMBEDDED,
21
+ 'no_link_other' => NO_LINK_OTHER,
22
+ 'playlist' => PLAYLIST,
23
+ 'promoted' => PROMOTED,
24
+ 'related_video' => RELATED_VIDEO,
25
+ 'subscriber' => SUBSCRIBER,
26
+ 'yt_channel' => YT_CHANNEL,
27
+ 'yt_other_page' => YT_OTHER_PAGE,
28
+ 'yt_search' => YT_SEARCH,
29
+ }
30
+ end
31
+ end
@@ -19,7 +19,7 @@ module Brainzz
19
19
  def transform_date(value)
20
20
  value = transform(value)
21
21
  return nil unless value.is_a?(String)
22
- DateTime.parse(value)
22
+ DateTime.parse value
23
23
  end
24
24
  end
25
25
  end
@@ -0,0 +1,22 @@
1
+ module Brainzz
2
+ class ViewCount < BaseModel
3
+ attr_reader :views, :day, :source
4
+
5
+ def initialize(view_count_row)
6
+ @views = normalize_views(view_count_row[2])
7
+ @day = transform_date(view_count_row[0])
8
+ @source = source_value(view_count_row[1])
9
+ end
10
+
11
+ private
12
+
13
+ def normalize_views(views)
14
+ return views ? views.to_i : 0
15
+ end
16
+
17
+ def source_value(source)
18
+ source = '' unless source.is_a?(String)
19
+ Brainzz::ViewSourceEnum::SOURCES[source.downcase]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Brainzz
2
+ class AnalyticsParams < BaseParams
3
+ attr_reader :channel_id
4
+ attr_reader :start_date
5
+ attr_reader :end_date
6
+ attr_reader :refresh_token
7
+
8
+ def initialize(params)
9
+ @channel_id = normalize(params[:channel_id])
10
+ @start_date = normalize_date(params[:start_date])
11
+ @end_date = normalize_date(params[:end_date])
12
+ @refresh_token = normalize(params[:refresh_token])
13
+ end
14
+
15
+ def valid?
16
+ !!(channel_id && start_date && end_date && refresh_token)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ module Brainzz
2
+ class BaseParams
3
+ private
4
+
5
+ def normalize(value)
6
+ value = nil if value && value.strip == ''
7
+ value
8
+ end
9
+
10
+ def normalize_date(date)
11
+ date.strftime('%F') if date.respond_to?(:strftime)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module Brainzz
2
+ class PlaylistItemsParams < BaseParams
3
+ attr_reader :playlist_id, :next_page_token
4
+
5
+ def initialize(playlist_id, response)
6
+ @playlist_id = normalize(playlist_id)
7
+ @next_page_token = page_token_for(response)
8
+ end
9
+
10
+ def valid?
11
+ !!playlist_id
12
+ end
13
+
14
+ private
15
+
16
+ def page_token_for(response)
17
+ response && response.data && response.data.next_page_token
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ module Brainzz
2
+ class VideoDetailsParams
3
+ attr_reader :video_ids
4
+
5
+ def initialize(video_ids)
6
+ @video_ids = video_ids
7
+ end
8
+
9
+ def valid?
10
+ !!(@video_ids && !@video_ids.empty?)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Brainzz
2
+ class ViewCountParams < AnalyticsParams
3
+ attr_reader :video_id
4
+
5
+ def initialize(params)
6
+ super
7
+ @video_id = normalize(params[:video_id])
8
+ end
9
+
10
+ def valid?
11
+ !!(super && video_id)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Brainzz
2
+ class ViewCountResponse < Reverb::Response
3
+ def on_success
4
+ self.data = []
5
+ body['rows'].each do |row|
6
+ self.data << ViewCount.new(row)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ module Brainzz
2
+ module AccessTokenService
3
+ extend self
4
+
5
+ @@mutex = Mutex.new
6
+
7
+ def retrieve_token(refresh_token)
8
+ @@mutex.synchronize do
9
+ if !defined?(@token) or @token.expired?
10
+ response = Toke.retrieve_token(params(refresh_token))
11
+ @token = response.data if response.success?
12
+ end
13
+
14
+ @token.token if defined?(@token)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def params(refresh_token)
21
+ {
22
+ :refresh_token => refresh_token,
23
+ :client_id => ENV['BRAINZZ_CLIENT_ID'],
24
+ :client_secret => ENV['BRAINZZ_CLIENT_SECRET'],
25
+ }
26
+ end
27
+ end
28
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainzz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis Herrick
8
- - Meagan Cooney
8
+ - Joshua Book
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-24 00:00:00.000000000 Z
12
+ date: 2015-03-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: toke
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: gems
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -163,10 +177,14 @@ extra_rdoc_files:
163
177
  files:
164
178
  - README.md
165
179
  - lib/brainzz.rb
180
+ - lib/brainzz/commands/analytics_command.rb
166
181
  - lib/brainzz/commands/base_command.rb
182
+ - lib/brainzz/commands/data_command.rb
167
183
  - lib/brainzz/commands/playlist_items_command.rb
168
184
  - lib/brainzz/commands/video_details_command.rb
185
+ - lib/brainzz/commands/view_count_command.rb
169
186
  - lib/brainzz/core.rb
187
+ - lib/brainzz/enums/view_source_enum.rb
170
188
  - lib/brainzz/factories.rb
171
189
  - lib/brainzz/factories/playlist_item.rb
172
190
  - lib/brainzz/factories/playlist_items_wrapper.rb
@@ -176,8 +194,16 @@ files:
176
194
  - lib/brainzz/models/playlist_item.rb
177
195
  - lib/brainzz/models/playlist_items_wrapper.rb
178
196
  - lib/brainzz/models/video.rb
197
+ - lib/brainzz/models/view_count.rb
198
+ - lib/brainzz/params/analytics_params.rb
199
+ - lib/brainzz/params/base_params.rb
200
+ - lib/brainzz/params/playlist_items_params.rb
201
+ - lib/brainzz/params/video_details_params.rb
202
+ - lib/brainzz/params/view_count_params.rb
179
203
  - lib/brainzz/responses/playlist_items_response.rb
180
204
  - lib/brainzz/responses/video_details_response.rb
205
+ - lib/brainzz/responses/view_count_response.rb
206
+ - lib/brainzz/services/access_token_service.rb
181
207
  - license/gplv3.md
182
208
  - license/lgplv3.md
183
209
  - license/lgplv3.png
@@ -201,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
227
  version: '0'
202
228
  requirements: []
203
229
  rubyforge_project:
204
- rubygems_version: 2.4.5
230
+ rubygems_version: 2.4.6
205
231
  signing_key:
206
232
  specification_version: 4
207
233
  summary: YouTube API interface