StatsCollect 0.1.1.20101220 → 0.2.0.20110830

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -15,34 +15,26 @@ module StatsCollect
15
15
  # * *iConf* (<em>map<Symbol,Object></em>): Configuration of this backend
16
16
  def initSession(iConf)
17
17
  @IdxID = 0
18
+ logMsg 'Session initialized.'
18
19
  end
19
20
 
20
- # Get the next stats order.
21
- # Code to begin a new transaction can be set in this method too.
21
+ # Get the next stats orders.
22
22
  #
23
- # Return:
24
- # * _DateTime_: The time stamp, or nil if no new stats order
25
- # * <em>list<String></em>: List of locations
26
- # * <em>list<String></em>: List of objects
27
- # * <em>list<String></em>: List of categories
28
- # * _Integer_: The order status
29
- def getNextStatsOrder
30
- rTimeStamp = nil
31
- rLstLocations = nil
32
- rLstObjects = nil
33
- rLstCategories = nil
34
- rStatus = nil
35
-
36
- if (defined?(@LstStatsOrders) == nil)
37
- @LstStatsOrders = [
38
- [ DateTime.now, [], [], [], STATS_ORDER_STATUS_TOBEPROCESSED ]
39
- ]
40
- end
41
- if (!@LstStatsOrders.empty?)
42
- rTimeStamp, rLstLocations, rLstObjects, rLstCategories, rStatus = @LstStatsOrders.pop
43
- end
44
-
45
- return rTimeStamp, rLstLocations, rLstObjects, rLstCategories, rStatus
23
+ # Parameters:
24
+ # * *oStatsOrdersProxy* (_StatsOrdersProxy_): The stats orders proxy to be used to give stats orders
25
+ def getStatsOrders(oStatsOrdersProxy)
26
+ oStatsOrdersProxy.addStatsOrder(0, DateTime.now, [], [], [], STATS_ORDER_STATUS_TOBEPROCESSED)
27
+ # oStatsOrdersProxy.addStatsOrder(0, DateTime.now, ['MySpace'], [], ['Friends list'], STATS_ORDER_STATUS_TOBEPROCESSED)
28
+ logMsg 'Added stats order 0.'
29
+ end
30
+
31
+ # Dequeue the given stat orders IDs.
32
+ # Code to begin a new transaction can be set in this method too. In this case, the dequeue should be part of the transaction, or it will have to be re-enqueued during rollback method call (otherwise orders will be lost).
33
+ #
34
+ # Parameters:
35
+ # * *iLstStatsOrderIDs* (<em>list<Integer></em>): The list of stats order IDs to dequeue
36
+ def dequeueStatsOrders(iLstStatsOrderIDs)
37
+ logMsg "Transaction started and stats orders dequeued: #{iLstStatsOrderIDs.join(', ')}"
46
38
  end
47
39
 
48
40
  # Get the list of known locations
@@ -125,7 +117,23 @@ module StatsCollect
125
117
  # * *iValue* (_Object_): The value to store
126
118
  # * *iValueType* (_Integer_): The value type
127
119
  def addStat(iTimeStamp, iLocationID, iObjectID, iCategoryID, iValue, iValueType)
128
- logMsg "Added stat: #{iTimeStamp} | Location: #{iLocationID} | Object: #{iObjectID} | Category: #{iCategoryID} | Value: #{iValue}"
120
+ logMsg "Added stat: #{iTimeStamp} | Location: #{iLocationID} | Object: #{iObjectID} | Category: #{iCategoryID} (value type: #{iValueType}) | Value: #{iValue}"
121
+ end
122
+
123
+ # Get an existing stat value
124
+ #
125
+ # Parameters:
126
+ # * *iTimeStamp* (_DateTime_): The timestamp
127
+ # * *iLocationID* (_Integer_): The location ID
128
+ # * *iObjectID* (_Integer_): The object ID
129
+ # * *iCategoryID* (_Integer_): The category ID
130
+ # * *iValueType* (_Integer_): The value type
131
+ # Return:
132
+ # * _Object_: The corresponding value, or nil if none
133
+ def getStat(iTimeStamp, iLocationID, iObjectID, iCategoryID, iValueType)
134
+ logMsg "Get stat: #{iTimeStamp} | Location: #{iLocationID} | Object: #{iObjectID} | Category: #{iCategoryID} (value type: #{iValueType})"
135
+
136
+ return nil
129
137
  end
130
138
 
131
139
  # Add a new stats order
@@ -150,6 +158,10 @@ module StatsCollect
150
158
  logMsg 'Transaction rollbacked'
151
159
  end
152
160
 
161
+ # Close a session of this backend
162
+ def closeSession
163
+ end
164
+
153
165
  end
154
166
 
155
167
  end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -22,7 +22,7 @@ module StatsCollect
22
22
  def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
23
  require 'mechanize'
24
24
  lMechanizeAgent = Mechanize.new
25
- # Get the number of likes from Facebook
25
+ # Get the number of shares
26
26
  if (oStatsProxy.isCategoryIncluded?('Monthly shares'))
27
27
  getDomains(oStatsProxy, lMechanizeAgent, iConf, 'month', 'Monthly shares')
28
28
  end
@@ -0,0 +1,106 @@
1
+ #--
2
+ # Copyright (c) 2010 - 2011 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 CSV
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
+ if (!iConf[:Files].empty?)
24
+ # Get the list of categories, locations and objects
25
+ lCategories = oStatsProxy.getCategories
26
+ lObjects = oStatsProxy.getObjects
27
+ lLocations = oStatsProxy.getLocations
28
+ lCSVLocations = []
29
+ lCSVObjects = []
30
+ lCSVCategories = []
31
+ require 'csv'
32
+ iConf[:Files].each do |iFileName|
33
+ lIdxLine = 0
34
+ lMissingIDs = false
35
+ ::CSV::open(iFileName, 'r', :col_sep => iConf[:ColumnSeparator], :quote_char => iConf[:QuoteChar], :row_sep => iConf[:RowSeparator]).each do |iRow|
36
+ case lIdxLine
37
+ when 0
38
+ # We have locations in this line
39
+ iRow[1..-1].each do |iLocationName|
40
+ if (lLocations[iLocationName] == nil)
41
+ logWarn "Unknown location from CSV file #{iFileName}: #{iLocationName}"
42
+ lMissingIDs = true
43
+ end
44
+ lCSVLocations << iLocationName
45
+ end
46
+ when 1
47
+ # We have objects in this line
48
+ iRow[1..-1].each do |iObjectName|
49
+ if (lObjects[iObjectName] == nil)
50
+ logWarn "Unknown object from CSV file #{iFileName}: #{iObjectName}"
51
+ lMissingIDs = true
52
+ end
53
+ lCSVObjects << iObjectName
54
+ end
55
+ when 2
56
+ # We have categories in this line
57
+ iRow[1..-1].each do |iCategoryName|
58
+ if (lCategories[iCategoryName] == nil)
59
+ logWarn "Unknown category from CSV file #{iFileName}: #{iCategoryName}"
60
+ lMissingIDs = true
61
+ end
62
+ lCSVCategories << iCategoryName
63
+ end
64
+ else
65
+ if (lMissingIDs and
66
+ (iConf[:IDsMustExist]))
67
+ raise RuntimeError.new("Missing some IDs from CSV file #{iFileName}.")
68
+ end
69
+ # A line of data
70
+ lTimestamp = DateTime.strptime(iRow[0], iConf[:DateTimeFormat])
71
+ iRow[1..-1].each_with_index do |iStrValue, iIdx|
72
+ if (iStrValue != nil)
73
+ # Interpret the CSV value based on the value type of this column
74
+ lValueType = (lCategories[lCSVCategories[iIdx]] || STATS_VALUE_TYPE_UNKNOWN)
75
+ lValue = nil
76
+ case lValueType
77
+ when STATS_VALUE_TYPE_INTEGER
78
+ lValue = Integer(iStrValue)
79
+ when STATS_VALUE_TYPE_FLOAT
80
+ lValue = Float(iStrValue)
81
+ when STATS_VALUE_TYPE_PERCENTAGE
82
+ lValue = Float(iStrValue)
83
+ when STATS_VALUE_TYPE_UNKNOWN
84
+ lValue = iStrValue
85
+ when STATS_VALUE_TYPE_MAP
86
+ lValue = eval(iStrValue)
87
+ when STATS_VALUE_TYPE_STRING
88
+ lValue = iStrValue
89
+ else
90
+ raise RuntimeError.new("Unknown value type for category #{lCSVCategories[iIdx]}: #{lValueType}")
91
+ end
92
+ oStatsProxy.addStat(lCSVObjects[iIdx], lCSVCategories[iIdx], lValue, :Timestamp => lTimestamp, :Location => lCSVLocations[iIdx])
93
+ end
94
+ end
95
+ end
96
+ lIdxLine += 1
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -22,6 +22,8 @@ module StatsCollect
22
22
  def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
