Beispiel #1
0
    def get_links(self, game: str, version: int, userid: UserID) -> List[Link]:
        """
        Given a game/version/userid, find all links between this user and other users

        Parameters:
            game - String identifier of the game looking up the user.
            version - Integer version of the game looking up the user.
            userid - Integer user ID, as looked up by one of the above functions.

        Returns:
            A list of Link objects.
        """
        sql = "SELECT type, other_userid, data FROM link WHERE game = :game AND version = :version AND userid = :userid"
        cursor = self.execute(sql, {'game': game, 'version': version, 'userid': userid})

        links = []
        for result in cursor.fetchall():
            links.append(
                Link(
                    userid,
                    result['type'],
                    UserID(result['other_userid']),
                    self.deserialize(result['data']),
                )
            )

        return links
Beispiel #2
0
    def get_all_time_based_achievements(self, game: str, version: int) -> List[Tuple[UserID, Achievement]]:
        """
        Given a game/version, find all time-based achievements for all players.

        Parameters:
            game - String identifier of the game looking up the user.
            version - Integer version of the game looking up the user.

        Returns:
            A list of (UserID, Achievement) objects.
        """
        sql = (
            "SELECT time_based_achievement.id AS id, time_based_achievement.type AS type, "
            "time_based_achievement.data AS data, time_based_achievement.timestamp AS timestamp, "
            "refid.userid AS userid FROM time_based_achievement, refid WHERE refid.game = :game AND "
            "refid.version = :version AND refid.refid = time_based_achievement.refid"
        )
        cursor = self.execute(sql, {'game': game, 'version': version})

        achievements = []
        for result in cursor.fetchall():
            achievements.append(
                (
                    UserID(result['userid']),
                    Achievement(
                        result['id'],
                        result['type'],
                        result['timestamp'],
                        self.deserialize(result['data']),
                    ),
                )
            )

        return achievements
Beispiel #3
0
    def get_all_arcades(self) -> List[Arcade]:
        """
        List all known arcades in the system.

        Returns:
            A list of Arcade objects.
        """
        sql = "SELECT userid, arcadeid FROM arcade_owner"
        cursor = self.execute(sql)
        arcade_to_owners: Dict[int, List[UserID]] = {}
        for row in cursor.fetchall():
            arcade = row['arcadeid']
            owner = UserID(row['userid'])
            if arcade not in arcade_to_owners:
                arcade_to_owners[arcade] = []
            arcade_to_owners[arcade].append(owner)

        sql = "SELECT id, name, description, pin, data FROM arcade"
        cursor = self.execute(sql)
        return [
            Arcade(
                ArcadeID(result['id']),
                result['name'],
                result['description'],
                result['pin'],
                self.deserialize(result['data']),
                arcade_to_owners.get(result['id'], []),
            ) for result in cursor.fetchall()
        ]
Beispiel #4
0
    def get_all_lobbies(self, game: str,
                        version: int) -> List[Tuple[UserID, ValidatedDict]]:
        """
        Given a game and version, look up all active lobbies.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.

        Returns:
            A list of dictionaries representing lobby info stored by a game class.
        """
        sql = ("SELECT userid, id, data FROM lobby "
               "WHERE game = :game AND version = :version AND time > :time")
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'time': Time.now() - Time.SECONDS_IN_HOUR,
            },
        )

        ret = []
        for result in cursor.fetchall():
            data = ValidatedDict(self.deserialize(result['data']))
            data['id'] = result['id']
            ret.append((UserID(result['userid']), data))
        return ret
Beispiel #5
0
    def get_all_play_session_infos(
            self, game: str,
            version: int) -> List[Tuple[UserID, ValidatedDict]]:
        """
        Given a game and version, look up all play session information.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.

        Returns:
            A list of Tuples, consisting of a UserID and the dictionary that would be
            returned for that user if get_play_session_info() was called for that user.
        """
        sql = ("SELECT id, time, userid, data FROM playsession "
               "WHERE game = :game AND version = :version "
               "AND time > :time")
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'time': Time.now() - Time.SECONDS_IN_HOUR,
            },
        )

        ret = []
        for result in cursor.fetchall():
            data = ValidatedDict(self.deserialize(result['data']))
            data['id'] = result['id']
            data['time'] = result['time']
            ret.append((UserID(result['userid']), data))
        return ret
