Format code with black, include wrapper script to handle that, update linter checks to accomodate, fix a few miscelaneous type errors.

This commit is contained in:
Jennifer Taylor
2022-10-15 18:56:30 +00:00
parent d6cddebe88
commit 25e162042a
271 changed files with 68554 additions and 52909 deletions
+7 -3
View File
@@ -14,11 +14,15 @@ class BaseObject:
various fetch versions.
"""
def __init__(self, data: Data, game: GameConstants, version: int, omnimix: bool) -> None:
def __init__(
self, data: Data, game: GameConstants, version: int, omnimix: bool
) -> None:
self.data = data
self.game = game
self.version = version
self.omnimix = omnimix
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> Any:
raise APIException('Object fetch not supported for this version!')
def fetch_v1(
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
) -> Any:
raise APIException("Object fetch not supported for this version!")
+65 -59
View File
@@ -7,31 +7,30 @@ from bemani.data import Song
class CatalogObject(BaseObject):
def __format_ddr_song(self, song: Song) -> Dict[str, Any]:
groove = song.data.get_dict('groove')
groove = song.data.get_dict("groove")
return {
'editid': str(song.data.get_int('edit_id')),
'difficulty': song.data.get_int('difficulty'),
'bpm_min': song.data.get_int('bpm_min'),
'bpm_max': song.data.get_int('bpm_max'),
'category': str(song.data.get_int('category')),
'groove': {
'air': groove.get_int('air'),
'chaos': groove.get_int('chaos'),
'freeze': groove.get_int('freeze'),
'stream': groove.get_int('stream'),
'voltage': groove.get_int('voltage'),
"editid": str(song.data.get_int("edit_id")),
"difficulty": song.data.get_int("difficulty"),
"bpm_min": song.data.get_int("bpm_min"),
"bpm_max": song.data.get_int("bpm_max"),
"category": str(song.data.get_int("category")),
"groove": {
"air": groove.get_int("air"),
"chaos": groove.get_int("chaos"),
"freeze": groove.get_int("freeze"),
"stream": groove.get_int("stream"),
"voltage": groove.get_int("voltage"),
},
}
def __format_iidx_song(self, song: Song) -> Dict[str, Any]:
return {
'difficulty': song.data.get_int('difficulty'),
'bpm_min': song.data.get_int('bpm_min'),
'bpm_max': song.data.get_int('bpm_max'),
'notecount': song.data.get_int('notecount'),
'category': str(int(song.id / 1000)),
"difficulty": song.data.get_int("difficulty"),
"bpm_min": song.data.get_int("bpm_min"),
"bpm_max": song.data.get_int("bpm_max"),
"notecount": song.data.get_int("notecount"),
"category": str(int(song.id / 1000)),
}
def __format_jubeat_song(self, song: Song) -> Dict[str, Any]:
@@ -46,67 +45,69 @@ class CatalogObject(BaseObject):
6: VersionConstants.JUBEAT_PROP,
7: VersionConstants.JUBEAT_QUBELL,
8: VersionConstants.JUBEAT_CLAN,
9: VersionConstants.JUBEAT_FESTO
9: VersionConstants.JUBEAT_FESTO,
}.get(int(song.id / 10000000), VersionConstants.JUBEAT)
# Map the category to the version numbers defined on BEMAPI.
categorymapping = {
VersionConstants.JUBEAT: '1',
VersionConstants.JUBEAT_RIPPLES: '2',
VersionConstants.JUBEAT_RIPPLES_APPEND: '2a',
VersionConstants.JUBEAT_KNIT: '3',
VersionConstants.JUBEAT_KNIT_APPEND: '3a',
VersionConstants.JUBEAT_COPIOUS: '4',
VersionConstants.JUBEAT_COPIOUS_APPEND: '4a',
VersionConstants.JUBEAT_SAUCER: '5',
VersionConstants.JUBEAT_SAUCER_FULFILL: '5a',
VersionConstants.JUBEAT_PROP: '6',
VersionConstants.JUBEAT_QUBELL: '7',
VersionConstants.JUBEAT_CLAN: '8',
VersionConstants.JUBEAT_FESTO: '9',
VersionConstants.JUBEAT: "1",
VersionConstants.JUBEAT_RIPPLES: "2",
VersionConstants.JUBEAT_RIPPLES_APPEND: "2a",
VersionConstants.JUBEAT_KNIT: "3",
VersionConstants.JUBEAT_KNIT_APPEND: "3a",
VersionConstants.JUBEAT_COPIOUS: "4",
VersionConstants.JUBEAT_COPIOUS_APPEND: "4a",
VersionConstants.JUBEAT_SAUCER: "5",
VersionConstants.JUBEAT_SAUCER_FULFILL: "5a",
VersionConstants.JUBEAT_PROP: "6",
VersionConstants.JUBEAT_QUBELL: "7",
VersionConstants.JUBEAT_CLAN: "8",
VersionConstants.JUBEAT_FESTO: "9",
}
return {
'difficulty': song.data.get_int('difficulty'),
'category': categorymapping.get(song.data.get_int('version', defaultcategory), '1'),
'bpm_min': song.data.get_int('bpm_min'),
'bpm_max': song.data.get_int('bpm_max'),
"difficulty": song.data.get_int("difficulty"),
"category": categorymapping.get(
song.data.get_int("version", defaultcategory), "1"
),
"bpm_min": song.data.get_int("bpm_min"),
"bpm_max": song.data.get_int("bpm_max"),
}
def __format_museca_song(self, song: Song) -> Dict[str, Any]:
return {
'difficulty': song.data.get_int('difficulty'),
'bpm_min': song.data.get_int('bpm_min'),
'bpm_max': song.data.get_int('bpm_max'),
'limited': song.data.get_int('limited'),
"difficulty": song.data.get_int("difficulty"),
"bpm_min": song.data.get_int("bpm_min"),
"bpm_max": song.data.get_int("bpm_max"),
"limited": song.data.get_int("limited"),
}
def __format_popn_song(self, song: Song) -> Dict[str, Any]:
return {
'difficulty': song.data.get_int('difficulty'),
'category': song.data.get_str('category'),
"difficulty": song.data.get_int("difficulty"),
"category": song.data.get_str("category"),
}
def __format_reflec_song(self, song: Song) -> Dict[str, Any]:
return {
'difficulty': song.data.get_int('difficulty'),
'category': str(song.data.get_int('folder')),
'musicid': song.data.get_str('chart_id'),
"difficulty": song.data.get_int("difficulty"),
"category": str(song.data.get_int("folder")),
"musicid": song.data.get_str("chart_id"),
}
def __format_sdvx_song(self, song: Song) -> Dict[str, Any]:
return {
'difficulty': song.data.get_int('difficulty'),
'bpm_min': song.data.get_int('bpm_min'),
'bpm_max': song.data.get_int('bpm_max'),
'limited': song.data.get_int('limited'),
"difficulty": song.data.get_int("difficulty"),
"bpm_min": song.data.get_int("bpm_min"),
"bpm_max": song.data.get_int("bpm_max"),
"limited": song.data.get_int("limited"),
}
def __format_song(self, song: Song) -> Dict[str, Any]:
base = {
'song': str(song.id),
'chart': str(song.chart),
'title': song.name or "",
'artist': song.artist or "",
'genre': song.genre or "",
"song": str(song.id),
"chart": str(song.chart),
"title": song.name or "",
"artist": song.artist or "",
"genre": song.genre or "",
}
if self.game == GameConstants.DDR:
@@ -198,7 +199,7 @@ class CatalogObject(BaseObject):
"type": item.type[3:],
}
for item in items
if item.type in ['qp_body', 'qp_face', 'qp_hair', 'qp_hand', 'qp_head']
if item.type in ["qp_body", "qp_face", "qp_hair", "qp_hand", "qp_head"]
],
}
@@ -222,17 +223,22 @@ class CatalogObject(BaseObject):
else:
return self.version
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]:
def fetch_v1(
self, idtype: APIConstants, 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!',
"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 == VersionConstants.JUBEAT_CLAN:
if (
self.game == GameConstants.JUBEAT
and self.version == VersionConstants.JUBEAT_CLAN
):
# 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
@@ -255,7 +261,7 @@ class CatalogObject(BaseObject):
)
songs.extend(additions)
retval = {
'songs': [self.__format_song(song) for song in songs],
"songs": [self.__format_song(song) for song in songs],
}
# Fetch any optional extras per-game, return
+41 -27
View File
@@ -7,24 +7,23 @@ from bemani.data import UserID
class ProfileObject(BaseObject):
def __format_ddr_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
return {
'area': profile.get_int('area', -1) if exact else -1,
"area": profile.get_int("area", -1) if exact else -1,
}
def __format_iidx_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
qpro = profile.get_dict('qpro')
qpro = profile.get_dict("qpro")
return {
'area': profile.get_int('pid', -1),
'qpro': {
'head': qpro.get_int('head', -1) if exact else -1,
'hair': qpro.get_int('hair', -1) if exact else -1,
'face': qpro.get_int('face', -1) if exact else -1,
'body': qpro.get_int('body', -1) if exact else -1,
'hand': qpro.get_int('hand', -1) if exact else -1,
}
"area": profile.get_int("pid", -1),
"qpro": {
"head": qpro.get_int("head", -1) if exact else -1,
"hair": qpro.get_int("hair", -1) if exact else -1,
"face": qpro.get_int("face", -1) if exact else -1,
"body": qpro.get_int("body", -1) if exact else -1,
"hand": qpro.get_int("hand", -1) if exact else -1,
},
}
def __format_jubeat_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
@@ -35,25 +34,27 @@ class ProfileObject(BaseObject):
def __format_popn_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
return {
'character': profile.get_int('chara', -1) if exact else -1,
"character": profile.get_int("chara", -1) if exact else -1,
}
def __format_reflec_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
return {
'icon': profile.get_dict('config').get_int('icon_id', -1) if exact else -1,
"icon": profile.get_dict("config").get_int("icon_id", -1) if exact else -1,
}
def __format_sdvx_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
return {}
def __format_profile(self, cardids: List[str], profile: Profile, settings: ValidatedDict, exact: bool) -> Dict[str, Any]:
def __format_profile(
self, cardids: List[str], profile: Profile, settings: ValidatedDict, exact: bool
) -> Dict[str, Any]:
base = {
'name': profile.get_str('name'),
'cards': cardids,
'registered': settings.get_int('first_play_timestamp', -1),
'updated': settings.get_int('last_play_timestamp', -1),
'plays': settings.get_int('total_plays', -1),
'match': 'exact' if exact else 'partial',
"name": profile.get_str("name"),
"cards": cardids,
"registered": settings.get_int("first_play_timestamp", -1),
"updated": settings.get_int("last_play_timestamp", -1),
"plays": settings.get_int("total_plays", -1),
"match": "exact" if exact else "partial",
}
if self.game == GameConstants.DDR:
@@ -73,19 +74,23 @@ class ProfileObject(BaseObject):
return base
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
def fetch_v1(
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
) -> List[Dict[str, Any]]:
# Fetch the profiles
profiles: List[Tuple[UserID, Profile]] = []
if idtype == APIConstants.ID_TYPE_SERVER:
profiles.extend(self.data.local.user.get_all_profiles(self.game, self.version))
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!',
"Unsupported ID for lookup!",
405,
)
elif idtype == APIConstants.ID_TYPE_INSTANCE:
raise APIException(
'Unsupported ID for lookup!',
"Unsupported ID for lookup!",
405,
)
elif idtype == APIConstants.ID_TYPE_CARD:
@@ -103,11 +108,13 @@ class ProfileObject(BaseObject):
# 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)
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!')
raise APIException("Invalid ID type!")
# Now, fetch the users, and filter out profiles belonging to orphaned users
retval: List[Dict[str, Any]] = []
@@ -126,6 +133,13 @@ class ProfileObject(BaseObject):
if settings is None:
settings = ValidatedDict({})
retval.append(self.__format_profile(id_to_cards[userid], profile, settings, profile.version == self.version))
retval.append(
self.__format_profile(
id_to_cards[userid],
profile,
settings,
profile.version == self.version,
)
)
return retval
+156 -131
View File
@@ -7,15 +7,14 @@ from bemani.data import Score, UserID
class RecordsObject(BaseObject):
def __format_ddr_record(self, record: Score) -> Dict[str, Any]:
halo = {
DBConstants.DDR_HALO_NONE: 'none',
DBConstants.DDR_HALO_GOOD_FULL_COMBO: 'gfc',
DBConstants.DDR_HALO_GREAT_FULL_COMBO: 'fc',
DBConstants.DDR_HALO_PERFECT_FULL_COMBO: 'pfc',
DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO: 'mfc',
}.get(record.data.get_int('halo'), 'none')
DBConstants.DDR_HALO_NONE: "none",
DBConstants.DDR_HALO_GOOD_FULL_COMBO: "gfc",
DBConstants.DDR_HALO_GREAT_FULL_COMBO: "fc",
DBConstants.DDR_HALO_PERFECT_FULL_COMBO: "pfc",
DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO: "mfc",
}.get(record.data.get_int("halo"), "none")
rank = {
DBConstants.DDR_RANK_AAA: "AAA",
DBConstants.DDR_RANK_AA_PLUS: "AA+",
@@ -33,175 +32,175 @@ class RecordsObject(BaseObject):
DBConstants.DDR_RANK_D_PLUS: "D+",
DBConstants.DDR_RANK_D: "D",
DBConstants.DDR_RANK_E: "E",
}.get(record.data.get_int('rank'), 'E')
}.get(record.data.get_int("rank"), "E")
if self.version == VersionConstants.DDR_ACE:
# DDR Ace is specia
ghost = [int(x) for x in record.data.get_str('ghost')]
ghost = [int(x) for x in record.data.get_str("ghost")]
else:
if 'trace' not in record.data:
if "trace" not in record.data:
ghost = []
else:
ghost = record.data.get_int_array('trace', len(record.data['trace']))
ghost = record.data.get_int_array("trace", len(record.data["trace"]))
return {
'rank': rank,
'halo': halo,
'combo': record.data.get_int('combo'),
'ghost': ghost,
"rank": rank,
"halo": halo,
"combo": record.data.get_int("combo"),
"ghost": ghost,
}
def __format_iidx_record(self, record: Score) -> Dict[str, Any]:
status = {
DBConstants.IIDX_CLEAR_STATUS_NO_PLAY: 'np',
DBConstants.IIDX_CLEAR_STATUS_FAILED: 'failed',
DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR: 'ac',
DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR: 'ec',
DBConstants.IIDX_CLEAR_STATUS_CLEAR: 'nc',
DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR: 'hc',
DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR: 'exhc',
DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO: 'fc',
}.get(record.data.get_int('clear_status'), 'np')
DBConstants.IIDX_CLEAR_STATUS_NO_PLAY: "np",
DBConstants.IIDX_CLEAR_STATUS_FAILED: "failed",
DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR: "ac",
DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR: "ec",
DBConstants.IIDX_CLEAR_STATUS_CLEAR: "nc",
DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR: "hc",
DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR: "exhc",
DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO: "fc",
}.get(record.data.get_int("clear_status"), "np")
return {
'status': status,
'miss': record.data.get_int('miss_count', -1),
'ghost': [b for b in record.data.get_bytes('ghost')],
'pgreat': record.data.get_int('pgreats', -1),
'great': record.data.get_int('greats', -1),
"status": status,
"miss": record.data.get_int("miss_count", -1),
"ghost": [b for b in record.data.get_bytes("ghost")],
"pgreat": record.data.get_int("pgreats", -1),
"great": record.data.get_int("greats", -1),
}
def __format_jubeat_record(self, record: Score) -> Dict[str, Any]:
status = {
DBConstants.JUBEAT_PLAY_MEDAL_FAILED: 'failed',
DBConstants.JUBEAT_PLAY_MEDAL_CLEARED: 'cleared',
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: 'nfc',
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO: 'fc',
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: 'nec',
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT: 'exc',
}.get(record.data.get_int('medal'), 'failed')
if 'ghost' not in record.data:
DBConstants.JUBEAT_PLAY_MEDAL_FAILED: "failed",
DBConstants.JUBEAT_PLAY_MEDAL_CLEARED: "cleared",
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: "nfc",
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO: "fc",
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: "nec",
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT: "exc",
}.get(record.data.get_int("medal"), "failed")
if "ghost" not in record.data:
ghost: List[int] = []
else:
ghost = record.data.get_int_array('ghost', len(record.data['ghost']))
ghost = record.data.get_int_array("ghost", len(record.data["ghost"]))
return {
'status': status,
'combo': record.data.get_int('combo', -1),
'ghost': ghost,
'music_rate': record.data.get_int('music_rate'),
"status": status,
"combo": record.data.get_int("combo", -1),
"ghost": ghost,
"music_rate": record.data.get_int("music_rate"),
}
def __format_museca_record(self, record: Score) -> Dict[str, Any]:
rank = {
DBConstants.MUSECA_GRADE_DEATH: 'death',
DBConstants.MUSECA_GRADE_POOR: 'poor',
DBConstants.MUSECA_GRADE_MEDIOCRE: 'mediocre',
DBConstants.MUSECA_GRADE_GOOD: 'good',
DBConstants.MUSECA_GRADE_GREAT: 'great',
DBConstants.MUSECA_GRADE_EXCELLENT: 'excellent',
DBConstants.MUSECA_GRADE_SUPERB: 'superb',
DBConstants.MUSECA_GRADE_MASTERPIECE: 'masterpiece',
DBConstants.MUSECA_GRADE_PERFECT: 'perfect'
}.get(record.data.get_int('grade'), 'death')
DBConstants.MUSECA_GRADE_DEATH: "death",
DBConstants.MUSECA_GRADE_POOR: "poor",
DBConstants.MUSECA_GRADE_MEDIOCRE: "mediocre",
DBConstants.MUSECA_GRADE_GOOD: "good",
DBConstants.MUSECA_GRADE_GREAT: "great",
DBConstants.MUSECA_GRADE_EXCELLENT: "excellent",
DBConstants.MUSECA_GRADE_SUPERB: "superb",
DBConstants.MUSECA_GRADE_MASTERPIECE: "masterpiece",
DBConstants.MUSECA_GRADE_PERFECT: "perfect",
}.get(record.data.get_int("grade"), "death")
status = {
DBConstants.MUSECA_CLEAR_TYPE_FAILED: 'failed',
DBConstants.MUSECA_CLEAR_TYPE_CLEARED: 'cleared',
DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO: 'fc',
}.get(record.data.get_int('clear_type'), 'failed')
DBConstants.MUSECA_CLEAR_TYPE_FAILED: "failed",
DBConstants.MUSECA_CLEAR_TYPE_CLEARED: "cleared",
DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO: "fc",
}.get(record.data.get_int("clear_type"), "failed")
return {
'rank': rank,
'status': status,
'combo': record.data.get_int('combo', -1),
'buttonrate': record.data.get_dict('stats').get_int('btn_rate'),
'longrate': record.data.get_dict('stats').get_int('long_rate'),
'volrate': record.data.get_dict('stats').get_int('vol_rate'),
"rank": rank,
"status": status,
"combo": record.data.get_int("combo", -1),
"buttonrate": record.data.get_dict("stats").get_int("btn_rate"),
"longrate": record.data.get_dict("stats").get_int("long_rate"),
"volrate": record.data.get_dict("stats").get_int("vol_rate"),
}
def __format_popn_record(self, record: Score) -> Dict[str, Any]:
status = {
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: 'cf',
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: 'df',
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: 'sf',
DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: 'ec',
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: 'cc',
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: 'dc',
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: 'sc',
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: 'cfc',
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: 'dfc',
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: 'sfc',
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT: 'p',
}.get(record.data.get_int('medal'), 'cf')
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: "cf",
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: "df",
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: "sf",
DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: "ec",
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: "cc",
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: "dc",
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: "sc",
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: "cfc",
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: "dfc",
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: "sfc",
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT: "p",
}.get(record.data.get_int("medal"), "cf")
return {
'status': status,
'combo': record.data.get_int('combo', -1),
"status": status,
"combo": record.data.get_int("combo", -1),
}
def __format_reflec_record(self, record: Score) -> Dict[str, Any]:
status = {
DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: 'np',
DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED: 'failed',
DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED: 'cleared',
DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: 'hc',
DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: 'shc',
}.get(record.data.get_int('clear_type'), 'np')
DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: "np",
DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED: "failed",
DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED: "cleared",
DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: "hc",
DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: "shc",
}.get(record.data.get_int("clear_type"), "np")
halo = {
DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE: 'none',
DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: 'ac',
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: 'fc',
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: 'fcaj',
}.get(record.data.get_int('combo_type'), 'none')
DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE: "none",
DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: "ac",
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: "fc",
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: "fcaj",
}.get(record.data.get_int("combo_type"), "none")
return {
'rate': record.data.get_int('achievement_rate'),
'status': status,
'halo': halo,
'combo': record.data.get_int('combo', -1),
'miss': record.data.get_int('miss_count', -1),
"rate": record.data.get_int("achievement_rate"),
"status": status,
"halo": halo,
"combo": record.data.get_int("combo", -1),
"miss": record.data.get_int("miss_count", -1),
}
def __format_sdvx_record(self, record: Score) -> Dict[str, Any]:
status = {
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY: 'np',
DBConstants.SDVX_CLEAR_TYPE_FAILED: 'failed',
DBConstants.SDVX_CLEAR_TYPE_CLEAR: 'cleared',
DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR: 'hc',
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: 'uc',
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: 'puc',
}.get(record.data.get_int('clear_type'), 'np')
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY: "np",
DBConstants.SDVX_CLEAR_TYPE_FAILED: "failed",
DBConstants.SDVX_CLEAR_TYPE_CLEAR: "cleared",
DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR: "hc",
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: "uc",
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: "puc",
}.get(record.data.get_int("clear_type"), "np")
rank = {
DBConstants.SDVX_GRADE_NO_PLAY: 'E',
DBConstants.SDVX_GRADE_D: 'D',
DBConstants.SDVX_GRADE_C: 'C',
DBConstants.SDVX_GRADE_B: 'B',
DBConstants.SDVX_GRADE_A: 'A',
DBConstants.SDVX_GRADE_A_PLUS: 'A+',
DBConstants.SDVX_GRADE_AA: 'AA',
DBConstants.SDVX_GRADE_AA_PLUS: 'AA+',
DBConstants.SDVX_GRADE_AAA: 'AAA',
DBConstants.SDVX_GRADE_AAA_PLUS: 'AAA+',
DBConstants.SDVX_GRADE_S: 'S',
}.get(record.data.get_int('grade'), 'E')
DBConstants.SDVX_GRADE_NO_PLAY: "E",
DBConstants.SDVX_GRADE_D: "D",
DBConstants.SDVX_GRADE_C: "C",
DBConstants.SDVX_GRADE_B: "B",
DBConstants.SDVX_GRADE_A: "A",
DBConstants.SDVX_GRADE_A_PLUS: "A+",
DBConstants.SDVX_GRADE_AA: "AA",
DBConstants.SDVX_GRADE_AA_PLUS: "AA+",
DBConstants.SDVX_GRADE_AAA: "AAA",
DBConstants.SDVX_GRADE_AAA_PLUS: "AAA+",
DBConstants.SDVX_GRADE_S: "S",
}.get(record.data.get_int("grade"), "E")
return {
'status': status,
'rank': rank,
'combo': record.data.get_int('combo', -1),
'buttonrate': record.data.get_dict('stats').get_int('btn_rate'),
'longrate': record.data.get_dict('stats').get_int('long_rate'),
'volrate': record.data.get_dict('stats').get_int('vol_rate'),
"status": status,
"rank": rank,
"combo": record.data.get_int("combo", -1),
"buttonrate": record.data.get_dict("stats").get_int("btn_rate"),
"longrate": record.data.get_dict("stats").get_int("long_rate"),
"volrate": record.data.get_dict("stats").get_int("vol_rate"),
}
def __format_record(self, cardids: List[str], record: Score) -> Dict[str, Any]:
base = {
'cards': cardids,
'song': str(record.id),
'chart': str(record.chart),
'points': record.points,
'timestamp': record.timestamp,
'updated': record.update,
"cards": cardids,
"song": str(record.id),
"chart": str(record.chart),
"points": record.points,
"timestamp": record.timestamp,
"updated": record.update,
}
if self.game == GameConstants.DDR:
@@ -231,9 +230,11 @@ class RecordsObject(BaseObject):
else:
return self.version
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
since = params.get('since')
until = params.get('until')
def fetch_v1(
self, idtype: APIConstants, 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]] = []
@@ -241,7 +242,9 @@ class RecordsObject(BaseObject):
# 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))
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])
@@ -249,14 +252,25 @@ class RecordsObject(BaseObject):
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))
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)
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:
@@ -270,9 +284,20 @@ class RecordsObject(BaseObject):
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)])
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!')
raise APIException("Invalid ID type!")
# Now, fetch the users, and filter out scores belonging to orphaned users
id_to_cards: Dict[UserID, List[str]] = {}
+102 -54
View File
@@ -7,20 +7,21 @@ from bemani.data import Attempt, UserID
class StatisticsObject(BaseObject):
def __format_statistics(self, stats: Dict[str, Any]) -> Dict[str, Any]:
return {
'cards': [],
'song': str(stats['id']),
'chart': str(stats['chart']),
'plays': stats.get('plays', -1),
'clears': stats.get('clears', -1),
'combos': stats.get('combos', -1),
"cards": [],
"song": str(stats["id"]),
"chart": str(stats["chart"]),
"plays": stats.get("plays", -1),
"clears": stats.get("clears", -1),
"combos": stats.get("combos", -1),
}
def __format_user_statistics(self, cardids: List[str], stats: Dict[str, Any]) -> Dict[str, Any]:
def __format_user_statistics(
self, cardids: List[str], stats: Dict[str, Any]
) -> Dict[str, Any]:
base = self.__format_statistics(stats)
base['cards'] = cardids
base["cards"] = cardids
return base
@property
@@ -42,11 +43,20 @@ class StatisticsObject(BaseObject):
}:
return True
if self.game == GameConstants.IIDX:
return attempt.data.get_int('clear_status') != DBConstants.IIDX_CLEAR_STATUS_NO_PLAY
return (
attempt.data.get_int("clear_status")
!= DBConstants.IIDX_CLEAR_STATUS_NO_PLAY
)
if self.game == GameConstants.REFLEC_BEAT:
return attempt.data.get_int('clear_type') != DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY
return (
attempt.data.get_int("clear_type")
!= DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY
)
if self.game == GameConstants.SDVX:
return attempt.data.get_int('clear_type') != DBConstants.SDVX_CLEAR_TYPE_NO_PLAY
return (
attempt.data.get_int("clear_type")
!= DBConstants.SDVX_CLEAR_TYPE_NO_PLAY
)
return False
@@ -55,29 +65,39 @@ class StatisticsObject(BaseObject):
return False
if self.game == GameConstants.DDR:
return attempt.data.get_int('rank') != DBConstants.DDR_RANK_E
return attempt.data.get_int("rank") != DBConstants.DDR_RANK_E
if self.game == GameConstants.IIDX:
return attempt.data.get_int('clear_status') != DBConstants.IIDX_CLEAR_STATUS_FAILED
return (
attempt.data.get_int("clear_status")
!= DBConstants.IIDX_CLEAR_STATUS_FAILED
)
if self.game == GameConstants.JUBEAT:
return attempt.data.get_int('medal') != DBConstants.JUBEAT_PLAY_MEDAL_FAILED
return attempt.data.get_int("medal") != DBConstants.JUBEAT_PLAY_MEDAL_FAILED
if self.game == GameConstants.MUSECA:
return attempt.data.get_int('clear_type') != DBConstants.MUSECA_CLEAR_TYPE_FAILED
return (
attempt.data.get_int("clear_type")
!= DBConstants.MUSECA_CLEAR_TYPE_FAILED
)
if self.game == GameConstants.POPN_MUSIC:
return attempt.data.get_int('medal') not in [
return attempt.data.get_int("medal") not in [
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED,
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED,
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED,
]
if self.game == GameConstants.REFLEC_BEAT:
return attempt.data.get_int('clear_type') != DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
if self.game == GameConstants.SDVX:
return (
attempt.data.get_int('grade') != DBConstants.SDVX_GRADE_NO_PLAY and
attempt.data.get_int('clear_type') not in [
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY,
DBConstants.SDVX_CLEAR_TYPE_FAILED,
]
attempt.data.get_int("clear_type")
!= DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
)
if self.game == GameConstants.SDVX:
return attempt.data.get_int(
"grade"
) != DBConstants.SDVX_GRADE_NO_PLAY and attempt.data.get_int(
"clear_type"
) not in [
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY,
DBConstants.SDVX_CLEAR_TYPE_FAILED,
]
return False
@@ -86,31 +106,37 @@ class StatisticsObject(BaseObject):
return False
if self.game == GameConstants.DDR:
return attempt.data.get_int('halo') != DBConstants.DDR_HALO_NONE
return attempt.data.get_int("halo") != DBConstants.DDR_HALO_NONE
if self.game == GameConstants.IIDX:
return attempt.data.get_int('clear_status') == DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO
return (
attempt.data.get_int("clear_status")
== DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO
)
if self.game == GameConstants.JUBEAT:
return attempt.data.get_int('medal') in [
return attempt.data.get_int("medal") in [
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO,
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT,
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT,
]
if self.game == GameConstants.MUSECA:
return attempt.data.get_int('clear_type') == DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO
return (
attempt.data.get_int("clear_type")
== DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO
)
if self.game == GameConstants.POPN_MUSIC:
return attempt.data.get_int('medal') in [
return attempt.data.get_int("medal") in [
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO,
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO,
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO,
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT,
]
if self.game == GameConstants.REFLEC_BEAT:
return attempt.data.get_int('combo_type') in [
return attempt.data.get_int("combo_type") in [
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO,
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST,
]
if self.game == GameConstants.SDVX:
return attempt.data.get_int('clear_type') in [
return attempt.data.get_int("clear_type") in [
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN,
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN,
]
@@ -125,29 +151,31 @@ class StatisticsObject(BaseObject):
stats[attempt.id] = {}
if attempt.chart not in stats[attempt.id]:
stats[attempt.id][attempt.chart] = {
'plays': 0,
'clears': 0,
'combos': 0,
"plays": 0,
"clears": 0,
"combos": 0,
}
if self.__is_play(attempt):
stats[attempt.id][attempt.chart]['plays'] += 1
stats[attempt.id][attempt.chart]["plays"] += 1
if self.__is_clear(attempt):
stats[attempt.id][attempt.chart]['clears'] += 1
stats[attempt.id][attempt.chart]["clears"] += 1
if self.__is_combo(attempt):
stats[attempt.id][attempt.chart]['combos'] += 1
stats[attempt.id][attempt.chart]["combos"] += 1
retval = []
for songid in stats:
for songchart in stats[songid]:
stat = stats[songid][songchart]
stat['id'] = songid
stat['chart'] = songchart
stat["id"] = songid
stat["chart"] = songchart
retval.append(self.__format_statistics(stat))
return retval
def __aggregate_local(self, cards: Dict[int, List[str]], attempts: List[Tuple[UserID, Attempt]]) -> List[Dict[str, Any]]:
def __aggregate_local(
self, cards: Dict[int, List[str]], attempts: List[Tuple[UserID, Attempt]]
) -> List[Dict[str, Any]]:
stats: Dict[UserID, Dict[int, Dict[int, Dict[str, int]]]] = {}
for (userid, attempt) in attempts:
@@ -157,36 +185,43 @@ class StatisticsObject(BaseObject):
stats[userid][attempt.id] = {}
if attempt.chart not in stats[userid][attempt.id]:
stats[userid][attempt.id][attempt.chart] = {
'plays': 0,
'clears': 0,
'combos': 0,
"plays": 0,
"clears": 0,
"combos": 0,
}
if self.__is_play(attempt):
stats[userid][attempt.id][attempt.chart]['plays'] += 1
stats[userid][attempt.id][attempt.chart]["plays"] += 1
if self.__is_clear(attempt):
stats[userid][attempt.id][attempt.chart]['clears'] += 1
stats[userid][attempt.id][attempt.chart]["clears"] += 1
if self.__is_combo(attempt):
stats[userid][attempt.id][attempt.chart]['combos'] += 1
stats[userid][attempt.id][attempt.chart]["combos"] += 1
retval = []
for userid in stats:
for songid in stats[userid]:
for songchart in stats[userid][songid]:
stat = stats[userid][songid][songchart]
stat['id'] = songid
stat['chart'] = songchart
stat["id"] = songid
stat["chart"] = songchart
retval.append(self.__format_user_statistics(cards[userid], stat))
return retval
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
def fetch_v1(
self, idtype: APIConstants, 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)]
[
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:
@@ -196,7 +231,12 @@ class StatisticsObject(BaseObject):
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)]
[
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])
@@ -206,7 +246,13 @@ class StatisticsObject(BaseObject):
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)
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]] = {}
@@ -221,10 +267,12 @@ class StatisticsObject(BaseObject):
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)
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!')
raise APIException("Invalid ID type!")
return retval