active_public_resources 0.0.2

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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +24 -0
  6. data/Guardfile +9 -0
  7. data/LICENSE.txt +18 -0
  8. data/README.md +326 -0
  9. data/Rakefile +7 -0
  10. data/active_public_resources.gemspec +27 -0
  11. data/active_public_resources_config.yml.example +10 -0
  12. data/lib/active_public_resources/base_response_type.rb +11 -0
  13. data/lib/active_public_resources/base_return_type.rb +17 -0
  14. data/lib/active_public_resources/client.rb +22 -0
  15. data/lib/active_public_resources/driver.rb +21 -0
  16. data/lib/active_public_resources/driver_response.rb +15 -0
  17. data/lib/active_public_resources/drivers/khan_academy.rb +143 -0
  18. data/lib/active_public_resources/drivers/quizlet.rb +92 -0
  19. data/lib/active_public_resources/drivers/schooltube.rb +99 -0
  20. data/lib/active_public_resources/drivers/vimeo.rb +181 -0
  21. data/lib/active_public_resources/drivers/youtube.rb +113 -0
  22. data/lib/active_public_resources/request_criteria.rb +51 -0
  23. data/lib/active_public_resources/response_types/exercise.rb +11 -0
  24. data/lib/active_public_resources/response_types/folder.rb +18 -0
  25. data/lib/active_public_resources/response_types/image.rb +11 -0
  26. data/lib/active_public_resources/response_types/quiz.rb +11 -0
  27. data/lib/active_public_resources/response_types/video.rb +12 -0
  28. data/lib/active_public_resources/return_types/file.rb +11 -0
  29. data/lib/active_public_resources/return_types/iframe.rb +11 -0
  30. data/lib/active_public_resources/return_types/image_url.rb +11 -0
  31. data/lib/active_public_resources/return_types/oembed.rb +11 -0
  32. data/lib/active_public_resources/return_types/url.rb +11 -0
  33. data/lib/active_public_resources/version.rb +3 -0
  34. data/lib/active_public_resources.rb +55 -0
  35. data/spec/lib/active_public_resources/client_spec.rb +75 -0
  36. data/spec/lib/active_public_resources/driver_spec.rb +48 -0
  37. data/spec/lib/active_public_resources/drivers/khan_academy_spec.rb +106 -0
  38. data/spec/lib/active_public_resources/drivers/quizlet_spec.rb +50 -0
  39. data/spec/lib/active_public_resources/drivers/schooltube_spec.rb +50 -0
  40. data/spec/lib/active_public_resources/drivers/vimeo_spec.rb +71 -0
  41. data/spec/lib/active_public_resources/drivers/youtube_spec.rb +52 -0
  42. data/spec/lib/active_public_resources/live_client_spec.rb +91 -0
  43. data/spec/lib/active_public_resources/request_criteria_spec.rb +53 -0
  44. data/spec/spec_helper.rb +46 -0
  45. data/spec/vcr/active_public_resources/client/khan_academy_should_traverse_folders.yml +1039 -0
  46. data/spec/vcr/active_public_resources/client/quizlet_should_perform_request.yml +70 -0
  47. data/spec/vcr/active_public_resources/client/shooltube_should_perform_request.yml +815 -0
  48. data/spec/vcr/active_public_resources/client/vimeo_should_perform_request.yml +70 -0
  49. data/spec/vcr/active_public_resources/client/youtube_should_perform_request.yml +212 -0
  50. data/spec/vcr/active_public_resources/drivers/khan_academy_driver_perform_request/should_get_folder_cs/programming.yml +117 -0
  51. data/spec/vcr/active_public_resources/drivers/khan_academy_driver_perform_request/should_get_folder_cs.yml +72 -0
  52. data/spec/vcr/active_public_resources/drivers/khan_academy_driver_perform_request/should_get_folder_science/mcat/society_and_culture/social_structures.yml +578 -0
  53. data/spec/vcr/active_public_resources/drivers/khan_academy_driver_perform_request/should_get_root_folders.yml +122 -0
  54. data/spec/vcr/active_public_resources/drivers/khan_academy_perform_request/should_get_folder_cs/programming.yml +120 -0
  55. data/spec/vcr/active_public_resources/drivers/khan_academy_perform_request/should_get_folder_cs.yml +73 -0
  56. data/spec/vcr/active_public_resources/drivers/khan_academy_perform_request/should_get_folder_science/mcat/society_and_culture/social_structures.yml +577 -0
  57. data/spec/vcr/active_public_resources/drivers/khan_academy_perform_request/should_get_root_folders.yml +119 -0
  58. data/spec/vcr/active_public_resources/drivers/quizlet_driver_perform_request/should_perform_request.yml +75 -0
  59. data/spec/vcr/active_public_resources/drivers/quizlet_perform_request/should_perform_request.yml +72 -0
  60. data/spec/vcr/active_public_resources/drivers/schooltube_driver_perform_request/should_perform_request.yml +815 -0
  61. data/spec/vcr/active_public_resources/drivers/schooltube_perform_request/should_perform_request.yml +810 -0
  62. data/spec/vcr/active_public_resources/drivers/vimeo_driver_perform_request/should_perform_request.yml +279 -0
  63. data/spec/vcr/active_public_resources/drivers/vimeo_perform_request/should_perform_request.yml +70 -0
  64. data/spec/vcr/active_public_resources/drivers/youtube_driver_perform_request/should_perform_request.yml +212 -0
  65. data/spec/vcr/active_public_resources/drivers/youtube_perform_request/should_perform_request.yml +209 -0
  66. metadata +182 -0