Beispiel #6
0
    def get_events(
        self,
        userid: Optional[UserID] = None,
        arcadeid: Optional[ArcadeID] = None,
        event: Optional[str] = None,
        limit: Optional[int] = None,
        since_id: Optional[int] = None,
        until_id: Optional[int] = None,
    ) -> List[Event]:
        # Base query
        sql = "SELECT id, timestamp, userid, arcadeid, type, data FROM audit "

        # Lets get specific!
        wheres = []
        if userid is not None:
            wheres.append("userid = :userid")
        if arcadeid is not None:
            wheres.append("arcadeid = :arcadeid")
        if event is not None:
            wheres.append("type = :event")
        if since_id is not None:
            wheres.append("id >= :since_id")
        if until_id is not None:
            wheres.append("id < :until_id")
        if len(wheres) > 0:
            sql = sql + f"WHERE {' AND '.join(wheres)} "

        # Order it newest to oldest
        sql = sql + "ORDER BY id DESC"
        if limit is not None:
            sql = sql + " LIMIT :limit"
        cursor = self.execute(
            sql, {
                'userid': userid,
                'arcadeid': arcadeid,
                'event': event,
                'limit': limit,
                'since_id': since_id,
                'until_id': until_id
            })
        events = []
        for result in cursor.fetchall():
            if result['userid'] is not None:
                userid = UserID(result['userid'])
            else:
                userid = None
            if result['arcadeid'] is not None:
                arcadeid = ArcadeID(result['arcadeid'])
            else:
                arcadeid = None
            events.append(
                Event(
                    result['id'],
                    result['timestamp'],
                    userid,
                    arcadeid,
                    result['type'],
                    self.deserialize(result['data']),
                ), )
        return events
Beispiel #7
0
    def get_all_profiles(self, game: str,
                         version: int) -> List[Tuple[UserID, ValidatedDict]]:
        """
        Given a game/version, look up all user profiles for that game.

        Parameters:
            game - String identifier of the game we want all user profiles for.
            version - Integer version of the game we want all user profiles for.

        Returns:
            A list of (UserID, dictionaries) previously stored by a game class for each profile.
        """
        sql = (
            "SELECT refid.userid AS userid, refid.refid AS refid, extid.extid AS extid, profile.data AS data "
            "FROM refid, profile, extid "
            "WHERE refid.game = :game AND refid.version = :version "
            "AND refid.refid = profile.refid AND extid.game = refid.game AND extid.userid = refid.userid"
        )
        cursor = self.execute(sql, {'game': game, 'version': version})

        profiles = []
        for result in cursor.fetchall():
            profile = {
                'refid': result['refid'],
                'extid': result['extid'],
                'game': game,
                'version': version,
            }
            profile.update(self.deserialize(result['data']))
            profiles.append((
                UserID(result['userid']),
                ValidatedDict(profile),
            ))

        return profiles
Beispiel #8
0
    def from_extid(self, game: str, version: int,
                   extid: int) -> Optional[UserID]:
        """
        Given a generated ExtID, look up a user ID.

        Note that there is a unique RefID and ExtID for each profile, and both can be used
        to look up a user. When creating a new profile, we generate a unique RefID and ExtID.

        Parameters:
            game - String identifier of the game looking up the user.
            version - Integer version of the game looking up the user.
            extid - ExtID in question, most likely previously generated by this class.

        Returns:
            User ID as an integer if found, or None if not.
        """
        # First, look up the user account
        sql = "SELECT userid FROM extid WHERE game = :game AND extid = :extid"
        cursor = self.execute(sql, {'game': game, 'extid': extid})
        if cursor.rowcount != 1:
            # Couldn't find a user with this refid
            return None

        result = cursor.fetchone()
        return UserID(result['userid'])
Beispiel #9
0
    def get_all_cards(self) -> List[Tuple[str, Optional[UserID]]]:
        """
        Look up all cards associated with any account.

        Returns:
            A list of Tuples representing representing card ID, user ID pairs.
        """
        sql = "SELECT id, userid FROM card"
        cursor = self.execute(sql)
        return [(res['id'], UserID(res['userid']) if res['userid'] is not None else None) for res in cursor.fetchall()]
Beispiel #10
0
    def get_all_cards(self) -> List[Tuple[str, UserID]]:
        """
        Look up all cards associated with any account.

        Returns:
            A list of Tuples representing representing card ID, user ID pairs.
        """
        sql = "SELECT id, userid FROM card"
        cursor = self.execute(sql)
        return [(str(res['id']).upper(), UserID(res['userid'])) for res in cursor.fetchall()]
