def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]: # Fetch the profiles profiles: List[Tuple[UserID, ValidatedDict]] = [] if idtype == APIConstants.ID_TYPE_SERVER: profiles.extend(self.data.local.user.get_all_profiles(self.game, self.version)) elif idtype == APIConstants.ID_TYPE_SONG: raise APIException( 'Unsupported ID for lookup!', 405, ) elif idtype == APIConstants.ID_TYPE_INSTANCE: raise APIException( 'Unsupported ID for lookup!', 405, ) elif idtype == APIConstants.ID_TYPE_CARD: users: Set[UserID] = set() for cardid in ids: userid = self.data.local.user.from_cardid(cardid) if userid is not None: # Don't duplicate loads for users with multiple card IDs if multiples # of those IDs are requested. if userid in users: continue users.add(userid) # We can possibly find another profile for this user. This is important # in the case that we returned scores for a user that doesn't have a # profile on a particular version. We allow that on this network, so in # order to not break remote networks, try our best to return any profile. profile = self.data.local.user.get_any_profile(self.game, self.version, userid) if profile is not None: profiles.append((userid, profile)) else: raise APIException('Invalid ID type!') # Now, fetch the users, and filter out profiles belonging to orphaned users retval: List[Dict[str, Any]] = [] id_to_cards: Dict[UserID, List[str]] = {} for (userid, profile) in profiles: if userid not in id_to_cards: cards = self.data.local.user.get_cards(userid) if len(cards) == 0: # Can't add this user, skip the profile continue id_to_cards[userid] = cards # Format the profile and add it settings = self.data.local.game.get_settings(self.game, userid) if settings is None: settings = ValidatedDict({}) retval.append(self.__format_profile(id_to_cards[userid], profile, settings, profile['version'] == self.version)) return retval
def info() -> Dict[str, Any]: requestdata = request.get_json() if requestdata is None: raise APIException('Request JSON could not be decoded.') if requestdata: raise APIException('Unrecognized parameters for request.') return { 'versions': SUPPORTED_VERSIONS, 'name': g.config.get('name', 'e-AMUSEMENT Network'), 'email': g.config.get('email', '*****@*****.**'), }
def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]: retval: List[Dict[str, Any]] = [] # Fetch the attempts if idtype == APIConstants.ID_TYPE_SERVER: retval = self.__aggregate_global([ attempt[1] for attempt in self.data.local.music.get_all_attempts( self.game, self.music_version) ]) elif idtype == APIConstants.ID_TYPE_SONG: if len(ids) == 1: songid = int(ids[0]) chart = None else: songid = int(ids[0]) chart = int(ids[1]) retval = self.__aggregate_global([ attempt[1] for attempt in self.data.local.music.get_all_attempts(self.game, self.music_version, songid=songid, songchart=chart) ]) elif idtype == APIConstants.ID_TYPE_INSTANCE: songid = int(ids[0]) chart = int(ids[1]) cardid = ids[2] userid = self.data.local.user.from_cardid(cardid) if userid is not None: retval = self.__aggregate_local( {userid: self.data.local.user.get_cards(userid)}, self.data.local.music.get_all_attempts(self.game, self.music_version, songid=songid, songchart=chart, userid=userid)) elif idtype == APIConstants.ID_TYPE_CARD: id_to_cards: Dict[int, List[str]] = {} attempts: List[Tuple[UserID, Attempt]] = [] for cardid in ids: userid = self.data.local.user.from_cardid(cardid) if userid is not None: # Don't duplicate loads for users with multiple card IDs if multiples # of those IDs are requested. if userid in id_to_cards: continue id_to_cards[userid] = self.data.local.user.get_cards( userid) attempts.extend( self.data.local.music.get_all_attempts( self.game, self.music_version, userid=userid)) retval = self.__aggregate_local(id_to_cards, attempts) else: raise APIException('Invalid ID type!') return retval
def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]: # Verify IDs if idtype != APIConstants.ID_TYPE_SERVER: raise APIException( 'Unsupported ID for lookup!', 405, ) # Fetch the songs songs = self.data.local.music.get_all_songs(self.game, self.music_version) if self.game == GameConstants.JUBEAT and self.version in [ VersionConstants.JUBEAT_CLAN, VersionConstants.JUBEAT_FESTO ]: # There's always a special case. We don't store all music IDs since those in # the range of 80000301-80000347 are actually the same song, but copy-pasted # for different prefectures and slightly different charts. So, we need to copy # that song data so that remote clients can resolve scores for those ID ranges. additions: List[Song] = [] for song in songs: if song.id == 80000301: for idrange in range(80000302, 80000348): additions.append( Song( song.game, song.version, idrange, song.chart, song.name, song.artist, song.genre, song.data, )) songs.extend(additions) retval = { 'songs': [self.__format_song(song) for song in songs], } # Fetch any optional extras per-game, return retval.update(self.__format_extras()) return retval
def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]: since = params.get('since') until = params.get('until') # Fetch the scores records: List[Tuple[UserID, Score]] = [] if idtype == APIConstants.ID_TYPE_SERVER: # Because of the way this query works, we can't apply since/until to it directly. # If we did, it would miss higher scores earned before since or after until, and # incorrectly report records. records.extend(self.data.local.music.get_all_records(self.game, self.music_version)) elif idtype == APIConstants.ID_TYPE_SONG: if len(ids) == 1: songid = int(ids[0]) chart = None else: songid = int(ids[0]) chart = int(ids[1]) records.extend(self.data.local.music.get_all_scores(self.game, self.music_version, songid=songid, songchart=chart, since=since, until=until)) elif idtype == APIConstants.ID_TYPE_INSTANCE: songid = int(ids[0]) chart = int(ids[1]) cardid = ids[2] userid = self.data.local.user.from_cardid(cardid) if userid is not None: score = self.data.local.music.get_score(self.game, self.music_version, userid, songid, chart) if score is not None: records.append((userid, score)) elif idtype == APIConstants.ID_TYPE_CARD: users: Set[UserID] = set() for cardid in ids: userid = self.data.local.user.from_cardid(cardid) if userid is not None: # Don't duplicate loads for users with multiple card IDs if multiples # of those IDs are requested. if userid in users: continue users.add(userid) records.extend([(userid, score) for score in self.data.local.music.get_scores(self.game, self.music_version, userid, since=since, until=until)]) else: raise APIException('Invalid ID type!') # Now, fetch the users, and filter out scores belonging to orphaned users id_to_cards: Dict[UserID, List[str]] = {} retval: List[Dict[str, Any]] = [] for (userid, record) in records: # Postfilter for queries that can't filter. This will save on data transferred. if since is not None: if record.update < since: continue if until is not None: if record.update >= until: continue if userid not in id_to_cards: cards = self.data.local.user.get_cards(userid) if len(cards) == 0: # Can't add this user, skip the score continue id_to_cards[userid] = cards # Format the score and add it retval.append(self.__format_record(id_to_cards[userid], record)) return retval
def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str, Any]: requestdata = request.get_json() for expected in ['type', 'ids', 'objects']: if expected not in requestdata: raise APIException('Missing parameters for request.') for param in requestdata: if param not in ['type', 'ids', 'objects', 'since', 'until']: raise APIException('Unrecognized parameters for request.') args = copy.deepcopy(requestdata) del args['type'] del args['ids'] del args['objects'] if protoversion not in SUPPORTED_VERSIONS: # Don't know about this protocol version abort(501) # Figure out what games we support based on config, and map those. gamemapping = {} for (gameid, constant) in [ ('ddr', GameConstants.DDR), ('iidx', GameConstants.IIDX), ('jubeat', GameConstants.JUBEAT), ('museca', GameConstants.MUSECA), ('popnmusic', GameConstants.POPN_MUSIC), ('reflecbeat', GameConstants.REFLEC_BEAT), ('soundvoltex', GameConstants.SDVX), ]: if g.config.get('support', {}).get(constant, False): gamemapping[gameid] = constant game = gamemapping.get(requestgame) if game is None: # Don't support this game! abort(404) if requestversion[0] == 'o': omnimix = True requestversion = requestversion[1:] else: omnimix = False version = { GameConstants.DDR: { '12': VersionConstants.DDR_X2, '13': VersionConstants.DDR_X3_VS_2NDMIX, '14': VersionConstants.DDR_2013, '15': VersionConstants.DDR_2014, '16': VersionConstants.DDR_ACE, }, GameConstants.IIDX: { '20': VersionConstants.IIDX_TRICORO, '21': VersionConstants.IIDX_SPADA, '22': VersionConstants.IIDX_PENDUAL, '23': VersionConstants.IIDX_COPULA, '24': VersionConstants.IIDX_SINOBUZ, '25': VersionConstants.IIDX_CANNON_BALLERS, }, GameConstants.JUBEAT: { '5': VersionConstants.JUBEAT_SAUCER, '5a': VersionConstants.JUBEAT_SAUCER_FULFILL, '6': VersionConstants.JUBEAT_PROP, '7': VersionConstants.JUBEAT_QUBELL, '8': VersionConstants.JUBEAT_CLAN, }, GameConstants.MUSECA: { '1': VersionConstants.MUSECA, '1p': VersionConstants.MUSECA_1_PLUS, }, GameConstants.POPN_MUSIC: { '19': VersionConstants.POPN_MUSIC_TUNE_STREET, '20': VersionConstants.POPN_MUSIC_FANTASIA, '21': VersionConstants.POPN_MUSIC_SUNNY_PARK, '22': VersionConstants.POPN_MUSIC_LAPISTORIA, '23': VersionConstants.POPN_MUSIC_ECLALE, '24': VersionConstants.POPN_MUSIC_USANEKO, }, GameConstants.REFLEC_BEAT: { '1': VersionConstants.REFLEC_BEAT, '2': VersionConstants.REFLEC_BEAT_LIMELIGHT, # We don't support non-final COLETTE, so just return scores for # final colette to any network that asks. '3w': VersionConstants.REFLEC_BEAT_COLETTE, '3sp': VersionConstants.REFLEC_BEAT_COLETTE, '3su': VersionConstants.REFLEC_BEAT_COLETTE, '3a': VersionConstants.REFLEC_BEAT_COLETTE, '3as': VersionConstants.REFLEC_BEAT_COLETTE, # We don't support groovin'!!, so just return upper scores. '4': VersionConstants.REFLEC_BEAT_GROOVIN, '4u': VersionConstants.REFLEC_BEAT_GROOVIN, '5': VersionConstants.REFLEC_BEAT_VOLZZA, '5a': VersionConstants.REFLEC_BEAT_VOLZZA_2, '6': VersionConstants.REFLEC_BEAT_REFLESIA, }, GameConstants.SDVX: { '1': VersionConstants.SDVX_BOOTH, '2': VersionConstants.SDVX_INFINITE_INFECTION, '3': VersionConstants.SDVX_GRAVITY_WARS, '4': VersionConstants.SDVX_HEAVENLY_HAVEN, }, }.get(game, {}).get(requestversion) if version is None: # Don't support this version! abort(404) idtype = requestdata['type'] ids = requestdata['ids'] if idtype not in [ APIConstants.ID_TYPE_CARD, APIConstants.ID_TYPE_SONG, APIConstants.ID_TYPE_INSTANCE, APIConstants.ID_TYPE_SERVER ]: raise APIException('Invalid ID type provided!') if idtype == APIConstants.ID_TYPE_CARD and len(ids) == 0: raise APIException('Invalid number of IDs given!') if idtype == APIConstants.ID_TYPE_SONG and len(ids) not in [1, 2]: raise APIException('Invalid number of IDs given!') if idtype == APIConstants.ID_TYPE_INSTANCE and len(ids) != 3: raise APIException('Invalid number of IDs given!') if idtype == APIConstants.ID_TYPE_SERVER and len(ids) != 0: raise APIException('Invalid number of IDs given!') responsedata = {} for obj in requestdata['objects']: handler = { 'records': RecordsObject, 'profile': ProfileObject, 'statistics': StatisticsObject, 'catalog': CatalogObject, }.get(obj) if handler is None: # Don't support this object type abort(404) inst = handler(g.data, game, version, omnimix) try: fetchmethod = getattr(inst, f'fetch_{protoversion}') except AttributeError: # Don't know how to handle this object for this version abort(501) responsedata[obj] = fetchmethod(idtype, ids, args) return responsedata
def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> Any: raise APIException('Object fetch not supported for this version!')