1
0
mirror of synced 2024-11-14 09:47:46 +01:00

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 <beerpsi@duck.com>
Co-committed-by: beerpsi <beerpsi@duck.com>
This commit is contained in:
beerpsi 2024-03-14 14:44:32 +00:00 committed by Hay1tsme
parent 346f82a32a
commit 40a0817009
5 changed files with 178 additions and 5 deletions

View File

@ -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)

View File

@ -925,6 +925,17 @@ class ChuniBase:
for rp in upsert["userRecentPlayerList"]: for rp in upsert["userRecentPlayerList"]:
pass 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"} return {"returnCode": "1"}
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:

View File

@ -1,10 +1,9 @@
from typing import Dict, List, Optional from typing import Dict, List, Optional
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_ from sqlalchemy import Table, Column, UniqueConstraint, and_
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger from sqlalchemy.types import Integer, String, Boolean, JSON, BigInteger
from sqlalchemy.engine.base import Connection
from sqlalchemy.schema import ForeignKey from sqlalchemy.schema import ForeignKey
from sqlalchemy.engine import Row from sqlalchemy.engine import Row
from sqlalchemy.sql import func, select from sqlalchemy.sql import select, delete
from sqlalchemy.dialects.mysql import insert from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata from core.data.schema import BaseData, metadata
@ -393,6 +392,26 @@ team = Table(
mysql_charset="utf8mb4", 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): class ChuniProfileData(BaseData):
async def update_name(self, user_id: int, new_name: str) -> bool: async def update_name(self, user_id: int, new_name: str) -> bool:
@ -714,3 +733,27 @@ class ChuniProfileData(BaseData):
return { return {
"total_play_count": total_play_count "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

View File

@ -1068,6 +1068,24 @@ class OngekiBase:
for x in upsert["userKopList"]: for x in upsert["userKopList"]:
await self.data.profile.put_kop(user_id, x) 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"} return {"returnCode": 1, "apiName": "upsertUserAll"}
async def handle_get_user_rival_api_request(self, data: Dict) -> Dict: async def handle_get_user_rival_api_request(self, data: Dict) -> Dict:

View File

@ -246,6 +246,26 @@ rival = Table(
mysql_charset="utf8mb4", 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): class OngekiProfileData(BaseData):
def __init__(self, cfg: CoreConfig, conn: Connection) -> None: def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
@ -508,6 +528,7 @@ class OngekiProfileData(BaseData):
) )
return None return None
return result.lastrowid return result.lastrowid
async def delete_rival(self, aime_id: int, rival_id: int) -> Optional[int]: 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) sql = delete(rival).where(rival.c.user==aime_id, rival.c.rivalUserId==rival_id)
result = await self.execute(sql) result = await self.execute(sql)
@ -515,3 +536,27 @@ class OngekiProfileData(BaseData):
self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}") self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}")
else: else:
return result.rowcount 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