@@ -0,0 +1,143 @@
1
+ require 'net/https'
2
+
3
+ module ActivePublicResources
4
+ module Drivers
5
+ class KhanAcademy < Driver
6
+
7
+ BASE_URL = "https://www.khanacademy.org/api/v1/"
8
+
9
+ def perform_request(request_criteria)
10
+ request_criteria.folder ||= 'root'
11
+ results, videos, exercises = get_folder(request_criteria.folder)
12
+ parse_results(request_criteria, results, videos, exercises)
13
+ end
14
+
15
+ private
16
+
17
+ def perform_rest_request(path)
18
+ uri = URI( BASE_URL + path )
19
+ res = Net::HTTP.get_response(uri)
20
+ JSON.parse(res.body)
21
+ end
22
+
23
+ def get_folder(folder_id)
24
+ results = perform_rest_request("topic/#{folder_id}")
25
+ child_data_types = results['child_data'].map { |item| item['kind'] }.uniq
26
+ videos = []
27
+ if child_data_types.include? "Video"
28
+ videos = get_videos(folder_id)
29
+ end
30
+ exercises = []
31
+ if child_data_types.include? "Exercise"
32
+ exercises = get_exercises(folder_id)
33
+ end
34
+ [results, videos, exercises]
35
+ end
36
+
37
+ def get_videos(folder_id)
38
+ perform_rest_request("topic/#{folder_id}/videos")
39
+ end
40
+
41
+ def get_exercises(folder_id)
42
+ perform_rest_request("topic/#{folder_id}/exercises")
43
+ end
44
+
45
+ def parse_results(request_criteria, results, videos=[], exercises=[])
46
+ parent_id = parse_parent_id(results['slug'], results['extended_slug'])
47
+
48
+ driver_response = DriverResponse.new(
49
+ :criteria => request_criteria,
50
+ :next_criteria => nil,
51
+ :total_items => results['children'].length
52
+ )
53
+
54
+ topics = results['children'].find_all { |item| item['kind'] == 'Topic' }
55
+ topics.each do |topic|
56
+ driver_response.items << parse_folder(topic, parent_id)
57
+ end
58
+
59
+ videos.each do |video_data|
60
+ driver_response.items << parse_video(video_data)
61
+ end
62
+
63
+ exercises.each do |exercise_data|
64
+ driver_response.items << parse_exercise(exercise_data)
65
+ end
66
+
67
+ driver_response
68
+ end
69
+
70
+ def parse_folder(data, parent_id)
71
+ folder = ActivePublicResources::ResponseTypes::Folder.new
72
+ folder.id = data['id']
73
+ folder.title = data['title']
74
+ folder.parent_id = parent_id
75
+ folder
76
+ end
77
+
78
+ def parse_video(data)
79
+ video = ActivePublicResources::ResponseTypes::Video.new
80
+ video.id = data['readable_id']
81
+ video.title = data['title']
82
+ video.description = data['description']
83
+ video.thumbnail_url = data['image_url']
84
+ video.url = data['url']
85
+ video.duration = data['duration'] ? data['duration'].to_i : 0
86
+ video.num_views = 0
87
+ video.num_likes = 0
88
+ video.num_comments = 0
89
+ video.created_date = data['date_added'].present? ? Date.parse(data['date_added']) : nil
90
+ video.username = data['author_names'].present? ? data['author_names'].first : ''
91
+ video.width = 640
92
+ video.height = 360
93
+
94
+ # Return Types
95
+ video.return_types << APR::ReturnTypes::Url.new(
96
+ :url => video.url,
97
+ :text => video.title,
98
+ :title => video.title
99
+ )
100
+ video.return_types << APR::ReturnTypes::Iframe.new(
101
+ :url => "//www.youtube.com/embed/#{data['youtube_id']}?feature=oembed",
102
+ :text => video.title,
103
+ :title => video.title,
104
+ :width => 640,
105
+ :height => 360
106
+ )
107
+ video
108
+ end
109
+
110
+ def parse_exercise(data)
111
+ exercise = ActivePublicResources::ResponseTypes::Exercise.new
112
+ exercise.id = data['global_id']
113
+ exercise.title = data['display_name']
114
+ exercise.description = data['description']
115
+ exercise.thumbnail_url = data['image_url_256']
116
+ exercise.url = data['ka_url']
117
+
118
+ exercise.return_types << APR::ReturnTypes::Url.new(
119
+ :url => exercise.url,
120
+ :text => exercise.title,
121
+ :title => exercise.title
122
+ )
123
+
124
+ exercise
125
+ end
126
+
127
+ def parse_parent_id(slug, extended_slug)
128
+ if extended_slug.present?
129
+ if extended_slug != slug
130
+ parts = extended_slug.split("/")
131
+ parts.pop
132
+ parts.last
133
+ else
134
+ 'root'
135
+ end
136
+ else
137
+ nil
138
+ end
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,92 @@
1
+ require 'net/https'
2
+
3
+ module ActivePublicResources
4
+ module Drivers
5
+ class Quizlet < Driver
6
+
7
+ def initialize(config_options={})
8
+ validate_options(config_options, [:client_id])
9
+ @client_id = config_options[:client_id]
10
+ end
11
+
12
+ def perform_request(request_criteria)
13
+ raise StandardError.new("driver has not been initialized properly") unless @client_id.present?
14
+ request_criteria.validate_presence!([:query])
15
+
16
+ uri = URI('https://api.quizlet.com/2.0/search/sets')
17
+ params = {
18
+ 'q' => request_criteria.query,
19
+ 'page' => request_criteria.page || 1,
20
+ 'per_page' => request_criteria.per_page || 25,
21
+ 'client_id' => @client_id
22
+ }
23
+ uri.query = URI.encode_www_form(params)
24
+ res = Net::HTTP.get_response(uri)
25
+ results = JSON.parse(res.body)
26
+
27
+ return parse_results(request_criteria, results)
28
+ end
29
+
30
+ private
31
+
32
+ def parse_results(request_criteria, results)
33
+ @driver_response = DriverResponse.new(
34
+ :criteria => request_criteria,
35
+ :next_criteria => next_criteria(request_criteria, results),
36
+ :total_items => results['total_results'],
37
+ :items => results['sets'].map { |data| parse_quiz(data) }
38
+ )
39
+ end
40
+
41
+ def next_criteria(request_criteria, results)
42
+ if results['total_pages'] > results['page']
43
+ return RequestCriteria.new({
44
+ :query => request_criteria.query,
45
+ :page => (request_criteria.page || 1) + 1,
46
+ :per_page => (request_criteria.per_page || 25)
47
+ })
48
+ end
49
+ end
50
+
51
+ def parse_quiz(data)
52
+ quiz = ActivePublicResources::ResponseTypes::Quiz.new
53
+ quiz.id = data['id']
54
+ quiz.title = data['title']
55
+ quiz.description = data['description']
56
+ quiz.url = data['url']
57
+ quiz.term_count = data['term_count']
58
+ quiz.created_date = Time.at(data['created_date']).utc.to_date
59
+ quiz.has_images = data['has_images']
60
+ quiz.subjects = data['subjects']
61
+
62
+ # Return Types
63
+
64
+ quiz.return_types << APR::ReturnTypes::Url.new(
65
+ :url => quiz.url,
66
+ :text => quiz.title,
67
+ :title => quiz.title
68
+ )
69
+
70
+ # See http://quizlet.com/help/can-i-embed-quizlet-on-my-website
71
+ { flashcards: "Flashcards",
72
+ learn: "Learn",
73
+ scatter: "Scatter",
74
+ speller: "Speller",
75
+ test: "Test",
76
+ spacerace: "Space Race" }.each do |k,v|
77
+
78
+ quiz.return_types << APR::ReturnTypes::Iframe.new(
79
+ :url => "https://quizlet.com/#{data['id']}/#{k}/embedv2",
80
+ :text => v,
81
+ :title => quiz.title,
82
+ :width => "100%",
83
+ :height => 410
84
+ )
85
+ end
86
+
87
+ quiz
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,99 @@
1
+ require 'net/https'
2
+
3
+ module ActivePublicResources
4
+ module Drivers
5
+ class Schooltube < Driver
6
+
7
+ def perform_request(request_criteria)
8
+ request_criteria.validate_presence!([:query])
9
+ uri = URI('http://www.schooltube.com/api/v1/video/search/')
10
+ params = {
11
+ 'term' => request_criteria.query,
12
+ 'orderby' => normalize_request_criteria(request_criteria, 'sort') || '-view_count',
13
+ 'offset' => offset(request_criteria.page, request_criteria.per_page),
14
+ 'limit' => request_criteria.per_page || 25
15
+ }
16
+ uri.query = URI.encode_www_form(params)
17
+ res = Net::HTTP.get_response(uri)
18
+ results = JSON.parse(res.body)
19
+
20
+ return parse_results(request_criteria, results)
21
+ end
22
+
23
+ private
24
+
25
+ def offset(page, per_page)
26
+ p = page || 1
27
+ pp = per_page || 25
28
+ p * pp - pp
29
+ end
30
+
31
+ def normalize_request_criteria(request_criteria, field_name)
32
+ case field_name
33
+ when 'sort'
34
+ case request_criteria.instance_variable_get("@#{field_name}")
35
+ when 'views'
36
+ return '-view_count'
37
+ else
38
+ return '-view_count'
39
+ end
40
+ else
41
+ request_criteria.instance_variable_get("@#{field_name}")
42
+ end
43
+ end
44
+
45
+ def parse_results(request_criteria, results)
46
+ @driver_response = DriverResponse.new(
47
+ :criteria => request_criteria,
48
+ :next_criteria => next_criteria(request_criteria, results),
49
+ :total_items => nil,
50
+ :items => results['objects'].map { |data| parse_video(data) }
51
+ )
52
+ end
53
+
54
+ def next_criteria(request_criteria, results)
55
+ if results['meta']['has_next']
56
+ return RequestCriteria.new({
57
+ :query => request_criteria.query,
58
+ :page => (request_criteria.page || 1) + 1,
59
+ :per_page => results['meta']['limit'].to_i
60
+ })
61
+ end
62
+ end
63
+
64
+ def parse_video(data)
65
+ video = ActivePublicResources::ResponseTypes::Video.new
66
+ video.id = data['vkey']
67
+ video.title = data['title']
68
+ video.description = data['description']
69
+ video.thumbnail_url = data['thumbnail_url']
70
+ video.url = data['short_url']
71
+ video.duration = data['duration'] ? data['duration'].to_i : 0
72
+ video.num_views = data['view_count'] ? data['view_count'].to_i : 0
73
+ video.num_likes = data['thumbs_up'] ? data['thumbs_up'].to_i : 0
74
+ video.num_comments = 0
75
+ video.created_date = Date.parse(data['create_date'])
76
+ video.username = data['username']
77
+ video.width = 640
78
+ video.height = 360
79
+
80
+ # Return Types
81
+ video.return_types << APR::ReturnTypes::Url.new(
82
+ :url => video.url,
83
+ :text => video.title,
84
+ :title => video.title
85
+ )
86
+ video.return_types << APR::ReturnTypes::Iframe.new(
87
+ :url => "//www.schooltube.com/embed/#{data['vkey']}",
88
+ :text => video.title,
89
+ :title => video.title,
90
+ :width => 640,
91
+ :height => 360
92
+ )
93
+
94
+ video
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,181 @@
1
+ require 'vimeo'
2
+
3
+ module ActivePublicResources
4
+ module Drivers
5
+ class Vimeo < Driver
6
+ attr_reader :client
7
+
8
+ DEFAULT_CRITERIA = {
9
+ :page => 1,
10
+ :per_page => 25,
11
+ :sort => 'relevant',
12
+ :content_filter => 'safe'
13
+ }
14
+
15
+ # Constructor
16
+ #
17
+ # @param [Hash] config_options the options which the vimeo gem requires
18
+ # @option config_options [String] :consumer_key Vimeo consumer key (required)
19
+ # @option config_options [String] :consumer_secret Vimeo consumer secret (required)
20
+ # @option config_options [String] :access_token Vimeo access token (required)
21
+ # @option config_options [String] :access_token_secret Vimeo access token secret (required)
22
+ def initialize(config_options={})
23
+ validate_options(config_options,
24
+ [:consumer_key, :consumer_secret, :access_token, :access_token_secret])
25
+
26
+ @client = ::Vimeo::Advanced::Video.new(
27
+ config_options[:consumer_key],
28
+ config_options[:consumer_secret],
29
+ token: config_options[:access_token],
30
+ secret: config_options[:access_token_secret]
31
+ )
32
+ end
33
+
34
+ # Perform search request to Vimeo with search criteria
35
+ #
36
+ # @param [Hash] criteria the criteria used to perform the search
37
+ # @option criteria [String] :query The query text to search for (required)
38
+ # @option criteria [Integer] :page The page number
39
+ # @option criteria [Integer] :per_page The number of items per page
40
+ # @option criteria [String] :sort The sort filter
41
+ # @option criteria [String] :content_filter The content filter of which to apply
42
+ # this is not supported yet - https://developer.vimeo.com/api/docs/spec
43
+ #
44
+ # @example Request
45
+ # driver = ActivePublicResources::Drivers::Vimeo.new({ .. config options .. })
46
+ # results = driver.perform_request({ query: 'education' })
47
+ #
48
+ # @example Returns
49
+ # {
50
+ # 'items': [
51
+ # {
52
+ # 'kind' : 'video',
53
+ # 'id' : '1',
54
+ # 'title' : '',
55
+ # 'description' : '',
56
+ # 'thumbnail_url' : '',
57
+ # 'url' : '',
58
+ # 'duration' : 150,
59
+ # 'num_views' : 13,
60
+ # 'num_likes' : 1,
61
+ # 'num_comments' : 2,
62
+ # 'created_date' : '',
63
+ # 'username' : '',
64
+ # 'embed_html' : '',
65
+ # 'width' : 640,
66
+ # 'height' : 360
67
+ # },
68
+ # ...
69
+ # ],
70
+ # 'meta': {
71
+ # 'nextCriteria': {
72
+ # 'query' : 'education',
73
+ # 'page' : 2
74
+ # 'per_page' : 25
75
+ # 'sort' : 'relevant'
76
+ # 'content_filter' : 'safe'
77
+ # }
78
+ # }
79
+ # }
80
+ #
81
+ # @return [JSON] the normalized response object
82
+ def perform_request(request_criteria)
83
+ request_criteria.validate_presence!([:query])
84
+ raise StandardError.new("driver has not been initialized properly") unless @client
85
+
86
+ results = @client.search(request_criteria.query, {
87
+ :page => request_criteria.page || 1,
88
+ :per_page => request_criteria.per_page || 25,
89
+ :full_response => 1,
90
+ :sort => request_criteria.sort || "relevant",
91
+ :user_id => nil,
92
+ :content_filter => request_criteria.content_filter || 'safe'
93
+ })
94
+
95
+ return parse_results(request_criteria, results)
96
+ end
97
+
98
+ private
99
+
100
+ def sort(val)
101
+ case val
102
+ when APR::RequestCriteria::SORT_RELEVANCE
103
+ 'relevant'
104
+ when APR::RequestCriteria::SORT_RECENT
105
+ 'newest'
106
+ when APR::RequestCriteria::SORT_POPULAR
107
+ 'most_played'
108
+ else
109
+ 'relevant'
110
+ end
111
+ end
112
+
113
+ def content_filter(val)
114
+ case val
115
+ when APR::RequestCriteria::CONTENT_FILTER_NONE
116
+ 'safe'
117
+ when APR::RequestCriteria::CONTENT_FILTER_STRICT
118
+ 'safe'
119
+ else
120
+ 'safe'
121
+ end
122
+ end
123
+
124
+ def parse_results(request_criteria, results)
125
+ @driver_response = DriverResponse.new(
126
+ :criteria => request_criteria,
127
+ :next_criteria => next_criteria(request_criteria, results),
128
+ :total_items => results['videos']['total'].to_i,
129
+ :items => results['videos']['video'].map { |data| parse_video(data) }
130
+ )
131
+ end
132
+
133
+ def next_criteria(request_criteria, results)
134
+ page = results['videos']['page'].to_i
135
+ per_page = results['videos']['perpage'].to_i
136
+ total = results['videos']['total'].to_i
137
+ if ((page * per_page) < total)
138
+ return RequestCriteria.new({
139
+ :query => request_criteria.query,
140
+ :page => page + 1,
141
+ :per_page => per_page
142
+ })
143
+ end
144
+ end
145
+
146
+ def parse_video(data)
147
+ video = ActivePublicResources::ResponseTypes::Video.new
148
+ video.id = data['id']
149
+ video.title = data['title']
150
+ video.description = data['description']
151
+ video.thumbnail_url = data['thumbnails']['thumbnail'][0]['_content']
152
+ video.url = data['urls']['url'][0]['_content']
153
+ video.duration = data['duration'].to_i
154
+ video.num_views = data['number_of_plays'].to_i
155
+ video.num_likes = data['number_of_likes'].to_i
156
+ video.num_comments = data['number_of_comments'].to_i
157
+ video.created_date = Date.parse(data['upload_date'])
158
+ video.username = data['owner']['display_name']
159
+ video.width = 640
160
+ video.height = 360
161
+
162
+ # Return Types
163
+ video.return_types << APR::ReturnTypes::Url.new(
164
+ :url => video.url,
165
+ :text => video.title,
166
+ :title => video.title
167
+ )
168
+ video.return_types << APR::ReturnTypes::Iframe.new(
169
+ :url => "//player.vimeo.com/video/#{data['id']}",
170
+ :text => video.title,
171
+ :title => video.title,
172
+ :width => 640,
173
+ :height => 360
174
+ )
175
+
176
+ video
177
+ end
178
+
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,113 @@
1
+ require 'net/https'
2
+
3
+ module ActivePublicResources
4
+ module Drivers
5
+ class Youtube < Driver
6
+
7
+ def perform_request(request_criteria)
8
+ request_criteria.validate_presence!([:query])
9
+
10
+ uri = URI('https://gdata.youtube.com/feeds/api/videos')
11
+ params = {
12
+ 'v' => 2,
13
+ 'q' => request_criteria.query,
14
+ 'orderby' => sort(request_criteria.sort),
15
+ 'alt' => 'json',
16
+ 'safeSearch' => content_filter(request_criteria.content_filter),
17
+ 'start-index' => request_criteria.page || 1,
18
+ 'max-results' => request_criteria.per_page || 25
19
+ }
20
+
21
+ uri.query = URI.encode_www_form(params)
22
+
23
+ res = Net::HTTP.get_response(uri)
24
+ results = JSON.parse(res.body)
25
+
26
+ return parse_results(request_criteria, results)
27
+ end
28
+
29
+ private
30
+
31
+ def sort(val)
32
+ case val
33
+ when APR::RequestCriteria::SORT_RELEVANCE
34
+ 'relevance'
35
+ when APR::RequestCriteria::SORT_RECENT
36
+ 'relevance'
37
+ when APR::RequestCriteria::SORT_POPULAR
38
+ 'relevance'
39
+ else
40
+ 'relevance'
41
+ end
42
+ end
43
+
44
+ def content_filter(val)
45
+ case val
46
+ when APR::RequestCriteria::CONTENT_FILTER_NONE
47
+ 'strict'
48
+ when APR::RequestCriteria::CONTENT_FILTER_STRICT
49
+ 'strict'
50
+ else
51
+ 'strict'
52
+ end
53
+ end
54
+
55
+ def parse_results(request_criteria, results)
56
+ @driver_response = DriverResponse.new(
57
+ :criteria => request_criteria,
58
+ :next_criteria => next_criteria(request_criteria, results),
59
+ :total_items => results['feed']['openSearch$totalResults']['$t'].to_i,
60
+ :items => results['feed']['entry'].map { |data| parse_video(data) }
61
+ )
62
+ end
63
+
64
+ def next_criteria(request_criteria, results)
65
+ page = results['feed']['openSearch$startIndex']['$t'].to_i
66
+ per_page = results['feed']['openSearch$itemsPerPage']['$t'].to_i
67
+ total = results['feed']['openSearch$totalResults']['$t'].to_i
68
+ if ((page * per_page) < total)
69
+ return RequestCriteria.new({
70
+ :query => request_criteria.query,
71
+ :page => page + 1,
72
+ :per_page => per_page
73
+ })
74
+ end
75
+ end
76
+
77
+ def parse_video(data)
78
+ video_id = data['id']['$t'].split(':').last
79
+ video = APR::ResponseTypes::Video.new
80
+ video.id = video_id
81
+ video.title = data['title']['$t']
82
+ video.description = data['media$group']['media$description']['$t']
83
+ video.thumbnail_url = data['media$group']['media$thumbnail'][0]['url']
84
+ video.url = data['media$group']['media$player']['url']
85
+ video.duration = data['media$group']['yt$duration']['seconds'].to_i
86
+ video.num_views = data['yt$statistics'] ? data['yt$statistics']['viewCount'].to_i : 0
87
+ video.num_likes = data['yt$rating'] ? data['yt$rating']['numLikes'].to_i : 0
88
+ video.num_comments = data['gd$comments'] ? data['gd$comments']['gd$feedLink']['countHint'] : 0
89
+ video.created_date = Date.parse(data['published']['$t'])
90
+ video.username = data['author'][0]['name']['$t']
91
+ video.width = 640
92
+ video.height = 360
93
+
94
+ # Return Types
95
+ video.return_types << APR::ReturnTypes::Url.new(
96
+ :url => video.url,
97
+ :text => video.title,
98
+ :title => video.title
99
+ )
100
+ video.return_types << APR::ReturnTypes::Iframe.new(
101
+ :url => "//www.youtube.com/embed/#{video_id}?feature=oembed",
102
+ :text => video.title,
103
+ :title => video.title,
104
+ :width => 640,
105
+ :height => 360
106
+ )
107
+
108
+ video
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,51 @@
1
+ module ActivePublicResources
2
+ class RequestCriteria
3
+
4
+ SORTS = [
5
+ SORT_RELEVANCE = 'relevance',
6
+ SORT_RECENT = 'recent',
7
+ SORT_POPULAR = 'popular'
8
+ ]
9
+
10
+ CONTENT_FILTERS = [
11
+ CONTENT_FILTER_NONE = 'none',
12
+ CONTENT_FILTER_STRICT = 'strict'
13
+ ]
14
+
15
+ attr_accessor :query, :page, :per_page, :content_filter, :sort, :folder
16
+
17
+ def initialize(args={})
18
+ args.each do |k,v|
19
+ if k.to_s == 'sort' && !SORTS.include?(v)
20
+ raise ArgumentError.new("sort is invalid. Must be in [#{SORTS.join(', ')}]")
21
+ end
22
+ if k.to_s == 'content_filter' && !CONTENT_FILTERS.include?(v)
23
+ raise ArgumentError.new("content_filter is invalid. Must be in [#{CONTENT_FILTERS.join(', ')}]")
24
+ end
25
+ instance_variable_set("@#{k}", v) unless v.nil?
26
+ end
27
+ end
28
+
29
+ def sort=(val)
30
+ unless SORTS.include?(val)
31
+ raise ArgumentError.new("is invalid. Must be in [#{SORTS.join(', ')}]")
32
+ end
33
+ @sort = val
34
+ end
35
+
36
+ def content_filter=(val)
37
+ unless CONTENT_FILTERS.include?(val)
38
+ raise ArgumentError.new("is invalid. Must be in [#{CONTENT_FILTERS.join(', ')}]")
39
+ end
40
+ @content_filter = val
41
+ end
42
+
43
+ def validate_presence!(attr_names)
44
+ attr_names.each do |k|
45
+ if instance_variable_get("@#{k}").blank?
46
+ raise ArgumentError.new("must include #{attr_names.join(', ')}")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ module ActivePublicResources
2
+ module ResponseTypes
3
+ class Exercise < BaseResponseType
4
+ attr_accessor :thumbnail_url, :url
5
+
6
+ def kind
7
+ 'exercise'
8
+ end
9
+ end
10
+ end
11
+ end