StatsCollect 0.1.0.20101220

Sign up to get free protection for your applications and to get access to all the features.
@@ -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