StatsCollect 0.1.0.20101220

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.
@@ -0,0 +1,62 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class Facebook
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ require 'mechanize'
24
+ lMechanizeAgent = Mechanize.new
25
+ lLoginForm = lMechanizeAgent.get('http://www.facebook.com').forms[0]
26
+ lLoginForm.email = iConf[:LoginEMail]
27
+ lLoginForm.pass = iConf[:LoginPassword]
28
+ # Submit to get to the home page
29
+ lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first)
30
+ if ((oStatsProxy.isObjectIncluded?('Global')) and
31
+ (oStatsProxy.isCategoryIncluded?('Friends')))
32
+ getProfile(oStatsProxy, lMechanizeAgent)
33
+ end
34
+ end
35
+
36
+ # Get the profile statistics
37
+ #
38
+ # Parameters:
39
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
40
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
41
+ def getProfile(oStatsProxy, iMechanizeAgent)
42
+ lProfilePage = iMechanizeAgent.get('http://www.facebook.com/profile.php')
43
+ lNbrFriends = nil
44
+ lProfilePage.root.css('script').each do |iScriptNode|
45
+ lMatch = iScriptNode.content.match(/>(\d*) friends<\\\/a><\\\/span>/)
46
+ if (lMatch != nil)
47
+ lNbrFriends = Integer(lMatch[1])
48
+ break
49
+ end
50
+ end
51
+ if (lNbrFriends == nil)
52
+ logErr "Unable to get number of friends: #{lProfilePage.root}"
53
+ else
54
+ oStatsProxy.addStat('Global', 'Friends', lNbrFriends)
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,63 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class FacebookArtist
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ require 'mechanize'
24
+ lMechanizeAgent = Mechanize.new
25
+ lLoginForm = lMechanizeAgent.get('http://www.facebook.com').forms[0]
26
+ lLoginForm.email = iConf[:LoginEMail]
27
+ lLoginForm.pass = iConf[:LoginPassword]
28
+ # Submit to get to the home page
29
+ lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first)
30
+ if ((oStatsProxy.isObjectIncluded?('Global')) and
31
+ (oStatsProxy.isCategoryIncluded?('Likes')))
32
+ getArtistProfile(oStatsProxy, lMechanizeAgent, iConf)
33
+ end
34
+ end
35
+
36
+ # Get the artist profile statistics
37
+ #
38
+ # Parameters:
39
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
40
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
41
+ # * *iConf* (<em>map<Symbol,Object></em>): The conf
42
+ def getArtistProfile(oStatsProxy, iMechanizeAgent, iConf)
43
+ lProfilePage = iMechanizeAgent.get("http://www.facebook.com/pages/#{iConf[:PageID]}")
44
+ lNbrLikes = nil
45
+ lProfilePage.root.css('script').each do |iScriptNode|
46
+ lMatch = iScriptNode.content.match(/>(\d*) People Like This/)
47
+ if (lMatch != nil)
48
+ lNbrLikes = Integer(lMatch[1])
49
+ break
50
+ end
51
+ end
52
+ if (lNbrLikes == nil)
53
+ logErr "Unable to get number of likes: #{lProfilePage.root}"
54
+ else
55
+ oStatsProxy.addStat('Global', 'Likes', lNbrLikes)
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,51 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class FacebookLike
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ require 'mechanize'
24
+ lMechanizeAgent = Mechanize.new
25
+ # Get the number of likes from Facebook
26
+ lErrorObjects = []
27
+ if (oStatsProxy.isCategoryIncluded?('Likes'))
28
+ iConf[:Objects].each do |iObject|
29
+ if (oStatsProxy.isObjectIncluded?(iObject))
30
+ lLikesContent = lMechanizeAgent.get("http://www.facebook.com/plugins/like.php?href=#{iObject}").root.css('span.connect_widget_not_connected_text').first.content.delete(',')
31
+ lMatch = lLikesContent.match(/^(\d*) likes./)
32
+ if (lMatch == nil)
33
+ logErr "Unable to parse FacebookLike output for object #{iObject}: #{lLikesContent}"
34
+ lErrorObjects << iObject
35
+ else
36
+ lNbrLikes = Integer(lMatch[1])
37
+ oStatsProxy.addStat(iObject, 'Likes', lNbrLikes)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ if (!lErrorObjects.empty?)
43
+ oStatsProxy.addUnrecoverableOrder(lErrorObjects, ['Likes'])
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,39 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class GoogleSearch
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ iConf[:Objects].each do |iObject|
24
+ if ((oStatsProxy.isObjectIncluded?(iObject)) and
25
+ (oStatsProxy.isCategoryIncluded?('Search results')))
26
+ require 'mechanize'
27
+ lMechanizeAgent = Mechanize.new
28
+ lProfilePage = lMechanizeAgent.get("http://www.google.com/search?q=#{URI.escape(iObject)}")
29
+ lNbrSearchResults = Integer(lProfilePage.root.css('div#resultStats').first.content.delete(',').strip.match(/ (\d*) /)[1])
30
+ oStatsProxy.addStat(iObject, 'Search results', lNbrSearchResults)
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,224 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class MySpace
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ require 'mechanize'
24
+ lMechanizeAgent = Mechanize.new
25
+ # Set a specific user agent, as myspace will treat our agent as a mobile one
26
+ lMechanizeAgent.user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13'
27
+ # Get the login page
28
+ lLoginForm = lMechanizeAgent.get('http://www.myspace.com/redirector?dest=/home').forms[2]
29
+ lLoginForm.Email = iConf[:LoginEMail]
30
+ lLoginForm.Password = iConf[:LoginPassword]
31
+ # Submit to get to the home page
32
+ lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first)
33
+ if ((oStatsProxy.isObjectIncluded?('Global')) and
34
+ (oStatsProxy.isCategoryIncluded?('Comments')))
35
+ getProfile(oStatsProxy, lMechanizeAgent)
36
+ end
37
+ if ((oStatsProxy.isObjectIncluded?('Global')) and
38
+ ((oStatsProxy.isCategoryIncluded?('Friends')) or
39
+ (oStatsProxy.isCategoryIncluded?('Visits'))))
40
+ getDashboard(oStatsProxy, lMechanizeAgent)
41
+ end
42
+ if (oStatsProxy.isCategoryIncluded?('Song plays'))
43
+ getSongs(oStatsProxy, lMechanizeAgent)
44
+ end
45
+ if ((oStatsProxy.isCategoryIncluded?('Video plays')) or
46
+ (oStatsProxy.isCategoryIncluded?('Video comments')) or
47
+ (oStatsProxy.isCategoryIncluded?('Video likes')) or
48
+ (oStatsProxy.isCategoryIncluded?('Video rating')))
49
+ getVideos(oStatsProxy, lMechanizeAgent)
50
+ end
51
+ if ((oStatsProxy.isCategoryIncluded?('Blog reads')) or
52
+ (oStatsProxy.isCategoryIncluded?('Blog likes')))
53
+ getBlogs(oStatsProxy, lMechanizeAgent, iConf)
54
+ end
55
+ end
56
+
57
+ # Get the profile statistics
58
+ #
59
+ # Parameters:
60
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
61
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
62
+ def getProfile(oStatsProxy, iMechanizeAgent)
63
+ # Click on the Profile link from the home page
64
+ lProfilePage = iMechanizeAgent.get('http://www.myspace.com/home').link_with(:text => 'Profile').click
65
+ # Screen scrap it
66
+ lNbrComments = Integer(lProfilePage.root.css('div.commentsModule > div > div > div > div.moduleBody > div.genericComments > a.moreComments > span.cnt').first.content.match(/of (\d*)/)[1])
67
+ oStatsProxy.addStat('Global', 'Comments', lNbrComments)
68
+ end
69
+
70
+ # Get the dashboard statistics
71
+ #
72
+ # Parameters:
73
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
74
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
75
+ def getDashboard(oStatsProxy, iMechanizeAgent)
76
+ # Get the dashboard page
77
+ lJSonData = eval(iMechanizeAgent.get_file('http://www.myspace.com/stats/fans_json/profile_stats/en-US/x=0').gsub(':','=>'))
78
+ lNbrVisits = lJSonData['data'].select { |iItem| next (iItem[0] == 'myspace_views') }.first[-1]
79
+ lNbrFriends = lJSonData['data'].select { |iItem| next (iItem[0] == 'myspace_friends') }.first[-1]
80
+ oStatsProxy.addStat('Global', 'Visits', lNbrVisits)
81
+ oStatsProxy.addStat('Global', 'Friends', lNbrFriends)
82
+
83
+ # OLD VERSION (keeping it as Myspace changes all the time
84
+ # lDashboardPage = iMechanizeAgent.get('http://www.myspace.com/music/dashboard')
85
+ # # Get the variables used by the Ajax script
86
+ # lMatch = lDashboardPage.root.css('section.moduleBody script').first.content.split("\n").join.match(/var appID = "([^"]*)".*var pkey = "([^"]*)";/)
87
+ # lAppID, lPKey = lMatch[1..2]
88
+ # lCoreUserID = nil
89
+ # lDashboardPage.root.css('body script').each do |iScriptNode|
90
+ # lMatch = iScriptNode.content.split("\n").join.match(/var coreUserId =(\d*)/)
91
+ # if (lMatch != nil)
92
+ # # Found it
93
+ # lCoreUserID = lMatch[1]
94
+ # break
95
+ # end
96
+ # end
97
+ # if (lCoreUserID == nil)
98
+ # logErr "Unable to find the core user ID: #{lDashboardPage.root}"
99
+ # else
100
+ # # Call the Ajax script
101
+ # lStatsAjaxContent = iMechanizeAgent.get_file("http://www.myspace.com/Modules/Music/Handlers/Dashboard.ashx?sourceApplication=#{lAppID}&pkey=#{lPKey}&action=GETCORESTATS&userID=#{lCoreUserID}")
102
+ # lStrVisits, lStrFriends = lStatsAjaxContent.match(/^\{'totalprofileviews':'([^']*)','totalfriends':'([^']*)'/)[1..2]
103
+ # lNbrVisits = Integer(lStrVisits.delete(','))
104
+ # lNbrFriends = Integer(lStrFriends.delete(','))
105
+ # oStatsProxy.addStat('Global', 'Visits', lNbrVisits)
106
+ # oStatsProxy.addStat('Global', 'Friends', lNbrFriends)
107
+ # end
108
+ end
109
+
110
+ # Get the songs statistics
111
+ #
112
+ # Parameters:
113
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
114
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
115
+ def getSongs(oStatsProxy, iMechanizeAgent)
116
+ # Get the songs page
117
+ lSongsPage = iMechanizeAgent.get('http://www.myspace.com/my/songs')
118
+ # Screen scrap it
119
+ # List of objects that can be tried again for the song plays category
120
+ lLstRecoverableObjectsForSongPlays = []
121
+ # List of songs read (used for display)
122
+ lLstSongsPlayRead = []
123
+ lSongsPage.root.css('div.UploadedSong').each do |iSongNode|
124
+ lSongTitle = nil
125
+ lNbrPlays = nil
126
+ iSongNode.css('div#songTitle').each do |iSongTitleNode|
127
+ lSongTitle = iSongTitleNode.content
128
+ end
129
+ lPlaysNode = iSongNode.children[11]
130
+ if (lPlaysNode == nil)
131
+ logErr "Unable to find plays node: #{iSongNode}"
132
+ else
133
+ begin
134
+ lNbrPlays = Integer(lPlaysNode.content)
135
+ rescue Exception
136
+ logErr "Invalid number of plays content: #{lPlaysNode}"
137
+ end
138
+ end
139
+ if (lSongTitle == nil)
140
+ logErr "Unable to get the song title: #{iSongNode}"
141
+ end
142
+ if (lNbrPlays == nil)
143
+ logErr "Unable to get the song number of plays: #{iSongNode}"
144
+ if (lSongTitle != nil)
145
+ # We can try this one again
146
+ lLstRecoverableObjectsForSongPlays << lSongTitle
147
+ end
148
+ end
149
+ if ((lSongTitle != nil) and
150
+ (lNbrPlays != nil))
151
+ oStatsProxy.addStat(lSongTitle, 'Song plays', lNbrPlays)
152
+ end
153
+ lLstSongsPlayRead << lSongTitle
154
+ end
155
+ logDebug "#{lLstSongsPlayRead.size} songs read for songs plays: #{lLstSongsPlayRead.join(', ')}"
156
+ if (!lLstRecoverableObjectsForSongPlays.empty?)
157
+ oStatsProxy.addRecoverableOrder(lLstRecoverableObjectsForSongPlays, ['Song plays'])
158
+ end
159
+ end
160
+
161
+ # Get the videos statistics
162
+ #
163
+ # Parameters:
164
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
165
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
166
+ def getVideos(oStatsProxy, iMechanizeAgent)
167
+ # Get the videos page
168
+ lVideosPage = iMechanizeAgent.get('http://www.myspace.com/my/videos')
169
+ # Screen scrap it
170
+ # List of videos read (used for display)
171
+ lLstVideosRead = []
172
+ lVideosPage.root.css('table.myUploadsList tr').each do |iVideoNode|
173
+ lVideoTitle = iVideoNode.css('td.summary h2 a').first.content
174
+ lStatsNodes = iVideoNode.css('td.controls div.text span')
175
+ lNbrPlays = Integer(lStatsNodes[1].content)
176
+ lNbrComments = Integer(lStatsNodes[2].content)
177
+ lMatch = lStatsNodes[3].content.match(/^(\d*)% \((\d*) vote/)
178
+ lRating = Integer(lMatch[1])
179
+ lNbrLikes = Integer(lMatch[2])
180
+ oStatsProxy.addStat(lVideoTitle, 'Video plays', lNbrPlays)
181
+ oStatsProxy.addStat(lVideoTitle, 'Video comments', lNbrComments)
182
+ oStatsProxy.addStat(lVideoTitle, 'Video likes', lNbrLikes)
183
+ oStatsProxy.addStat(lVideoTitle, 'Video rating', lRating)
184
+ lLstVideosRead << lVideoTitle
185
+ end
186
+ logDebug "#{lLstVideosRead.size} videos read: #{lLstVideosRead.join(', ')}"
187
+ end
188
+
189
+ # Get the blogs statistics
190
+ #
191
+ # Parameters:
192
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
193
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
194
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration
195
+ def getBlogs(oStatsProxy, iMechanizeAgent, iConf)
196
+ # TODO: Be able to get the list of blogs using MySpace only, without config
197
+ # Parse each blog ID given from the conf.
198
+ lLstBlogsRead = []
199
+ iConf[:BlogsID].each do |iBlogID|
200
+ # Get the blog page
201
+ lBlogPage = iMechanizeAgent.get("http://www.myspace.com/#{iConf[:MySpaceName]}/blog/#{iBlogID}")
202
+ lBlogTitle = lBlogPage.root.css('h2.post-title').first.content
203
+ lNbrLikes = 0
204
+ lStrLikes = lBlogPage.root.css('span.like span.likeLabel').first.content
205
+ if (!lStrLikes.empty?)
206
+ lNbrLikes = Integer(lStrLikes.match(/\((\d*)\)/)[1])
207
+ end
208
+ lNbrReads = 0
209
+ lStrReads = lBlogPage.root.css('li.blogCommentCnt span').first.content
210
+ if (!lStrReads.empty?)
211
+ lNbrReads = Integer(lStrReads.match(/\((\d*)\)/)[1])
212
+ end
213
+ oStatsProxy.addStat(lBlogTitle, 'Blog likes', lNbrLikes)
214
+ oStatsProxy.addStat(lBlogTitle, 'Blog reads', lNbrReads)
215
+ lLstBlogsRead << lBlogTitle
216
+ end
217
+ logDebug "#{lLstBlogsRead.size} blogs read: #{lLstBlogsRead.join(', ')}"
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
224
+ end
@@ -0,0 +1,143 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class ReverbNation
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ require 'mechanize'
24
+ lMechanizeAgent = Mechanize.new
25
+ lLoginForm = lMechanizeAgent.get('http://www.reverbnation.com/user/login').forms[2]
26
+ lLoginForm.field_with(:name => 'user[login]').value = iConf[:LoginEMail]
27
+ lLoginForm.field_with(:name => 'user[password]').value = iConf[:LoginPassword]
28
+ # Submit to get to the home page
29
+ lHomePage = lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first)
30
+ # Get the user id, as it will be necessary for further requests
31
+ lUserID = lHomePage.uri.to_s.match(/^http:\/\/www\.reverbnation\.com\/artist\/control_room\/(\d*)$/)[1]
32
+ if ((oStatsProxy.isCategoryIncluded?('Song plays')) or
33
+ (oStatsProxy.isCategoryIncluded?('Song downloads')) or
34
+ (oStatsProxy.isCategoryIncluded?('Video plays')) or
35
+ (oStatsProxy.isCategoryIncluded?('Song play ratio')) or
36
+ (oStatsProxy.isCategoryIncluded?('Song likes')) or
37
+ (oStatsProxy.isCategoryIncluded?('Song dislikes')))
38
+ getPlays(oStatsProxy, lMechanizeAgent, lUserID)
39
+ end
40
+ if ((oStatsProxy.isObjectIncluded?('Global')) and
41
+ ((oStatsProxy.isCategoryIncluded?('Chart position genre')) or
42
+ (oStatsProxy.isCategoryIncluded?('Chart position global')) or
43
+ (oStatsProxy.isCategoryIncluded?('Band equity')) or
44
+ (oStatsProxy.isCategoryIncluded?('Friends'))))
45
+ getReport(oStatsProxy, lMechanizeAgent, lUserID)
46
+ end
47
+ end
48
+
49
+ # Get the plays statistics
50
+ #
51
+ # Parameters:
52
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
53
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
54
+ # * *iUserID* (_String_): The ReverbNation user ID
55
+ def getPlays(oStatsProxy, iMechanizeAgent, iUserID)
56
+ # Get the Ajax stats table
57
+ lStatsTable = iMechanizeAgent.get("http://www.reverbnation.com/artist/new_stats_plays_table/#{iUserID}?all_time=true")
58
+ lStatsTableNode = Nokogiri::HTML(lStatsTable.content[31..-4].gsub(/\\"/,'"').gsub(/\\n/,"\n").gsub(/\\r/,''))
59
+ # Screen scrap it
60
+ lLstSongsRead = []
61
+ lStatsTableNode.css('table.statstable_full tr')[1..-3].each do |iSongNode|
62
+ lNodeContents = iSongNode.css('td')
63
+ lSongTitle = lNodeContents[1].content
64
+ lNbrSongPlays = Integer(lNodeContents[3].content)
65
+ lNbrSongDownloads = Integer(lNodeContents[4].content)
66
+ lNbrVideoPlays = Integer(lNodeContents[5].content)
67
+ lPlayRatio = Integer(lNodeContents[6].content.match(/^(\d*)%$/)[1])
68
+ lMatch = lNodeContents[7].content.match(/^(\d*)\/(\d*)$/)
69
+ lNbrLikes = Integer(lMatch[1])
70
+ lNbrDislikes = Integer(lMatch[2])
71
+ oStatsProxy.addStat(lSongTitle, 'Song plays', lNbrSongPlays)
72
+ oStatsProxy.addStat(lSongTitle, 'Song downloads', lNbrSongDownloads)
73
+ oStatsProxy.addStat(lSongTitle, 'Video plays', lNbrVideoPlays)
74
+ oStatsProxy.addStat(lSongTitle, 'Song play ratio', lPlayRatio)
75
+ oStatsProxy.addStat(lSongTitle, 'Song likes', lNbrLikes)
76
+ oStatsProxy.addStat(lSongTitle, 'Song dislikes', lNbrDislikes)
77
+ lLstSongsRead << lSongTitle
78
+ end
79
+ logDebug "#{lLstSongsRead.size} songs read: #{lLstSongsRead.join(', ')}"
80
+ end
81
+
82
+ # Get the report statistics
83
+ #
84
+ # Parameters:
85
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
86
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
87
+ # * *iUserID* (_String_): The ReverbNation user ID
88
+ def getReport(oStatsProxy, iMechanizeAgent, iUserID)
89
+ # Get the report page
90
+ lReportPage = iMechanizeAgent.get("http://www.reverbnation.com/artist/artist_report_printable/#{iUserID}")
91
+ lChartPositionGenre = nil
92
+ lChartPositionGlobal = nil
93
+ lReportPage.root.css('body div#mainwrap div div div.control_room_graph_column').each do |iStatsSectionNode|
94
+ lChildrenNodes = iStatsSectionNode.children
95
+ lChildrenNodes.each_with_index do |iNode, iIdx|
96
+ if (iNode.content == 'Genre:')
97
+ lChartPositionGenre = Integer(lChildrenNodes[iIdx+1].content.strip)
98
+ elsif (iNode.content == 'Global:')
99
+ lChartPositionGlobal = Integer(lChildrenNodes[iIdx+1].content.strip)
100
+ end
101
+ end
102
+ if ((lChartPositionGenre != nil) and
103
+ (lChartPositionGlobal != nil))
104
+ break
105
+ end
106
+ end
107
+ if (lChartPositionGenre == nil)
108
+ logErr "Unable to get the chart positions: #{lReportPage.root}"
109
+ else
110
+ lBandEquityScore = nil
111
+ lNbrFriends = nil
112
+ lReportPage.root.css('body div#mainwrap div div div.printable_pane').each do |iPaneNode|
113
+ lChildrenNodes = iPaneNode.children
114
+ lChildrenNodes.each_with_index do |iNode, iIdx|
115
+ if (iNode.content == 'Band Equity Score: ')
116
+ lBandEquityScore = Integer(lChildrenNodes[iIdx+1].content.strip)
117
+ elsif (iNode.content == 'Total Fans: ')
118
+ lNbrFriends = Integer(lChildrenNodes[iIdx+1].content.strip)
119
+ end
120
+ end
121
+ if ((lBandEquityScore != nil) and
122
+ (lNbrFriends != nil))
123
+ break
124
+ end
125
+ end
126
+ if (lBandEquityScore == nil)
127
+ logErr "Unable to get the band equity score: #{lReportPage.root}"
128
+ elsif (lNbrFriends == nil)
129
+ logErr "Unable to get the number of friends: #{lReportPage.root}"
130
+ else
131
+ oStatsProxy.addStat('Global', 'Chart position genre', lChartPositionGenre)
132
+ oStatsProxy.addStat('Global', 'Chart position global', lChartPositionGlobal)
133
+ oStatsProxy.addStat('Global', 'Band equity', lBandEquityScore)
134
+ oStatsProxy.addStat('Global', 'Friends', lNbrFriends)
135
+ end
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+
143
+ end
@@ -0,0 +1,51 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module StatsCollect
7
+
8
+ module Locations
9
+
10
+ class Tweets
11
+
12
+ # Execute the plugin.
13
+ # This method has to add the stats and errors to the proxy.
14
+ # It can filter only objects and categories given.
15
+ # It has access to its configuration.
16
+ #
17
+ # Parameters:
18
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
19
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
20
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
21
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
22
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
+ require 'mechanize'
24
+ lMechanizeAgent = Mechanize.new
25
+ # Get the number of likes from Facebook
26
+ if (oStatsProxy.isCategoryIncluded?('Tweets'))
27
+ lFailedObjects = []
28
+ iConf[:Objects].each do |iObject|
29
+ if (oStatsProxy.isObjectIncluded?(iObject))
30
+ lTweetsData = lMechanizeAgent.get_file("http://urls.api.twitter.com/1/urls/count.json?url=#{iObject}").gsub(/:/,'=>')
31
+ if (lTweetsData.match(/Unable to access URL counting services/) != nil)
32
+ # An error occurred: we can try again
33
+ logErr "Error while fetching Tweets for #{iObject}: #{lTweetsData}"
34
+ lFailedObjects << iObject
35
+ else
36
+ lNbrTweets = eval(lTweetsData)['count']
37
+ oStatsProxy.addStat(iObject, 'Tweets', lNbrTweets)
38
+ end
39
+ end
40
+ end
41
+ if (!lFailedObjects.empty?)
42
+ oStatsProxy.addRecoverableOrder(lFailedObjects, ['Tweets'])
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end