23
  require 'mechanize'
24
24
  lMechanizeAgent = Mechanize.new
25
+ # Set a specific user agent, as Facebook 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'
25
27
  lLoginForm = lMechanizeAgent.get('http://www.facebook.com').forms[0]
26
28
  lLoginForm.email = iConf[:LoginEMail]
27
29
  lLoginForm.pass = iConf[:LoginPassword]
@@ -29,7 +31,7 @@ module StatsCollect
29
31
  lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first)
30
32
  if ((oStatsProxy.isObjectIncluded?('Global')) and
31
33
  (oStatsProxy.isCategoryIncluded?('Friends')))
32
- getProfile(oStatsProxy, lMechanizeAgent)
34
+ getProfile(oStatsProxy, lMechanizeAgent, iConf)
33
35
  end
34
36
  end
35
37
 
@@ -38,11 +40,13 @@ module StatsCollect
38
40
  # Parameters:
39
41
  # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
40
42
  # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
41
- def getProfile(oStatsProxy, iMechanizeAgent)
42
- lProfilePage = iMechanizeAgent.get('http://www.facebook.com/profile.php')
43
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
44
+ def getProfile(oStatsProxy, iMechanizeAgent, iConf)
45
+ lProfilePage = iMechanizeAgent.get("http://www.facebook.com/#{iConf[:URLID]}")
43
46
  lNbrFriends = nil
44
47
  lProfilePage.root.css('script').each do |iScriptNode|
45
- lMatch = iScriptNode.content.match(/>Friends \((\d*)\)<\\\/a><\\\/span>/)
48
+ lMatch = iScriptNode.content.match(/sk=friends&amp;v=friends\\">Friends \((\d*)\)/)
49
+ #lMatch = iScriptNode.content.match(/>Friends \((\d*)\)/)
46
50
  # The following line is valid for old profiles only
47
51
  #lMatch = iScriptNode.content.match(/>(\d*) friends<\\\/a><\\\/span>/)
48
52
  if (lMatch != nil)
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -22,6 +22,8 @@ module StatsCollect
22
22
  def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
23
23
  require 'mechanize'
24
24
  lMechanizeAgent = Mechanize.new
25
+ # Set a specific user agent, as Facebook 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'
25
27
  lLoginForm = lMechanizeAgent.get('http://www.facebook.com').forms[0]
26
28
  lLoginForm.email = iConf[:LoginEMail]
27
29
  lLoginForm.pass = iConf[:LoginPassword]
@@ -43,7 +45,7 @@ module StatsCollect
43
45
  lProfilePage = iMechanizeAgent.get("http://www.facebook.com/pages/#{iConf[:PageID]}")
44
46
  lNbrLikes = nil
45
47
  lProfilePage.root.css('script').each do |iScriptNode|
46
- lMatch = iScriptNode.content.match(/>(\d*) People Like This/)
48
+ lMatch = iScriptNode.content.match(/>\\u003cspan class=\\"uiNumberGiant fsxxl fwb\\">(\d*)\\u003c\\\/span>/)
47
49
  if (lMatch != nil)
48
50
  lNbrLikes = Integer(lMatch[1])
49
51
  break
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -0,0 +1,103 @@
1
+ #--
2
+ # Copyright (c) 2010 - 2011 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 GoogleGroup
11
+
12
+ MEMBERSTATUS_INVITED = 0
13
+ MEMBERSTATUS_MEMBER = 1
14
+ MEMBERSTATUS_OWNER = 2
15
+
16
+ # Execute the plugin.
17
+ # This method has to add the stats and errors to the proxy.
18
+ # It can filter only objects and categories given.
19
+ # It has access to its configuration.
20
+ #
21
+ # Parameters:
22
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
23
+ # * *iConf* (<em>map<Symbol,Object></em>): The configuration associated to this plugin
24
+ # * *iLstObjects* (<em>list<String></em>): List of objects to filter (can be empty for all)
25
+ # * *iLstCategories* (<em>list<String></em>): List of categories to filter (can be empty for all)
26
+ def execute(oStatsProxy, iConf, iLstObjects, iLstCategories)
27
+ require 'mechanize'
28
+ lMechanizeAgent = Mechanize.new
29
+ lLoginForm = lMechanizeAgent.get('http://groups.google.com/').link_with(:text => 'Sign in').click.forms[0]
30
+ lLoginForm.Email = iConf[:LoginEMail]
31
+ lLoginForm.Passwd = iConf[:LoginPassword]
32
+ if (Mechanize::VERSION > '1.0.0')
33
+ lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first).meta_refresh.first.click
34
+ else
35
+ lMechanizeAgent.submit(lLoginForm, lLoginForm.buttons.first).meta.first.click
36
+ end
37
+ iConf[:Objects].each do |iGroupName|
38
+ if (oStatsProxy.isCategoryIncluded?('Friends'))
39
+ getMembers(oStatsProxy, lMechanizeAgent, iGroupName)
40
+ end
41
+ if (oStatsProxy.isCategoryIncluded?('Friends list'))
42
+ getMembersList(oStatsProxy, lMechanizeAgent, iGroupName)
43
+ end
44
+ end
45
+ end
46
+
47
+ # Get the members statistics
48
+ #
49
+ # Parameters:
50
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
51
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
52
+ # * *iGroupName* (_String_): Name of the group to retrieve members from
53
+ def getMembers(oStatsProxy, iMechanizeAgent, iGroupName)
54
+ lMembersPage = iMechanizeAgent.get("http://groups.google.com/group/#{iGroupName}/manage_members?hl=en")
55
+ lNbrFriends = Integer(lMembersPage.root.css('div.mngcontentbox table.membertabs tr td.st b').first.content.match(/All members \((\d*)\)/)[1])
56
+ oStatsProxy.addStat(iGroupName, 'Friends', lNbrFriends)
57
+ end
58
+
59
+ # Get the members list
60
+ #
61
+ # Parameters:
62
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
63
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
64
+ # * *iGroupName* (_String_): Name of the group to retrieve members from
65
+ def getMembersList(oStatsProxy, iMechanizeAgent, iGroupName)
66
+ lExportForm = iMechanizeAgent.get("http://groups.google.com/group/#{iGroupName}/manage_members?hl=en").forms[4]
67
+ lLstMembers = iMechanizeAgent.submit(lExportForm, lExportForm.buttons.first).content.split("\n")[2..-1]
68
+ # The map of members
69
+ # map< Name, [ MemberStatus, JoinDateTime ] >
70
+ lMapMembers = {}
71
+ lLstMembers.each do |iStrMemberInfo|
72
+ lEmail, _, lStrStatus, _, _, _, lStrYear, lStrMonth, lStrDay, lStrHour, lStrMinute, lStrSecond = iStrMemberInfo.split(',')
73
+ lStatus = nil
74
+ case lStrStatus
75
+ when 'invited'
76
+ lStatus = MEMBERSTATUS_INVITED
77
+ when 'member'
78
+ lStatus = MEMBERSTATUS_MEMBER
79
+ when 'owner'
80
+ lStatus = MEMBERSTATUS_OWNER
81
+ else
82
+ logErr "Unknown member status (#{lStrStatus}) for email #{lEmail}. Will be counted as a member."
83
+ lStatus = MEMBERSTATS_MEMBER
84
+ end
85
+ lMapMembers[lEmail] = [
86
+ lStatus,
87
+ DateTime.civil(
88
+ lStrYear.to_i,
89
+ lStrMonth.to_i,
90
+ lStrDay.to_i,
91
+ lStrHour.to_i,
92
+ lStrMinute.to_i,
93
+ lStrSecond.to_i)
94
+ ]
95
+ end
96
+ oStatsProxy.addStat(iGroupName, 'Friends list', lMapMembers)
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -30,14 +30,17 @@ module StatsCollect
30
30
  lLoginForm.Password = iConf[:LoginPassword]