Beispiel #11
0
    def get_all_users(self) -> List[User]:
        """
        Look up all users in the system.

        Returns:
            A list of User objects representing all users.
        """
        sql = "SELECT id, username, email, admin FROM user"
        cursor = self.execute(sql)
        return [
            User(UserID(result['id']), result['username'], result['email'], result['admin'] == 1)
            for result in cursor.fetchall()
        ]
Beispiel #12
0
    def from_session(self, session: str) -> Optional[UserID]:
        """
        Given a previously-opened session, look up a user ID.

        Parameters:
            session - String identifying a session that was opened by create_session.

        Returns:
            User ID as an integer if found, or None if the session is expired or doesn't exist.
        """
        userid = self._from_session(session, 'userid')
        if userid is None:
            return None
        return UserID(userid)
Beispiel #13
0
    def get_all_players(self, game: str, version: int) -> List[UserID]:
        """
        Given a game/version, look up all user IDs that played this game/version.

        Parameters:
            game - String identifier of the game we want all user profiles for.
            version - Integer version of the game we want all user profiles for.

        Returns:
            A list of UserIDs for users that played this version of this game.
        """
        sql = ("SELECT refid.userid AS userid FROM refid "
               "WHERE refid.game = :game AND refid.version = :version")
        cursor = self.execute(sql, {'game': game, 'version': version})

        return [UserID(result['userid']) for result in cursor.fetchall()]
Beispiel #14
0
    def get_score_by_key(self, game: str, version: int,
                         key: int) -> Optional[Tuple[UserID, Score]]:
        """
        Look up previous high score by key.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.
            key - Integer representing a unique key fetched in a previous Score lookup.

        Returns:
            The optional data stored by the game previously, or None if no score exists.
        """
        sql = (
            "SELECT music.songid AS songid, music.chart AS chart, score.id AS scorekey, score.timestamp AS timestamp, score.update AS `update`, "
            + "score.userid AS userid, score.lid AS lid, " +
            "(select COUNT(score_history.timestamp) FROM score_history WHERE score_history.musicid = music.id AND score_history.userid = score.userid) AS plays, "
            +
            "score.points AS points, score.data AS data FROM score, music WHERE score.id = :scorekey AND score.musicid = music.id "
            + "AND music.game = :game AND music.version = :version")
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'scorekey': key,
            },
        )
        if cursor.rowcount != 1:
            # score doesn't exist
            return None

        result = cursor.fetchone()
        return (UserID(result['userid']),
                Score(
                    result['scorekey'],
                    result['songid'],
                    result['chart'],
                    result['points'],
                    result['timestamp'],
                    result['update'],
                    result['lid'],
                    result['plays'],
                    self.deserialize(result['data']),
                ))
Beispiel #15
0
    def from_username(self, username: str) -> Optional[UserID]:
        """
        Given a username, look up a user ID.

        Parameters:
            username - A string representing the user's username.

        Returns:
            User ID as an integer if found, or None if not.
        """
        sql = "SELECT id FROM user WHERE username = :username"
        cursor = self.execute(sql, {'username': username})
        if cursor.rowcount != 1:
            # Couldn't find this username
            return None

        result = cursor.fetchone()
        return UserID(result['id'])
Beispiel #16
0
    def get_attempt_by_key(self, game: str, version: int,
                           key: int) -> Optional[Tuple[UserID, Attempt]]:
        """
        Look up a previous attempt by key.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.
            key - Integer representing a unique key fetched in a previous Attempt lookup.

        Returns:
            The optional data stored by the game previously, or None if no score exists.
        """
        sql = (
            "SELECT music.songid AS songid, music.chart AS chart, score_history.id AS scorekey, score_history.timestamp AS timestamp, score_history.userid AS userid, "
            +
            "score_history.lid AS lid, score_history.new_record AS new_record, score_history.points AS points, score_history.data AS data FROM score_history, music "
            +
            "WHERE score_history.id = :scorekey AND score_history.musicid = music.id AND music.game = :game AND music.version = :version"
        )
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'scorekey': key,
            },
        )
        if cursor.rowcount != 1:
            # score doesn't exist
            return None

        result = cursor.fetchone()
        return (UserID(result['userid']),
                Attempt(
                    result['scorekey'],
                    result['songid'],
                    result['chart'],
                    result['points'],
                    result['timestamp'],
                    result['lid'],
                    True if result['new_record'] == 1 else False,
                    self.deserialize(result['data']),
                ))
Beispiel #17
0
    def get_balances(self, arcadeid: ArcadeID) -> List[Tuple[UserID, int]]:
        """
        Given an arcade ID, look up all user's PASELI balances for that arcade.

        Parameters:
            arcadeid - The arcade in question.

        Returns:
            The PASELI balance for each user at this arcade.
        """
        sql = "SELECT userid, balance FROM balance WHERE arcadeid = :arcadeid"
        cursor = self.execute(sql, {'arcadeid': arcadeid})
        balances = []
        for entry in cursor.fetchall():
            balances.append((
                UserID(entry['userid']),
                entry['balance'],
            ))
        return balances
Beispiel #18
0
    def from_cardid(self, cardid: str) -> Optional[UserID]:
        """
        Given a 16 digit card ID, look up a user ID.

        Note that this is the E004 number as stored on the card. Not the 16 digit
        ASCII value on the back. Use CardCipher to convert.

        Parameters:
            cardid - 16-digit card ID to look for.

        Returns:
            User ID as an integer if found, or None if not.
        """
        # First, look up the user account
        sql = "SELECT userid FROM card WHERE id = :id"
        cursor = self.execute(sql, {'id': cardid})
        if cursor.rowcount != 1:
            # Couldn't find a user with this card
            return None

        result = cursor.fetchone()
        return UserID(result['userid'])
Beispiel #19
0
 def card_to_userid(cardid: str) -> UserID:
     return UserID(int(cardid, 16))
Beispiel #20
0
    def get_all_attempts(
        self,
        game: str,
        version: Optional[int] = None,
        userid: Optional[UserID] = None,
        songid: Optional[int] = None,
        songchart: Optional[int] = None,
        timelimit: Optional[int] = None,
        limit: Optional[int] = None,
        offset: Optional[int] = None,
    ) -> List[Tuple[Optional[UserID], Attempt]]:
        """
        Look up all of the attempts to score for a particular game.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.

        Returns:
            A list of UserID, Attempt objects representing all score attempts for a game, sorted newest to oldest attempts.
        """
        # First, construct the queries for grabbing the songid/chart
        if version is not None:
            songidquery = (
                'SELECT songid FROM music WHERE music.id = score_history.musicid AND game = :game AND version = :version'
            )
            chartquery = (
                'SELECT chart FROM music WHERE music.id = score_history.musicid AND game = :game AND version = :version'
            )
        else:
            songidquery = (
                'SELECT songid FROM music WHERE music.id = score_history.musicid AND game = :game ORDER BY version DESC LIMIT 1'
            )
            chartquery = (
                'SELECT chart FROM music WHERE music.id = score_history.musicid AND game = :game ORDER BY version DESC LIMIT 1'
            )

        # Now, construct the inner select statement so we can choose which scores we care about
        innerselect = ('SELECT DISTINCT(id) FROM music WHERE game = :game')
        if version is not None:
            innerselect = innerselect + ' AND version = :version'
        if songid is not None:
            innerselect = innerselect + ' AND songid = :songid'
        if songchart is not None:
            innerselect = innerselect + ' AND chart = :songchart'

        # Finally, construct the full query
        sql = (
            "SELECT ({}) AS songid, ({}) AS chart, id AS scorekey, timestamp, points, new_record, lid, data, userid "
            "FROM score_history WHERE musicid IN ({})").format(
                songidquery, chartquery, innerselect)

        # Now, limit the query
        if userid is not None:
            sql = sql + ' AND userid = :userid'
        if timelimit is not None:
            sql = sql + ' AND timestamp >= :timestamp'
        sql = sql + ' ORDER BY timestamp DESC'
        if limit is not None:
            sql = sql + ' LIMIT :limit'
        if offset is not None:
            sql = sql + ' OFFSET :offset'

        # Now, query itself
        cursor = self.execute(
            sql, {
                'game': game,
                'version': version,
                'userid': userid,
                'songid': songid,
                'songchart': songchart,
                'timestamp': timelimit,
                'limit': limit,
                'offset': offset,
            })

        # Now objectify the attempts
        attempts = []
        for result in cursor.fetchall():
            attempts.append(
                (UserID(result['userid']) if result['userid'] > 0 else None,
                 Attempt(
                     result['scorekey'],
                     result['songid'],
                     result['chart'],
                     result['points'],
                     result['timestamp'],
                     result['lid'],
                     True if result['new_record'] == 1 else False,
                     self.deserialize(result['data']),
                 )))

        return attempts
