Advertisement
nerylix

Outfit Class Breakdown Scraper

Apr 6th, 2014
564
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.06 KB | None | 0 0
  1. #!/usr/bin/python
  2. # Script to get class-partitioned playtimes of all members of an outfit
  3. # Author: Nerylix - [3GIS] Waterson
  4. # Start by running 'python get_playtimes.py -h'
  5. # Licensed under the MIT License:
  6. # http://opensource.org/licenses/MIT
  7.  
  8. import urllib2, json, time, sys
  9.  
  10. # SOE SID (aka API key) from http://census.soe.com/#devSignup
  11. soeSID = ''
  12.  
  13. # outfit alias, aka tag (case sensitive!)
  14. outfitAlias = '3GIS'
  15.  
  16. # ensure the presence of an SID
  17. if soeSID == '':
  18.   print 'ERROR: No SOE SID provided'
  19.   print 'Enter your SOE SID in the variable soeSID'
  20.   sys.exit(1)
  21.  
  22. def showHelp():
  23.   print (
  24. '''Usage: get_playtimes.py [<tag>]
  25. Script to get class-partitioned playtimes of all members of PlanetSide 2 outfit
  26. with given outfit tag. No tag defaults to 3GIS.
  27. Author: Nerylix - [3GIS] Waterson''')
  28.   sys.exit(0)
  29.  
  30. # get override outfit alias if present
  31. if len(sys.argv) == 2:
  32.   if '-h' in sys.argv[1]:
  33.     showHelp()
  34.   else:
  35.     outfitAlias = sys.argv[1]
  36. else:
  37.   print 'WARNING: using default outfit alias "3GIS"'
  38.   print 'NOTE: Run with "-h" for usage.'
  39.  
  40. baseAddr = 'http://census.soe.com/s:' + soeSID + '/'
  41.  
  42. # helper to make the calls
  43. def makeAPICall(collection, callArgs={}, count=False):
  44.   if count: addr = baseAddr + 'count/'
  45.   else:     addr = baseAddr + 'get/'
  46.   addr += 'ps2/' + collection + '/'
  47.   if len(callArgs) != 0:
  48.     addr += '?'
  49.     for k, v in callArgs.items():
  50.       addr += str(k) + '=' + str(v) + '&'
  51.     addr = addr[:-1]
  52.  
  53.     return json.loads(urllib2.urlopen(addr).read())
  54.  
  55. def overwrite(s):
  56.   sys.stdout.write('\r{0}'.format(s).ljust(70))
  57.   sys.stdout.flush()
  58.  
  59. # this is the mapping of profile_type_id to profile_id, indicating the classes
  60. # for which this data is available; this is a poorly designed feature of the
  61. # API.  See
  62. # http://census.soe.com/get/ps2/profile/?c:limit=100&c:lang=en&faction_id=1&c:show=name,profile_type_id&c:sort=profile_type_id
  63. # and
  64. # https://forums.station.sony.com/soe/index.php?threads/class-profile-mis-match-in-character_stat.11500063121/#post-11500338702
  65. profileIDList = [1, 3, 4, 5, 6, 7]
  66.  
  67. # store the data in a list with items of format:
  68. # [<name of player>, IN, LA, CM, EN, HA, MX]
  69. # where each two-letter identifier maps to the total playtime of that player
  70. # on the class indicated by the identifier (e.g. IN is the total playtime of
  71. # the player on the infiltrator class)
  72. playtimeList = []
  73.  
  74. # start by grabbing 3GIS's outfit ID, just in case it changes (I don't think it
  75. # can, but better safe than sorry)
  76. callArgs = {'alias':outfitAlias, 'c:show':'outfit_id,member_count'}
  77. print 'Grabbing {0} outfit ID...   '.format(outfitAlias)
  78. responseDict = makeAPICall('outfit', callArgs)
  79. try:
  80.   outfitID = responseDict['outfit_list'][0]['outfit_id'] # yuck
  81.   memberCount = responseDict['outfit_list'][0]['member_count']
  82. except KeyError:
  83.   print 'Error in API call!\nExiting...'
  84.   sys.exit(1)
  85. except IndexError:
  86.   # if IndexError, that almost certainly means there were no items in the
  87.   # returned list of results
  88.   print 'No outfit with tag {0} exists!\nExiting...'.format(outfitAlias)
  89.   sys.exit(1)
  90. print 'Done.'
  91. print 'Outfit ID:', outfitID
  92. print 'Member count:', memberCount
  93.  
  94. # use outfit key to get member list, resolved against playtime list from
  95. # characters_stat and against character name list from character
  96. # this is a scary API call :)
  97. callArgs = {'outfit_id':outfitID, 'c:limit':memberCount,
  98.             'c:join':'characters_stat^on:character_id^to:character_id^'
  99.             'terms:stat_name=play_time^list:1^inject_at:play_times^'
  100.             'show:profile_id\'value_forever,character^on:character_id^'
  101.             'to:character_id^inject_at:name^show:name.first',
  102.             'c:hide':'member_since,member_since_date,outfit_id,rank,rank_ordinal'}
  103. print 'Grabbing all {0} members\' playtime history...   '.format(outfitAlias)
  104. responseDict = makeAPICall('outfit_member', callArgs)
  105. memberList = responseDict['outfit_member_list']
  106. malformed = 0
  107. for member in memberList:
  108.   try:
  109.     name = member['name']['name']['first']
  110.     memberDataList = [name] + [0] * 6
  111.     memberPlaytimes = member['play_times']
  112.     for time in memberPlaytimes:
  113.       profileID = time['profile_id']
  114.       timePlayed = time['value_forever']
  115.       memberDataList[profileIDList.index(int(profileID)) + 1] = float(timePlayed)/3600.0
  116.   except KeyError:
  117.     malformed += 1
  118.     overwrite('WARNING: {0} malformed results encountered...'.format(malformed))
  119.     continue
  120.   playtimeList.append(memberDataList)
  121.  
  122. if malformed != 0:
  123.   print
  124. print 'Done.'
  125.  
  126. playtimeListSorted = sorted(playtimeList, key=lambda x: sum(x[1:]),
  127.                             reverse=True)
  128.  
  129. print 'Writing CSV...   '
  130. with open('{0}_playtimes.csv'.format(outfitAlias), 'w') as outFile:
  131.   outFile.write('Name,Infiltrator,Light Assault,Combat Medic,Engineer,'
  132.                 'Heavy Assault,MAX,Total\n')
  133.   for playtime in playtimeListSorted:
  134.     playtime.append(sum(playtime[1:]))
  135.     playtime = map(lambda x: str(x), playtime)
  136.     outFile.write(','.join(playtime) + '\n')
  137.  
  138. print 'Done.'
  139. print 'Check "{0}_playtimes.csv"'.format(outfitAlias)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement