From 40a0817009104f6bd48010830ae69dcc54569c2d Mon Sep 17 00:00:00 2001 From: beerpsi Date: Thu, 14 Mar 2024 14:44:32 +0000 Subject: [PATCH] CHUNITHM & O.N.G.E.K.I.: Handle userRatingBase*List (#113) These tables are not used by the game, but are useful for anyone wanting to develop a web UI showing what the player's rating consists of. As such, instead of storing them in JSON columns, I've split them out, one row per each entry. Reviewed-on: https://gitea.tendokyu.moe/Hay1tsme/artemis/pulls/113 Co-authored-by: beerpsi Co-committed-by: beerpsi --- .../6a7e8277763b_gekichu_rating_tables.py | 56 +++++++++++++++++++ titles/chuni/base.py | 11 ++++ titles/chuni/schema/profile.py | 51 +++++++++++++++-- titles/ongeki/base.py | 18 ++++++ titles/ongeki/schema/profile.py | 47 +++++++++++++++- 5 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 core/data/alembic/versions/6a7e8277763b_gekichu_rating_tables.py diff --git a/core/data/alembic/versions/6a7e8277763b_gekichu_rating_tables.py b/core/data/alembic/versions/6a7e8277763b_gekichu_rating_tables.py new file mode 100644 index 0000000..2d4074a --- /dev/null +++ b/core/data/alembic/versions/6a7e8277763b_gekichu_rating_tables.py @@ -0,0 +1,56 @@ +"""GekiChu rating tables + +Revision ID: 6a7e8277763b +Revises: d8950c7ce2fc +Create Date: 2024-03-13 12:18:53.210018 + +""" +from alembic import op +from sqlalchemy import Column, Integer, String + + +# revision identifiers, used by Alembic. +revision = '6a7e8277763b' +down_revision = 'd8950c7ce2fc' +branch_labels = None +depends_on = None + +GEKICHU_RATING_TABLE_NAMES = [ + "chuni_profile_rating", + "ongeki_profile_rating", +] + +def upgrade(): + for table_name in GEKICHU_RATING_TABLE_NAMES: + op.create_table( + table_name, + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("version", Integer, nullable=False), + Column("type", String(255), nullable=False), + Column("index", Integer, nullable=False), + Column("musicId", Integer), + Column("difficultId", Integer), + Column("romVersionCode", Integer), + Column("score", Integer), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + table_name, + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + op.create_unique_constraint( + f"{table_name}_uk", + table_name, + ["user", "version", "type", "index"], + ) + + +def downgrade(): + for table_name in GEKICHU_RATING_TABLE_NAMES: + op.drop_table(table_name) diff --git a/titles/chuni/base.py b/titles/chuni/base.py index 72d665e..c3b9ca2 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -925,6 +925,17 @@ class ChuniBase: for rp in upsert["userRecentPlayerList"]: pass + for rating_type in {"userRatingBaseList", "userRatingBaseHotList", "userRatingBaseNextList"}: + if rating_type not in upsert: + continue + + await self.data.profile.put_profile_rating( + user_id, + self.version, + rating_type, + upsert[rating_type], + ) + return {"returnCode": "1"} async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: diff --git a/titles/chuni/schema/profile.py b/titles/chuni/schema/profile.py index 849a5b3..9864928 100644 --- a/titles/chuni/schema/profile.py +++ b/titles/chuni/schema/profile.py @@ -1,10 +1,9 @@ from typing import Dict, List, Optional -from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_ -from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger -from sqlalchemy.engine.base import Connection +from sqlalchemy import Table, Column, UniqueConstraint, and_ +from sqlalchemy.types import Integer, String, Boolean, JSON, BigInteger from sqlalchemy.schema import ForeignKey from sqlalchemy.engine import Row -from sqlalchemy.sql import func, select +from sqlalchemy.sql import select, delete from sqlalchemy.dialects.mysql import insert from core.data.schema import BaseData, metadata @@ -393,6 +392,26 @@ team = Table( mysql_charset="utf8mb4", ) +rating = Table( + "chuni_profile_rating", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), + Column("version", Integer, nullable=False), + Column("type", String(255), nullable=False), + Column("index", Integer, nullable=False), + Column("musicId", Integer), + Column("difficultId", Integer), + Column("romVersionCode", Integer), + Column("score", Integer), + UniqueConstraint("user", "version", "type", "index", name="chuni_profile_rating_best_uk"), + mysql_charset="utf8mb4", +) + class ChuniProfileData(BaseData): async def update_name(self, user_id: int, new_name: str) -> bool: @@ -714,3 +733,27 @@ class ChuniProfileData(BaseData): return { "total_play_count": total_play_count } + + async def put_profile_rating( + self, + aime_id: int, + version: int, + rating_type: str, + rating_data: List[Dict], + ): + inserted_values = [ + {"user": aime_id, "version": version, "type": rating_type, "index": i, **x} + for (i, x) in enumerate(rating_data) + ] + sql = insert(rating).values(inserted_values) + update_dict = {x.name: x for x in sql.inserted if x.name != "id"} + sql = sql.on_duplicate_key_update(**update_dict) + result = await self.execute(sql) + + if result is None: + self.logger.warn( + f"put_profile_rating: Could not insert {rating_type}, aime_id: {aime_id}", + ) + return + + return result.lastrowid diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index d40ece8..ca5a38c 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -1067,6 +1067,24 @@ class OngekiBase: if "userKopList" in upsert: for x in upsert["userKopList"]: await self.data.profile.put_kop(user_id, x) + + for rating_type in { + "userRatingBaseBestList", + "userRatingBaseBestNewList", + "userRatingBaseHotList", + "userRatingBaseNextList", + "userRatingBaseNextNewList", + "userRatingBaseHotNextList", + }: + if rating_type not in upsert: + continue + + await self.data.profile.put_profile_rating( + user_id, + self.version, + rating_type, + upsert[rating_type], + ) return {"returnCode": 1, "apiName": "upsertUserAll"} diff --git a/titles/ongeki/schema/profile.py b/titles/ongeki/schema/profile.py index 1f6bcab..b42a0a3 100644 --- a/titles/ongeki/schema/profile.py +++ b/titles/ongeki/schema/profile.py @@ -246,6 +246,26 @@ rival = Table( mysql_charset="utf8mb4", ) +rating = Table( + "ongeki_profile_rating", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), + Column("version", Integer, nullable=False), + Column("type", String(255), nullable=False), + Column("index", Integer, nullable=False), + Column("musicId", Integer), + Column("difficultId", Integer), + Column("romVersionCode", Integer), + Column("score", Integer), + UniqueConstraint("user", "version", "type", "index", name="ongeki_profile_rating_best_uk"), + mysql_charset="utf8mb4", +) + class OngekiProfileData(BaseData): def __init__(self, cfg: CoreConfig, conn: Connection) -> None: @@ -508,10 +528,35 @@ class OngekiProfileData(BaseData): ) return None return result.lastrowid + async def delete_rival(self, aime_id: int, rival_id: int) -> Optional[int]: sql = delete(rival).where(rival.c.user==aime_id, rival.c.rivalUserId==rival_id) result = await self.execute(sql) if result is None: self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}") else: - return result.rowcount \ No newline at end of file + return result.rowcount + + async def put_profile_rating( + self, + aime_id: int, + version: int, + rating_type: str, + rating_data: List[Dict], + ): + inserted_values = [ + {"user": aime_id, "version": version, "type": rating_type, "index": i, **x} + for (i, x) in enumerate(rating_data) + ] + sql = insert(rating).values(inserted_values) + update_dict = {x.name: x for x in sql.inserted if x.name != "id"} + sql = sql.on_duplicate_key_update(**update_dict) + result = await self.execute(sql) + + if result is None: + self.logger.warn( + f"put_profile_rating_{rating_type}: Could not insert rating entries, aime_id: {aime_id}", + ) + return + + return result.lastrowid