Beispiel #21
0
    def get_all_records(
        self,
        game: str,
        version: Optional[int] = None,
        userlist: Optional[List[UserID]] = None,
        locationlist: Optional[List[int]] = None,
    ) -> List[Tuple[UserID, Score]]:
        """
        Look up all of a game's records, only returning the top score for each song. For score ties,
        king-of-the-hill rules are in effect, so for two players with an identical top score, the player
        that got the score last wins. If a list of user IDs is given, we will only look up records pertaining
        to those users. So if another user has a higher record, we will ignore this. This can be used to
        display area-local high scores, etc.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.
            userlist - List of UserIDs to limit the search to.
            locationlist - A list of location IDs to limit searches to.

        Returns:
            A list of UserID, Score objects representing all high scores for a game.
        """
        # First, construct the queries for grabbing the songid/chart
        if version is not None:
            songidquery = (
                'SELECT songid FROM music WHERE music.id = score.musicid AND game = :game AND version = :version'
            )
            chartquery = (
                'SELECT chart FROM music WHERE music.id = score.musicid AND game = :game AND version = :version'
            )
        else:
            songidquery = (
                'SELECT songid FROM music WHERE music.id = score.musicid AND game = :game ORDER BY version DESC LIMIT 1'
            )
            chartquery = (
                'SELECT chart FROM music WHERE music.id = score.musicid AND game = :game ORDER BY version DESC LIMIT 1'
            )

        # Next, get a list of all songs that were played given the input criteria
        musicid_sql = (
            "SELECT DISTINCT(score.musicid) FROM score, music WHERE score.musicid = music.id AND music.game = :game"
        )
        params: Dict[str, Any] = {'game': game}
        if version is not None:
            musicid_sql = musicid_sql + ' AND music.version = :version'
            params['version'] = version

        # Figure out where the record was earned
        if locationlist is not None:
            if len(locationlist) == 0:
                # We don't have any locations, but SQL will shit the bed, so lets add a default one.
                locationlist.append(-1)
            location_sql = "AND score.lid IN :locationlist"
            params['locationlist'] = tuple(locationlist)
        else:
            location_sql = ""

        # Figure out who got the record
        if userlist is not None:
            if len(userlist) == 0:
                # We don't have any users, but SQL will shit the bed, so lets add a fake one.
                userlist.append(UserID(-1))
            user_sql = f"SELECT userid FROM score WHERE score.musicid = played.musicid AND score.userid IN :userlist {location_sql} ORDER BY points DESC, timestamp DESC LIMIT 1"
            params['userlist'] = tuple(userlist)
        else:
            user_sql = f"SELECT userid FROM score WHERE score.musicid = played.musicid {location_sql} ORDER BY points DESC, timestamp DESC LIMIT 1"
        records_sql = f"SELECT ({user_sql}) AS userid, musicid FROM ({musicid_sql}) played"

        # Now, join it up against the score and music table to grab the info we need
        sql = (
            "SELECT ({}) AS songid, ({}) AS chart, score.points AS points, score.userid AS userid, score.id AS scorekey, score.data AS data, "
            + "score.timestamp AS timestamp, score.update AS `update`, " +
            "score.lid AS lid, (select COUNT(score_history.timestamp) FROM score_history WHERE score_history.musicid = score.musicid) AS plays "
            +
            "FROM score, ({}) records WHERE records.userid = score.userid AND records.musicid = score.musicid"
        ).format(songidquery, chartquery, records_sql)
        cursor = self.execute(sql, params)

        scores = []
        for result in cursor.fetchall():
            scores.append((UserID(result['userid']),
                           Score(
                               result['scorekey'],
                               result['songid'],
                               result['chart'],
                               result['points'],
                               result['timestamp'],
                               result['update'],
                               result['lid'],
                               result['plays'],
                               self.deserialize(result['data']),
                           )))

        return scores
