StatsCollect 0.1.1.20101220 → 0.2.0.20110830
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.
- data/ChangeLog +98 -0
- data/LICENSE +1 -1
- data/ReleaseInfo +8 -8
- data/StatsCollect.conf.rb.example +43 -2
- data/bin/StatsCollect.rb +1 -1
- data/lib/StatsCollect/Backends/MySQL.rb +253 -41
- data/lib/StatsCollect/Backends/Terminal.rb +39 -27
- data/lib/StatsCollect/Locations/AddThis.rb +2 -2
- data/lib/StatsCollect/Locations/CSV.rb +106 -0
- data/lib/StatsCollect/Locations/Facebook.rb +9 -5
- data/lib/StatsCollect/Locations/FacebookArtist.rb +4 -2
- data/lib/StatsCollect/Locations/FacebookLike.rb +1 -1
- data/lib/StatsCollect/Locations/GoogleGroup.rb +103 -0
- data/lib/StatsCollect/Locations/GoogleSearch.rb +1 -1
- data/lib/StatsCollect/Locations/MySpace.rb +63 -12
- data/lib/StatsCollect/Locations/RB.rb +64 -0
- data/lib/StatsCollect/Locations/ReverbNation.rb +34 -12
- data/lib/StatsCollect/Locations/Tweets.rb +1 -1
- data/lib/StatsCollect/Locations/Twitter.rb +1 -1
- data/lib/StatsCollect/Locations/Youtube.rb +12 -8
- data/lib/StatsCollect/Notifiers/Custom.rb +25 -0
- data/lib/StatsCollect/Notifiers/LogFile.rb +27 -0
- data/lib/StatsCollect/Notifiers/None.rb +1 -1
- data/lib/StatsCollect/Notifiers/SendMail.rb +1 -1
- data/lib/StatsCollect/Stats.rb +115 -74
- data/lib/StatsCollect/StatsOrdersProxy.rb +54 -0
- data/lib/StatsCollect/StatsProxy.rb +47 -6
- metadata +16 -10
- data/TODO +0 -2
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c)
|
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
|
21
|
-
# Code to begin a new transaction can be set in this method too.
|
21
|
+
# Get the next stats orders.
|
22
22
|
#
|
23
|
-
#
|
24
|
-
# *
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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)
|
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
|
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)
|
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
|
-
|
42
|
-
|
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(
|
48
|
+
lMatch = iScriptNode.content.match(/sk=friends&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)
|
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(
|
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
|
@@ -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)
|
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 (
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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.
|
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
|