def test_playtime_good_name(): good_name = 'Playtime' t = 30 cards = [ StatCardMock('StatName1', 'sdfafasf', True, 60 * 10).as_dict(), StatCardMock('TTPTotal', good_name, True, 60 * t).as_dict(), ] assert find_times(cards) == (t, DEFAULT)
def test_playtime_one_timestamp(): timeplayed = str(60 * 300) cards = [ StatCardMock('StatName1', 'DisplayName mock').as_dict(), StatCardMock('StatName2', 'This should be catched', True, timeplayed).as_dict(), ] assert find_times(cards) == (300, DEFAULT)
def test_playtime_one_timestamp_round(): """should round to nearest full minute""" timeplayed_sec = '91' timeplayed_min = 2 cards = [ StatCardMock('StatName1', 'DisplayName mock').as_dict(), StatCardMock('StatName2', 'This should be catched', True, timeplayed_sec).as_dict(), ] assert find_times(cards) == (timeplayed_min, DEFAULT)
def test_playtime_one_zero(): timeplayed = '0' cards = [ StatCardMock('StatName2', 'This should be catched', True, timeplayed, unit="Seconds").as_dict(), ] assert find_times(cards) == (int(timeplayed), DEFAULT)
def test_two_modes(): t1 = 24342 t2 = 1231 total = round((t1 + t2) / 60) cards = [ StatCardMock('TTPPVE', 'PvE Play time', True, t1).as_dict(), StatCardMock('TTPPVP', 'PvP Play time', True, t2).as_dict(), StatCardMock('storyCompletion.gameplayed.ACR', 'ACR | Story (%)').as_dict(), ] assert find_times(cards) == (total, DEFAULT)
def only_last_played(): last_modified = '2019-03-21T12:01:19.636Z' last_modified_timespan = 1553169680 cards = [ { 'displayName': 'Number of Maps Published', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 13, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:31:00.000Z', 'statName': 'AMapsPublished', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Wins in Arcade PVP', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 16, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-02-03T00:39:00.000Z', 'statName': 'APvPWins', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Deaths in...rcade PVP', 'endDate': None, 'format': 'Integer', 'lastModified': last_modified, 'locale': 'en-GB', 'obj': '', 'ordinal': 9, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:51:00.000Z', 'statName': 'DDeathArcadePvP', 'unit': '', 'value': '0' }, ] assert find_times(cards) == (DEFAULT, last_modified_timespan)
def test_playtime_assasin3_remastered(): t1 = '0' t2 = 'None' total_min = 0 cards = [ StatCardMock('timePlayed.gameplayed.AC3', 'AC3 | Play Time', True, t1, 'Seconds').as_dict(), StatCardMock('totalSync.gameplayed.AC3', 'AC3 | Total Synchronization (%)').as_dict(), StatCardMock('timePlayed.gameplayed.ACL', 'ACL | Play Time', True, t2, 'Seconds').as_dict(), StatCardMock('totalSync.gameplayed.ACL', 'ACL | Total Synchronization (%)').as_dict(), ] assert find_times(cards) == (total_min, DEFAULT)
def test_gametimes_two_modes_with_last_times_hours(): """Two modes with time in Minutes""" t1 = 2434 t2 = 123 total = round(t1 + t2) older_modify_time = '2018-12-29T22:14:00.021Z' newer_modify_time = '2019-03-29T22:14:20.200Z' newer_modify_timestamp = 1553897660 cards = [ StatCardMock('TTPSolo', 'Play time being alone', True, t1, 'Minutes', older_modify_time).as_dict(), StatCardMock('TTPCoop', 'Coop Play time', True, t2, 'Minutes', newer_modify_time).as_dict(), StatCardMock('storyCompletion.gameplayed.ACR', 'ACR | Story (%)').as_dict(), ] assert find_times(cards) == (total, newer_modify_timestamp)
def test_default_values_when_not_played(): """One card of type LongTimespan; "" and 1970 are Uplay defaults when game was not played""" json_stat = '''{ "statName": "TotalPlayTime", "displayName": "Play time", "locale": "en-US", "value": "", "obj": "", "ordinal": 3, "format": "LongTimespan", "unit": "Seconds", "semantic": "Cumulative", "sort": "Descending", "startDate": null, "endDate": null, "lastModified": "1970-01-01T00:00:00.000Z" }''' cards = [json.loads(json_stat)] assert find_times(cards) == (0, 0)
def test_gametimes_champions_of_anteria(): """Buggy uplay game stats. 'Hours' in this case should be treated as 'Miliseconds'""" playtime_min = 1 playtime_ms = 1000 * 60 * playtime_min space_id = '4b20d5ee-461e-4d27-8c56-e258577c5ed3' json_stat = f'''{{ "statName": "TotalDuration", "displayName": "Playtime", "locale": "en-US", "value": "{playtime_ms}", "obj": "", "ordinal": 0, "format": "LongTimespan", "unit": "Hours", "semantic": "Cumulative", "sort": "Descending", "startDate": "2016-08-01T09:30:00.000Z", "endDate": null, "lastModified": "2017-04-20T22:20:05.150Z" }}''' cards = [json.loads(json_stat)] assert find_times(cards, space_id) == (playtime_min, 1492726805)
def test_playtime_assasin_collection(): t1 = '120' # 2min t2 = '65' # ~1min t3 = 'None' total_min = 3 cards = [ StatCardMock('storyCompletion.gameplayed.AC2', 'ACII | Story (%)', False, '23').as_dict(), StatCardMock('timePlayed.gameplayed.AC2', 'ACII | Play Time', True, t1, 'Seconds').as_dict(), StatCardMock('totalSync.gameplayed.ACB', 'ACB | Total Synchronization (%)').as_dict(), StatCardMock('storyCompletion.gameplayed.ACB', 'ACB | Story (%)').as_dict(), StatCardMock('timePlayed.gameplayed.ACB', 'ACB | Play Time', True, t2, 'Seconds').as_dict(), StatCardMock('totalSync.gameplayed.ACR', 'ACR | Total Synchronization (%)').as_dict(), StatCardMock('storyCompletion.gameplayed.ACR', 'ACR | Story (%)').as_dict(), StatCardMock('timePlayed.gameplayed.ACR', 'ACR | Play Time', True, t3, 'None').as_dict() ] assert find_times(cards) == (total_min, DEFAULT)
async def get_playtime(self, game_ids): if not self.client.is_authenticated(): raise AuthenticationRequired() games_playtime = {} blacklist = json.loads( self.persistent_cache.get('games_without_stats', '{}')) current_time = int(time.time()) for game_id in game_ids: if not self.games_collection.get(game_id): await self.get_owned_games() break for game_id in game_ids: try: expire_in = blacklist.get(game_id, 0) - current_time if expire_in > 0: log.debug( f'Cache: No game stats for {game_id}. Recheck in {expire_in}s' ) games_playtime[game_id] = GameTime(game_id, None, None) continue game = self.games_collection[game_id] if not game.space_id: games_playtime[game_id] = GameTime(game_id, None, None) continue try: response = await self.client.get_game_stats(game.space_id) except ApplicationError as err: self._game_time_import_failure(game_id, err) continue statscards = response.get('Statscards', None) if statscards is None: blacklist[ game_id] = current_time + 3600 * 24 * 14 # two weeks games_playtime[game_id] = GameTime(game_id, None, None) continue playtime, last_played = find_times(statscards, game_id) if playtime == 0: playtime = None if last_played == 0: last_played = None log.info( f'Stats for {game.name}: playtime: {playtime}, last_played: {last_played}' ) games_playtime[game_id] = GameTime(game_id, playtime, last_played) except Exception as e: log.error( f"Getting game times for game {game_id} has crashed: " + repr(e)) self._game_time_import_failure(game_id, UnknownError()) self.persistent_cache['games_without_stats'] = json.dumps(blacklist) self.push_cache() return games_playtime
def test_no_candidate(): cards = [ StatCardMock('StatName1', 'DisplayName mock').as_dict(), StatCardMock('StatName2', 'DisplayName mock').as_dict() ] assert find_times(cards) == (DEFAULT, DEFAULT)
def test_gametimes_farcry5(): total_playetime_sec = 300 total_playetime_min = 5 last_modified = '2019-03-21T12:01:19.636Z' last_modified_timespan = 1553169680 cards = [ { 'displayName': 'Number of Maps Published', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 13, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:31:00.000Z', 'statName': 'AMapsPublished', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Wins in Arcade PVP', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 16, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-02-03T00:39:00.000Z', 'statName': 'APvPWins', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Deaths in...rcade PVP', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 9, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:51:00.000Z', 'statName': 'DDeathArcadePvP', 'unit': '', 'value': '0' }, { 'displayName': 'Story Progress', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-19T18:21:59.047Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 15, 'semantic': 'Best', 'sort': 'Descending', 'startDate': '2018-01-22T23:52:00.000Z', 'statName': 'HGlobalProg', 'unit': '', 'value': '0' }, { 'displayName': 'Number of All Headshot Kills', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.637Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 10, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:52:00.000Z', 'statName': 'KHeadshot', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Kills in ...rcade PVP', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.637Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 8, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:55:00.000Z', 'statName': 'KKillArcadePvP', 'unit': '', 'value': '0' }, { 'displayName': 'Number of NPC Kills...aign mode', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 2, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:57:00.000Z', 'statName': 'KKillCampaign', 'unit': '', 'value': '0' }, { 'displayName': 'Number of All Takedown Kills', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 11, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:58:00.000Z', 'statName': 'KTKDAll', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Fish Caught', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 6, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-22T23:59:00.000Z', 'statName': 'OWFishing', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Hostages Rescued', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 5, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:00:00.000Z', 'statName': 'OWHostage', 'unit': '', 'value': '0' }, { 'displayName': 'Outposts Captured', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 3, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:01:00.000Z', 'statName': 'OWOutpost', 'unit': '', 'value': '0' }, { 'displayName': 'Number of Challenge...Completed', 'endDate': None, 'format': 'Integer', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 4, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:02:00.000Z', 'statName': 'PChallenge', 'unit': '', 'value': '0' }, { 'displayName': 'Time Played in Arcade mode', 'endDate': None, 'format': 'LongTimespan', 'lastModified': '2019-03-21T12:01:19.637Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 7, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:03:00.000Z', 'statName': 'TTPArcade', 'unit': 'Seconds', 'value': '0' }, { 'displayName': 'Time Played in campaign mode', 'endDate': None, 'format': 'LongTimespan', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 1, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:04:00.000Z', 'statName': 'TTPCampaign', 'unit': 'Seconds', 'value': '0' }, { 'displayName': 'Time Played in all game modes', 'endDate': None, 'format': 'LongTimespan', 'lastModified': last_modified, 'locale': 'en-GB', 'obj': '', 'ordinal': 0, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:05:00.000Z', 'statName': 'TTPGameplay', 'unit': 'Seconds', 'value': total_playetime_sec }, { 'displayName': 'Time Spent in Arcade Editor', 'endDate': None, 'format': 'LongTimespan', 'lastModified': '2019-03-21T12:01:19.636Z', 'locale': 'en-GB', 'obj': '', 'ordinal': 12, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2018-01-23T00:07:00.000Z', 'statName': 'TTPIGE', 'unit': 'Seconds', 'value': '0' }, { 'displayName': 'Arcade Player Level', 'endDate': None, 'format': 'Integer', 'lastModified': '1970-01-01T00:00:00.000Z', 'locale': 'en-GB', 'obj': None, 'ordinal': 14, 'semantic': 'Cumulative', 'sort': 'Descending', 'startDate': '2017-09-29T22:14:00.000Z', 'statName': 'ArcadePlayerLevel', 'unit': '', 'value': None }, ] assert find_times(cards) == (total_playetime_min, last_modified_timespan)