31
31
  # Submit to get to the home page
32
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)
33
+ if (oStatsProxy.isObjectIncluded?('Global'))
34
+ if (oStatsProxy.isCategoryIncluded?('Comments'))
35
+ getProfile(oStatsProxy, lMechanizeAgent)
36
+ end
37
+ if ((oStatsProxy.isCategoryIncluded?('Friends')) or
38
+ (oStatsProxy.isCategoryIncluded?('Visits')))
39
+ getDashboard(oStatsProxy, lMechanizeAgent)
40
+ end
41
+ if (oStatsProxy.isCategoryIncluded?('Friends list'))
42
+ getFriendsList(oStatsProxy, lMechanizeAgent)
43
+ end
41
44
  end
42
45
  if (oStatsProxy.isCategoryIncluded?('Song plays'))
43
46
  getSongs(oStatsProxy, lMechanizeAgent)
@@ -63,7 +66,8 @@ module StatsCollect
63
66
  # Click on the Profile link from the home page
64
67
  lProfilePage = iMechanizeAgent.get('http://www.myspace.com/home').link_with(:text => 'Profile').click
65
68
  # 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])
69
+ lNbrComments = Integer(lProfilePage.root.css('article#module18 div.wrapper section.content div.commentContainer a.moreComments span.cnt').first.content.match(/of (\d*)/)[1])
70
+
67
71
  oStatsProxy.addStat('Global', 'Comments', lNbrComments)
68
72
  end
69
73
 
@@ -75,8 +79,8 @@ module StatsCollect
75
79
  def getDashboard(oStatsProxy, iMechanizeAgent)
76
80
  # Get the dashboard page
77
81
  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]
82
+ lNbrVisits = Integer(lJSonData['data'].select { |iItem| next (iItem[0] == 'myspace_views') }.first[-1].gsub(',',''))
83
+ lNbrFriends = Integer(lJSonData['data'].select { |iItem| next (iItem[0] == 'myspace_friends') }.first[-1].gsub(',',''))
80
84
  oStatsProxy.addStat('Global', 'Visits', lNbrVisits)
81
85
  oStatsProxy.addStat('Global', 'Friends', lNbrFriends)
82
86
 
@@ -217,6 +221,53 @@ module StatsCollect
217
221
  logDebug "#{lLstBlogsRead.size} blogs read: #{lLstBlogsRead.join(', ')}"
218
222
  end
219
223
 
