activity_mapper 0.1.0

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 (41) hide show
  1. data/README.textile +211 -0
  2. data/lib/activity_mapper.rb +35 -0
  3. data/lib/activity_mapper/activity_data_mapper.rb +93 -0
  4. data/lib/activity_mapper/connector.rb +71 -0
  5. data/lib/activity_mapper/lexicon.txt +92662 -0
  6. data/lib/activity_mapper/linguistics.rb +147 -0
  7. data/lib/activity_mapper/service_module.rb +194 -0
  8. data/lib/activity_mapper/service_modules.rb +4 -0
  9. data/lib/activity_mapper/service_modules/delicious.rb +41 -0
  10. data/lib/activity_mapper/service_modules/flickr.rb +59 -0
  11. data/lib/activity_mapper/service_modules/twitter.rb +57 -0
  12. data/lib/activity_mapper/service_modules/wakoopa.rb +350 -0
  13. data/lib/activity_mapper/service_modules/youtube.rb +63 -0
  14. data/lib/extensions/uri.rb +42 -0
  15. data/spec/connector_mock.rb +49 -0
  16. data/spec/data/delicious_ac8fdf9b4e304b150bf198b42a1cb1b4.json +1 -0
  17. data/spec/data/delicious_ee55656e0f69242ccf02c3eb0f97b296.json +1 -0
  18. data/spec/data/flickr_2b826afe2906894197d92f7a41c2785c. +1 -0
  19. data/spec/data/flickr_3c25cf51d174ee4bb2d8673e294ce4c0.json +196 -0
  20. data/spec/data/flickr_482967b550afd05993ec4256fa1de388.json +229 -0
  21. data/spec/data/flickr_54c4b36bea4e2b14c783e5c50cba8544.json +9 -0
  22. data/spec/data/flickr_848ea91c1a903d0347d1029bf863132a. +1 -0
  23. data/spec/data/flickr_bcb82142f3d6998cabab3c82fba15ced.json +141 -0
  24. data/spec/data/flickr_e9d78058938c7c845a8de3a2c3526c61. +1 -0
  25. data/spec/data/twitter_227e1ca72a2ba08a1b03be2cd64b681e.json +274 -0
  26. data/spec/data/twitter_5dd0884137bf193d55d446619ec65c7e.json +1 -0
  27. data/spec/data/twitter_dd694fe9ebda58a84f92a859fb1cb79c.json +1 -0
  28. data/spec/data/wakoopa_1a8b94baf4eed4fd0e7ac39dc2f53059.json +1 -0
  29. data/spec/data/youtube_050e71a038359cc8ff7e00161e687b4e.json +1 -0
  30. data/spec/data/youtube_6d895fb0245f47b9e4fcc868d1f91674.json +1 -0
  31. data/spec/data/youtube_a92042581e44f9d3da548e2c7a89849c.json +1 -0
  32. data/spec/data/youtube_bf7b648ff8720edb1db951b7b9a474c4.json +1 -0
  33. data/spec/data/zemanta_suggest_for_tweet_response.xml +405 -0
  34. data/spec/models.rb +114 -0
  35. data/spec/service_modules/delicious_spec.rb +32 -0
  36. data/spec/service_modules/flickr_spec.rb +43 -0
  37. data/spec/service_modules/twitter_spec.rb +44 -0
  38. data/spec/service_modules/wakoopa_spec.rb +35 -0
  39. data/spec/service_modules/youtube_spec.rb +47 -0
  40. data/spec/spec_helper.rb +8 -0
  41. metadata +130 -0
@@ -0,0 +1,57 @@
1
+
2
+ module ActivityMapper
3
+
4
+ class TwitterServiceModule < ServiceModule
5
+ ACTIVITY_MAP = {
6
+ nil => {
7
+ 'activity.occurred_at' => 'created_at',
8
+ 'activity.native_id' => 'id',
9
+ 'activity_object.native_id' => 'id',
10
+ 'activity.caption' => 'text',
11
+ 'activity_object.title' => 'text',
12
+ 'activity_object.body' => 'text'
13
+ }
14
+ }
15
+ ACCEPTED_HOSTS = [/twitter\.com/]
16
+
17
+ # TODO use this data:
18
+ # user.name
19
+ # user.description
20
+ # user.location
21
+ # user.url
22
+ def create_or_update_summary!(options = {})
23
+ attributes = {}
24
+ attributes[:username] = @profile.username || self.class.username_from_url(@profile.url)
25
+
26
+ mapper = ActivityDataMapper.new(ACTIVITY_MAP)
27
+ mapper.fetch!("http://twitter.com/statuses/user_timeline/#{attributes[:username]}.json", :format => :json)
28
+ tweets = mapper.data
29
+
30
+ attributes[:avatar_url] = tweets.blank? ? nil : tweets.first['user']['profile_image_url']
31
+ attributes[:native_user_id] = tweets.first['user']['id'].to_i
32
+ @profile.update_attributes(attributes)
33
+ end
34
+
35
+ # TODO use this data:
36
+ # tweet.favorited
37
+ # tweet.source # = what application was used to tweet (Wakoopa?)
38
+ # (tweet.in_reply_to_screen_name)
39
+ # (tweet.in_reply_to_status_id)
40
+ def aggregate_activity!(options = {})
41
+ mapper = ActivityDataMapper.new(ACTIVITY_MAP)
42
+ mapper.fetch!("http://twitter.com/statuses/user_timeline/#{@profile.username}.json", :format => :json)
43
+ mapper.map!
44
+ mapper.entries.sort! { |e2,e1|
45
+ e1['activity.occurred_at'] <=> e2['activity.occurred_at']
46
+ }
47
+ mapper.entries.each do |entry|
48
+ entry['activity_object.url'] = entry['activity.url'] = "http://twitter.com/#{@profile.username}/status/#{entry['activity.native_id']}"
49
+ break if Activity.exists?(@profile.user_id, entry)
50
+ create_activity(entry, ActivityObjectType::STATUS, ActivityVerb::POST) do |activity, activity_object|
51
+ end
52
+
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,350 @@
1
+
2
+ module ActivityMapper
3
+
4
+ class WakoopaServiceModule < ServiceModule
5
+ ACTIVITY_MAP = {
6
+ nil => {
7
+ 'activity.occurred_at' => 'software/last_active_at',
8
+ 'activity.caption' => 'software/name',
9
+ 'activity_object.native_id' => 'software/id',
10
+ 'activity_object.title' => 'software/name',
11
+ 'activity_object.body' => 'category/name',
12
+ 'activity_object.url' => 'software/complete_url',
13
+ 'media.thumbnail_url' => 'software/complete_icon_url',
14
+ 'rating_summary.rater_count' => 'software/num_users',
15
+ 'rating_summary.view_count' => 'software/active_seconds'
16
+ }
17
+ }
18
+ ACCEPTED_HOSTS = [/wakoopa\.com/]
19
+
20
+ # most_used
21
+
22
+ # Available data:
23
+ # software: active-seconds
24
+ # software: created-at
25
+ # software: description
26
+ # software: id *
27
+ # software: name *
28
+ # software: num-users
29
+ # software: updated-at
30
+ # software: url *
31
+ # software: complete-url
32
+ # software: complete-icon-url
33
+ # software: complete-thumb-url
34
+ # software: developer: id
35
+ # software: developer: name
36
+ # software: developer: complete-url
37
+ # software: category: description *
38
+ # software: category: id
39
+ # software: category: name
40
+ # software: category: complete-url
41
+ # software: os-types
42
+ # activity["most_used"].each do |software|
43
+ #
44
+ # occurred_at = DateTime.parse(software['created_at']).to_time
45
+ #
46
+ # next if Activity.exists?(self.user_id, software['category']['description'], occurred_at)
47
+ #
48
+ # activity = Activity.create(
49
+ # :service_profile => self,
50
+ # :user_id => self.user_id,
51
+ # :native_id => software['id'].to_i,
52
+ # :activity_verb_id => ActivityVerb::MOST_USED.id,
53
+ # :url => software['complete_url'],
54
+ # :occurred_at => occurred_at # UTC OK
55
+ # )
56
+ #
57
+ # activity.object = ActivityObject.create(
58
+ # :title => software['name'],
59
+ # :body => software['category']['description'],
60
+ # :url => activity.url,
61
+ # :native_id => activity.native_id,
62
+ # :activity_object_type_id => ActivityObjectType::STATUS.id
63
+ # )
64
+ #
65
+ # activity.save
66
+ # end
67
+
68
+ # comments
69
+
70
+ # Available data:
71
+ # created-at
72
+ # id
73
+ # text
74
+ # owner: active-seconds
75
+ # owner: country
76
+ # owner: created-at
77
+ # owner: id
78
+ # owner: name
79
+ # owner: updated-at
80
+ # owner: username
81
+ # owner: complete-url
82
+ # owner: complete-icon-url
83
+ # owner: complete-thumb-url
84
+ # owner: os-types
85
+
86
+
87
+ # TODO use this data:
88
+ # None, the Wakoopa API calls assume you know the user already and doesn't explicitly provide this data
89
+
90
+ # TO DO: implement these fields
91
+
92
+ # contacts
93
+ # NB in de JSON call hebben ze het ineens over user ipv contact
94
+ # Hiermee haal je de eerste 10 contacts/users binnen
95
+
96
+ # Available data
97
+ # contact: active-seconds
98
+ # contact: country
99
+ # contact: created-at
100
+ # contact: id
101
+ # contact: name
102
+ # contact: updated-at
103
+ # contact: username
104
+ # contact: complete-url
105
+ # contact: complete-icon-url
106
+ # contact: complete-thumb-url
107
+ # contact: os-types
108
+
109
+ # teams
110
+ # Alleen 1e 10 teams
111
+
112
+ # Available data
113
+ # team: active seconds
114
+ # team: created-at
115
+ # team: description
116
+ # team: id
117
+ # team: name
118
+ # team: num-users
119
+ # team: complete-url
120
+
121
+ def create_or_update_summary!(options = {})
122
+ #softwares = WakoopaConnector.most_used_for(attributes[:username]) # Gebeurt er nog wat met softwares? Nee, niet echt :]
123
+ # avatar not (yet) available in the API calls
124
+ # native_user_id not (yet) available in the API calls
125
+ @profile.update_attributes(:username => self.class.username_from_url(@profile.url))
126
+ end
127
+
128
+ # TODO: since this is a summary, this should be done on create! and update! Need to implement recent activity here
129
+ # TODO use this data:
130
+ # software.complete_icon_url
131
+ # software.complete_thumb_url
132
+ # software.category
133
+ # software.num_users
134
+ # software.developer
135
+ # software.url
136
+ # software.active_seconds
137
+ def aggregate_activity!(options = {})
138
+ # most_used_softwares = WakoopaConnector.most_used_for(username)
139
+ # attributes = attributes_or_url.is_a?(Hash) ? attributes_or_url : {:username => username_from_url(attributes_or_url)}
140
+ #activities = WakoopaConnector.get_all_activity_for(username)
141
+ #activities.each { |activity,data| aggregate_for_activity(activity,data) }
142
+
143
+ mapper = ActivityDataMapper.new(ACTIVITY_MAP)
144
+ mapper.fetch!("http://api.wakoopa.com/#{@profile.username}/recently_used.json",
145
+ :format => :json,
146
+ :strip_callback => true
147
+ )
148
+ mapper.map!
149
+ mapper.entries.each do |entry|
150
+ next if Activity.exists?(@profile.user_id, entry)
151
+ create_activity(entry, ActivityObjectType::SOFTWARE, ActivityVerb::RECENTLY_USED)
152
+ end
153
+
154
+ =begin
155
+ data.each do |software|
156
+
157
+ occurred_at = (software['software']['created_at'])
158
+
159
+ next if Activity.exists?(self.user_id, software['software']['name'], occurred_at)
160
+
161
+ activity = Activity.create(
162
+ :service_profile => self,
163
+ :user_id => self.user_id,
164
+ :native_id => software['software']['id'].to_i,
165
+ :activity_verb_id => ActivityVerb::RECENTLY_USED.id,
166
+ :url => software['software']['complete_url'],
167
+ :occurred_at => occurred_at # UTC OK
168
+ )
169
+
170
+ body = (software['software']['category'].class == NilClass) ? "" : software['software']['category']['description']
171
+
172
+ activity.object = ActivityObject.create(
173
+ :title => software['software']['name'],
174
+ :body => body,
175
+ :url => activity.url,
176
+ :native_id => activity.native_id,
177
+ :activity_object_type_id => ActivityObjectType::STATUS.id
178
+ )
179
+
180
+ activity.save
181
+ end
182
+ =end
183
+
184
+ end
185
+ =begin
186
+ def aggregate_for_activity(activity,data)
187
+ case activity
188
+ when "recently_used"
189
+ recently_used(data)
190
+ when "newly_used"
191
+ newly_used(data)
192
+ when "comments"
193
+ comments(data)
194
+ when "reviews"
195
+ reviews(data)
196
+ else puts "error"
197
+ end
198
+ end
199
+
200
+ def recently_used(data)
201
+ # Available data is the same as for most_used.
202
+ data.each do |software|
203
+
204
+ occurred_at = (software['software']['created_at'])
205
+
206
+ next if Activity.exists?(self.user_id, software['software']['name'], occurred_at)
207
+
208
+ activity = Activity.create(
209
+ :service_profile => self,
210
+ :user_id => self.user_id,
211
+ :native_id => software['software']['id'].to_i,
212
+ :activity_verb_id => ActivityVerb::RECENTLY_USED.id,
213
+ :url => software['software']['complete_url'],
214
+ :occurred_at => occurred_at # UTC OK
215
+ )
216
+
217
+ body = (software['software']['category'].class == NilClass) ? "" : software['software']['category']['description']
218
+
219
+ activity.object = ActivityObject.create(
220
+ :title => software['software']['name'],
221
+ :body => body,
222
+ :url => activity.url,
223
+ :native_id => activity.native_id,
224
+ :activity_object_type_id => ActivityObjectType::STATUS.id
225
+ )
226
+
227
+ activity.save
228
+ end
229
+ end
230
+
231
+ def newly_used(data)
232
+ # Available data is the same as for most_used.
233
+ data.each do |software|
234
+ # occurred_at = DateTime.parse(software['software']['updated_at']).to_time
235
+ # JSON converter automatically makes it into a time
236
+ occurred_at = software['software']['updated_at'] # Is this the proper field?
237
+
238
+ next if Activity.exists?(self.user_id, software['software']['name'], occurred_at)
239
+
240
+ activity = Activity.create(
241
+ :service_profile => self,
242
+ :user_id => self.user_id,
243
+ :native_id => software['software']['id'].to_i,
244
+ :activity_verb_id => ActivityVerb::NEWLY_USED.id,
245
+ :url => software['software']['complete_url'],
246
+ :occurred_at => occurred_at # UTC OK
247
+ )
248
+
249
+ body = (software['software']['category'].class == NilClass) ? "" : software['software']['category']['description']
250
+
251
+ activity.object = ActivityObject.create(
252
+ :title => software['software']['name'],
253
+ :body => body,
254
+ :url => activity.url,
255
+ :native_id => activity.native_id,
256
+ :activity_object_type_id => ActivityObjectType::STATUS.id
257
+ )
258
+
259
+ activity.save
260
+ end
261
+ end
262
+
263
+ # COMMENTS
264
+ # NB Get the 10 most recent comments. This can be adjusted to [1..30]
265
+
266
+ # Available data:
267
+ # created-at *
268
+ # id *
269
+ # text *
270
+ # owner: active-seconds
271
+ # owner: country
272
+ # owner: created-at
273
+ # owner: id
274
+ # owner: name
275
+ # owner: updated-at
276
+ # owner: username
277
+ # owner: complete-url
278
+ # owner: complete-icon-url
279
+ # owner: complete-thumb-url
280
+ # owner: os-types
281
+ def comments(data)
282
+ data.each do |comment|
283
+ # JSON converter automatically makes it into a time
284
+ occurred_at = comment['user_comment']['created_at']
285
+
286
+ next if Activity.exists?(self.user_id, comment['user_comment']['text'], occurred_at)
287
+
288
+ activity = Activity.create(
289
+ :service_profile => self,
290
+ :user_id => self.user_id,
291
+ :native_id => comment['user_comment']['id'].to_i,
292
+ :activity_verb_id => ActivityVerb::COMMENT.id,
293
+ :url => "http://wakoopa.com/#{username}/comments?page=1",
294
+ :occurred_at => occurred_at # UTC OK
295
+ )
296
+
297
+ activity.object = ActivityObject.create(
298
+ :title => comment['user_comment']['text'],
299
+ :body => comment['user_comment']['text'],
300
+ :url => activity.url,
301
+ :native_id => activity.native_id,
302
+ :activity_object_type_id => ActivityObjectType::STATUS.id
303
+ )
304
+
305
+ activity.save
306
+ end
307
+ end
308
+
309
+
310
+ # REVIEWS
311
+
312
+ # Available data: same as most_used, plus:
313
+ # created-at *
314
+ # id *
315
+ # rating TO DO
316
+ # text *
317
+ def reviews(data)
318
+
319
+ data.each do |review|
320
+
321
+ occurred_at = review['review']['created_at']
322
+
323
+ next if Activity.exists?(self.user_id, review['review']['text'], occurred_at)
324
+
325
+ activity = Activity.create(
326
+ :service_profile => self,
327
+ :user_id => self.user_id,
328
+ :native_id => review['review']['id'].to_i,
329
+ :activity_verb_id => ActivityVerb::REVIEW.id,
330
+ :url => review['review']['software']['complete_url'] + "/review/#{review['id']}",
331
+ :occurred_at => occurred_at # UTC OK
332
+ )
333
+
334
+ activity.object = ActivityObject.create(
335
+ :title => review['review']['text'],
336
+ :body => review['review']['text'],
337
+ :url => activity.url,
338
+ :native_id => activity.native_id,
339
+ :activity_object_type_id => ActivityObjectType::STATUS.id
340
+ )
341
+
342
+ activity.save
343
+ end
344
+ end
345
+
346
+ =end
347
+
348
+ end
349
+
350
+ end
@@ -0,0 +1,63 @@
1
+
2
+ module ActivityMapper
3
+
4
+ class YoutubeServiceModule < ServiceModule
5
+ ACTIVITY_MAP = {
6
+ 'feed/entry' => {
7
+ 'activity.occurred_at' => 'published/$t',
8
+ 'activity.caption' => 'title/$t',
9
+ 'activity_object.title' => 'title/$t',
10
+ 'activity_object.body' => 'media$group/media$description/$t',
11
+ 'activity_object.url' => 'media$group/media$player/url',
12
+ 'activity.url' => 'media$group/media$player/url',
13
+ 'media.thumbnail_url' => 'media$group/media$thumbnail[0]/url',
14
+ 'media.embed_url' => 'media$group/media$content[0]/url',
15
+ 'media.duration' => 'media$group/yt$duration/seconds',
16
+ 'rating_summary.rater_count' => 'gd$rating/numRaters',
17
+ 'rating_summary.min' => 'gd$rating/min',
18
+ 'rating_summary.max' => 'gd$rating/max',
19
+ 'rating_summary.average' => 'gd$rating/average',
20
+ 'rating_summary.view_count' => 'yt$statistics/viewCount',
21
+ 'rating_summary.favorite_count' => 'yt$statistics/favoriteCount'
22
+ }
23
+ }
24
+ ACCEPTED_HOSTS = [/youtube\.com/]
25
+
26
+ # TODO use this data:
27
+ # user view statistics
28
+ def create_or_update_summary!(options = {})
29
+ @profile.update_attributes(:username => self.class.username_from_url(@profile.url))
30
+ end
31
+
32
+ # TODO use this data
33
+ # native id (need to somehow encode this to a big ass number: eg: dyMVZqJk8s4)
34
+ # media.categories
35
+ # media.keywords
36
+ # media.credit
37
+ # (media.uploader)
38
+ def aggregate_activity!(options = {})
39
+ mapper = ActivityDataMapper.new(ACTIVITY_MAP)
40
+
41
+ mapper.fetch!(
42
+ "http://gdata.youtube.com/feeds/api/users/#{@profile.username}/uploads?v=2&alt=json",
43
+ :format => :json
44
+ )
45
+ mapper.map!
46
+ mapper.entries.each do |entry|
47
+ next if Activity.exists?(@profile.user_id, entry)
48
+ create_activity(entry, ActivityObjectType::VIDEO, ActivityVerb::POST)
49
+ end
50
+
51
+ mapper.fetch!(
52
+ "http://gdata.youtube.com/feeds/api/users/#{@profile.username}/favorites?v=2&alt=json",
53
+ :format => :json
54
+ )
55
+ mapper.map!
56
+ mapper.entries.each do |entry|
57
+ next if Activity.exists?(@profile.user_id, entry)
58
+ create_activity(entry, ActivityObjectType::VIDEO, ActivityVerb::FAVORITE)
59
+ end
60
+ end
61
+
62
+ end
63
+ end