Beispiel #22
0
    def get_all_scores(
        self,
        game: str,
        version: Optional[int] = None,
        userid: Optional[UserID] = None,
        songid: Optional[int] = None,
        songchart: Optional[int] = None,
        since: Optional[int] = None,
        until: Optional[int] = None,
    ) -> List[Tuple[UserID, Score]]:
        """
        Look up all of a game's high scores for all users.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.

        Returns:
            A list of UserID, Score objects representing all high scores for a game.
        """
        # First, construct the queries for grabbing the songid/chart
        if version is not None:
            songidquery = (
                'SELECT songid FROM music WHERE music.id = score.musicid AND game = :game AND version = :version'
            )
            chartquery = (
                'SELECT chart FROM music WHERE music.id = score.musicid AND game = :game AND version = :version'
            )
        else:
            songidquery = (
                'SELECT songid FROM music WHERE music.id = score.musicid AND game = :game ORDER BY version DESC LIMIT 1'
            )
            chartquery = (
                'SELECT chart FROM music WHERE music.id = score.musicid AND game = :game ORDER BY version DESC LIMIT 1'
            )

        # Select statement for getting play count
        playselect = (
            'SELECT COUNT(timestamp) FROM score_history WHERE score_history.musicid = score.musicid AND score_history.userid = score.userid'
        )

        # Now, construct the inner select statement so we can choose which scores we care about
        innerselect = ('SELECT DISTINCT(id) FROM music WHERE game = :game')
        if version is not None:
            innerselect = innerselect + ' AND version = :version'
        if songid is not None:
            innerselect = innerselect + ' AND songid = :songid'
        if songchart is not None:
            innerselect = innerselect + ' AND chart = :songchart'

        # Finally, construct the full query
        sql = (
            "SELECT ({}) AS songid, ({}) AS chart, id AS scorekey, points, timestamp, `update`, lid, data, userid, ({}) AS plays "
            "FROM score WHERE musicid IN ({})").format(songidquery, chartquery,
                                                       playselect, innerselect)

        # Now, limit the query
        if userid is not None:
            sql = sql + ' AND userid = :userid'
        if since is not None:
            sql = sql + ' AND score.update >= :since'
        if until is not None:
            sql = sql + ' AND score.update < :until'

        # Now, query itself
        cursor = self.execute(
            sql, {
                'game': game,
                'version': version,
                'userid': userid,
                'songid': songid,
                'songchart': songchart,
                'since': since,
                'until': until,
            })

        # Objectify result
        scores = []
        for result in cursor.fetchall():
            scores.append((UserID(result['userid']),
                           Score(
                               result['scorekey'],
                               result['songid'],
                               result['chart'],
                               result['points'],
                               result['timestamp'],
                               result['update'],
                               result['lid'],
                               result['plays'],
                               self.deserialize(result['data']),
                           )))

        return scores
Beispiel #23
0
 def test_is_remote(self) -> None:
     self.assertTrue(RemoteUser.is_remote(UserID(2**64 - 1)))
     self.assertTrue(RemoteUser.is_remote(UserID(2**32)))
     self.assertFalse(RemoteUser.is_remote(UserID(2**32 - 1)))
     self.assertFalse(RemoteUser.is_remote(UserID(0)))
     self.assertFalse(RemoteUser.is_remote(UserID(1)))
    def test_get_league_scores(self) -> None:
        data = Mock()
        data.local = Mock()
        data.local.user = Mock()

        # Test correct behavior on empty input
        self.assertEqual(
            JubeatProp._get_league_scores(
                None,
                999,
                []
            ),
            (
                [],
                [],
            ),
        )

        # Test that we can load last week's score if it exists for a user
        data.local.user.get_achievement = Mock(return_value={'score': [123, 456, 789]})
        self.assertEqual(
            JubeatProp._get_league_scores(
                data,
                999,
                [(UserID(1337), {})],
            ),
            (
                [(1337, 1368)],
                [],
            ),
        )
        data.local.user.get_achievement.assert_called_once_with(
            JubeatProp.game,
            JubeatProp.version,
            1337,
            998,
            'league',
        )
        data.local.user.get_achievement.reset_mock()

        # Test that if it doesn't exist last week they get marked as absent
        data.local.user.get_achievement = Mock(return_value=None)
        self.assertEqual(
            JubeatProp._get_league_scores(
                data,
                999,
                [(UserID(1337), {})],
            ),
            (
                [],
                [1337],
            ),
        )
        data.local.user.get_achievement.assert_called_once_with(
            JubeatProp.game,
            JubeatProp.version,
            1337,
            998,
            'league',
        )
        data.local.user.get_achievement.reset_mock()