224
+ # Get the friends list
225
+ #
226
+ # Parameters:
227
+ # * *oStatsProxy* (_StatsProxy_): The stats proxy to be used to populate stats
228
+ # * *iMechanizeAgent* (_Mechanize_): The agent reading pages
229
+ def getFriendsList(oStatsProxy, iMechanizeAgent)
230
+ lFriendsPage = iMechanizeAgent.get('http://www.myspace.com/my/friends/grid/page/1')
231
+ # Keep track of the last first friend of the page, as we will detect ending page thanks to it.
232
+ lLastFirstFriend = nil
233
+ lFriendsMap = {}
234
+ lIdxPage = 2
235
+ while (lFriendsPage != nil)
236
+ lFirstFriend = nil
237
+ lFriendsPage.root.css('ul.myDataList li').each do |iFriendNode|
238
+ if (iFriendNode['data-id'] != nil)
239
+ # We have a friend node
240
+ lFriendID = iFriendNode['data-id']
241
+ lFriendName = nil
242
+ iFriendNode.css('div div.vcard span.hcard a.nickname').each do |iFriendLinkNode|
243
+ lFriendName = iFriendLinkNode['href'][1..-1]
244
+ if (lFriendName == nil)
245
+ logErr "Could not get friend's name for ID #{lFriendID}: #{iFriendLinkNode}"
246
+ end
247
+ lFriendsMap[lFriendID] = lFriendName
248
+ end
249
+ if (lFirstFriend == nil)
250
+ # Check if the page has not changed
251
+ if (lLastFirstFriend == lFriendID)
252
+ # Finished
253
+ break
254
+ end
255
+ lFirstFriend = lFriendID
256
+ end
257
+ end
258
+ end
259
+ lLastFirstFriend = lFirstFriend
260
+ # Get next page if we did not reach the end
261
+ if (lLastFirstFriend == nil)
262
+ lFriendsPage = nil
263
+ else
264
+ lFriendsPage = iMechanizeAgent.get("http://www.myspace.com/my/friends/grid/page/#{lIdxPage}")
265
+ lIdxPage += 1
266
+ end
267
+ end
268
+ oStatsProxy.addStat('Global', 'Friends list', lFriendsMap)
269
+ end
270
+
220
271
  end
221
272
 
222
273
  end
@@ -0,0 +1,64 @@
1
+ #--
2
+ # Copyright (c) 2010 - 2011 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 RB
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
+ if (!iConf[:Files].empty?)
24
+ # Get the list of categories, locations and objects
25
+ lCategories = oStatsProxy.getCategories
26
+ lObjects = oStatsProxy.getObjects
27
+ lLocations = oStatsProxy.getLocations
28
+ iConf[:Files].each do |iFileName|
29
+ lLstStats = nil
30
+ File.open(iFileName, 'r') do |iFile|
31
+ lLstStats = eval(iFile.read)
32
+ end
33
+ lMissingIDs = false
34
+ logInfo "Read #{lLstStats.size} stats from RB file."
35
+ lLstStats.each do |ioStatInfo|
36
+ iCheckExistenceBeforeAdd, iTimeStamp, iLocationName, iObjectName, iCategoryName, iValue = ioStatInfo
37
+ ioStatInfo[1] = DateTime.strptime(iTimeStamp, iConf[:DateTimeFormat])
38
+ if (iConf[:IDsMustExist])
39
+ if (lLocations[iLocationName] == nil)
40
+ logWarn "Unknown location from RB file #{iFileName}: #{iLocationName}"
41
+ lMissingIDs = true
42
+ end
43
+ if (lObjects[iObjectName] == nil)
44
+ logWarn "Unknown object from RB file #{iFileName}: #{iObjectName}"
45
+ lMissingIDs = true
46
+ end
47
+ if (lCategories[iCategoryName] == nil)
48
+ logWarn "Unknown category from RB file #{iFileName}: #{iCategoryName}"
49
+ lMissingIDs = true
50
+ end
51
+ end
52
+ end
53
+ if (!lMissingIDs)
54
+ oStatsProxy.addStatsList(lLstStats)
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end