Initial D THE ARCADE S2 support added

This commit is contained in:
Dniel97
2023-10-01 03:54:23 +02:00
parent 38c1c31cf5
commit e0265485ff
28 changed files with 244427 additions and 29 deletions

964
titles/idac/schema/item.py Normal file
View File

@@ -0,0 +1,964 @@
from typing import Dict, Optional, List
from sqlalchemy import (
Table,
Column,
UniqueConstraint,
PrimaryKeyConstraint,
and_,
update,
)
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
from sqlalchemy.schema import ForeignKey
from sqlalchemy.engine import Row
from sqlalchemy.sql import func, select
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
car = Table(
"idac_user_car",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("version", Integer, nullable=False),
Column("car_id", Integer),
Column("style_car_id", Integer),
Column("color", Integer),
Column("bureau", Integer),
Column("kana", Integer),
Column("s_no", Integer),
Column("l_no", Integer),
Column("car_flag", Integer),
Column("tune_point", Integer),
Column("tune_level", Integer, server_default="1"),
Column("tune_parts", Integer),
Column("infinity_tune", Integer, server_default="0"),
Column("online_vs_win", Integer, server_default="0"),
Column(
"pickup_seq", Integer, server_default="1"
), # the order in which the car was picked up
Column(
"purchase_seq", Integer, server_default="1"
), # the order in which the car was purchased
Column("color_stock_list", String(32)),
Column("color_stock_new_list", String(32)),
Column("parts_stock_list", String(48)),
Column("parts_stock_new_list", String(48)),
Column("parts_set_equip_list", String(48)),
Column("parts_list", JSON),
Column("equip_parts_count", Integer, server_default="0"),
Column("total_car_parts_count", Integer, server_default="0"),
Column("use_count", Integer, server_default="0"),
Column("story_use_count", Integer, server_default="0"),
Column("timetrial_use_count", Integer, server_default="0"),
Column("vs_use_count", Integer, server_default="0"),
Column("net_vs_use_count", Integer, server_default="0"),
Column("theory_use_count", Integer, server_default="0"),
Column("car_mileage", Integer, server_default="0"),
UniqueConstraint("user", "version", "style_car_id", name="idac_user_car_uk"),
mysql_charset="utf8mb4",
)
ticket = Table(
"idac_user_ticket",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("ticket_id", Integer),
Column("ticket_cnt", Integer),
UniqueConstraint("user", "ticket_id", name="idac_user_ticket_uk"),
mysql_charset="utf8mb4",
)
story = Table(
"idac_user_story",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("story_type", Integer),
Column("chapter", Integer),
Column("loop_count", Integer, server_default="1"),
UniqueConstraint("user", "chapter", name="idac_user_story_uk"),
mysql_charset="utf8mb4",
)
episode = Table(
"idac_user_story_episode",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("chapter", Integer),
Column("episode", Integer),
Column("play_status", Integer),
UniqueConstraint("user", "chapter", "episode", name="idac_user_story_episode_uk"),
mysql_charset="utf8mb4",
)
difficulty = Table(
"idac_user_story_episode_difficulty",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("episode", Integer),
Column("difficulty", Integer),
Column("play_count", Integer),
Column("clear_count", Integer),
Column("play_status", Integer),
Column("play_score", Integer),
UniqueConstraint(
"user", "episode", "difficulty", name="idac_user_story_episode_difficulty_uk"
),
mysql_charset="utf8mb4",
)
course = Table(
"idac_user_course",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("course_id", Integer),
Column("run_counts", Integer, server_default="1"),
Column("skill_level_exp", Integer, server_default="0"),
UniqueConstraint("user", "course_id", name="idac_user_course_uk"),
mysql_charset="utf8mb4",
)
trial = Table(
"idac_user_time_trial",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("version", Integer, nullable=False),
Column("style_car_id", Integer),
Column("course_id", Integer),
Column("eval_id", Integer, server_default="0"),
Column("goal_time", Integer),
Column("section_time_1", Integer),
Column("section_time_2", Integer),
Column("section_time_3", Integer),
Column("section_time_4", Integer),
Column("mission", Integer),
Column("play_dt", TIMESTAMP, server_default=func.now()),
UniqueConstraint(
"user", "version", "course_id", "style_car_id", name="idac_user_time_trial_uk"
),
mysql_charset="utf8mb4",
)
challenge = Table(
"idac_user_challenge",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("vs_type", Integer),
Column("play_difficulty", Integer),
Column("cleared_difficulty", Integer),
Column("story_type", Integer),
Column("play_count", Integer, server_default="1"),
Column("weak_difficulty", Integer, server_default="0"),
Column("eval_id", Integer),
Column("advantage", Integer),
Column("sec1_advantage_avg", Integer),
Column("sec2_advantage_avg", Integer),
Column("sec3_advantage_avg", Integer),
Column("sec4_advantage_avg", Integer),
Column("nearby_advantage_rate", Integer),
Column("win_flag", Integer),
Column("result", Integer),
Column("record", Integer),
Column("course_id", Integer),
Column("last_play_course_id", Integer),
Column("style_car_id", Integer),
Column("course_day", Integer),
UniqueConstraint(
"user", "vs_type", "play_difficulty", name="idac_user_challenge_uk"
),
mysql_charset="utf8mb4",
)
theory_course = Table(
"idac_user_theory_course",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("course_id", Integer),
Column("max_victory_grade", Integer, server_default="0"),
Column("run_count", Integer, server_default="1"),
Column("powerhouse_lv", Integer),
Column("powerhouse_exp", Integer),
Column("played_powerhouse_lv", Integer),
Column("update_dt", TIMESTAMP, server_default=func.now()),
UniqueConstraint("user", "course_id", name="idac_user_theory_course_uk"),
mysql_charset="utf8mb4",
)
theory_partner = Table(
"idac_user_theory_partner",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("partner_id", Integer),
Column("fellowship_lv", Integer),
Column("fellowship_exp", Integer),
UniqueConstraint("user", "partner_id", name="idac_user_theory_partner_uk"),
mysql_charset="utf8mb4",
)
theory_running = Table(
"idac_user_theory_running",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("course_id", Integer),
Column("attack", Integer),
Column("defense", Integer),
Column("safety", Integer),
Column("runaway", Integer),
Column("trick_flag", Integer),
UniqueConstraint("user", "course_id", name="idac_user_theory_running_uk"),
mysql_charset="utf8mb4",
)
vs_info = Table(
"idac_user_vs_info",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
Column("group_key", String(25)),
Column("win_flg", Integer),
Column("style_car_id", Integer),
Column("course_id", Integer),
Column("course_day", Integer),
Column("players_num", Integer),
Column("winning", Integer),
Column("advantage_1", Integer),
Column("advantage_2", Integer),
Column("advantage_3", Integer),
Column("advantage_4", Integer),
Column("select_course_id", Integer),
Column("select_course_day", Integer),
Column("select_course_random", Integer),
Column("matching_success_sec", Integer),
Column("boost_flag", Integer),
Column("vs_history", Integer),
Column("break_count", Integer),
Column("break_penalty_flag", Integer),
UniqueConstraint("user", "group_key", name="idac_user_vs_info_uk"),
mysql_charset="utf8mb4",
)
stamp = Table(
"idac_user_stamp",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("m_stamp_event_id", Integer),
Column("select_flag", Integer),
Column("stamp_masu", Integer),
Column("daily_bonus", Integer),
Column("weekly_bonus", Integer),
Column("weekday_bonus", Integer),
Column("weekend_bonus", Integer),
Column("total_bonus", Integer),
Column("day_total_bonus", Integer),
Column("store_battle_bonus", Integer),
Column("story_bonus", Integer),
Column("online_battle_bonus", Integer),
Column("timetrial_bonus", Integer),
Column("fasteststreetlegaltheory_bonus", Integer),
Column("collaboration_bonus", Integer),
Column("add_bonus_daily_flag_1", Integer),
Column("add_bonus_daily_flag_2", Integer),
Column("add_bonus_daily_flag_3", Integer),
Column("create_date_daily", TIMESTAMP, server_default=func.now()),
Column("create_date_weekly", TIMESTAMP, server_default=func.now()),
UniqueConstraint("user", "m_stamp_event_id", name="idac_user_stamp_uk"),
mysql_charset="utf8mb4",
)
timetrial_event = Table(
"idac_user_timetrial_event",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("timetrial_event_id", Integer),
Column("point", Integer),
UniqueConstraint("user", "timetrial_event_id", name="idac_user_timetrial_event_uk"),
mysql_charset="utf8mb4",
)
class IDACItemData(BaseData):
def get_random_user_car(self, aime_id: int, version: int) -> Optional[List[Row]]:
sql = (
select(car)
.where(and_(car.c.user == aime_id, car.c.version == version))
.order_by(func.rand())
.limit(1)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_random_car(self, version: int) -> Optional[List[Row]]:
sql = select(car).where(car.c.version == version).order_by(func.rand()).limit(1)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_car(
self, aime_id: int, version: int, style_car_id: int
) -> Optional[List[Row]]:
sql = select(car).where(
and_(
car.c.user == aime_id,
car.c.version == version,
car.c.style_car_id == style_car_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_cars(
self, version: int, aime_id: int, only_pickup: bool = False
) -> Optional[List[Row]]:
if only_pickup:
sql = select(car).where(
and_(
car.c.user == aime_id,
car.c.version == version,
car.c.pickup_seq != 0,
)
)
else:
sql = select(car).where(
and_(car.c.user == aime_id, car.c.version == version)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_ticket(self, aime_id: int, ticket_id: int) -> Optional[Row]:
sql = select(ticket).where(
ticket.c.user == aime_id, ticket.c.ticket_id == ticket_id
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_tickets(self, aime_id: int) -> Optional[List[Row]]:
sql = select(ticket).where(ticket.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_story(self, aime_id: int, chapter_id: int) -> Optional[Row]:
sql = select(story).where(
and_(story.c.user == aime_id, story.c.chapter == chapter_id)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_stories(self, aime_id: int) -> Optional[List[Row]]:
sql = select(story).where(story.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_story_episodes(self, aime_id: int, chapter_id: int) -> Optional[List[Row]]:
sql = select(episode).where(
and_(episode.c.user == aime_id, episode.c.chapter == chapter_id)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_story_episode(self, aime_id: int, episode_id: int) -> Optional[Row]:
sql = select(episode).where(
and_(episode.c.user == aime_id, episode.c.episode == episode_id)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_story_episode_difficulties(
self, aime_id: int, episode_id: int
) -> Optional[List[Row]]:
sql = select(difficulty).where(
and_(difficulty.c.user == aime_id, difficulty.c.episode == episode_id)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_courses(self, aime_id: int) -> Optional[List[Row]]:
sql = select(course).where(course.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_course(self, aime_id: int, course_id: int) -> Optional[Row]:
sql = select(course).where(
and_(course.c.user == aime_id, course.c.course_id == course_id)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_time_trial_courses(self, version: int) -> Optional[List[Row]]:
sql = select(trial.c.course_id).where(trial.c.version == version).distinct()
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_time_trial_user_best_time_by_course_car(
self, version: int, aime_id: int, course_id: int, style_car_id: int
) -> Optional[Row]:
sql = select(trial).where(
and_(
trial.c.user == aime_id,
trial.c.version == version,
trial.c.course_id == course_id,
trial.c.style_car_id == style_car_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_time_trial_user_best_courses(
self, version: int, aime_id: int
) -> Optional[List[Row]]:
# get for a given aime_id the best time for each course
subquery = (
select(
trial.c.version,
func.min(trial.c.goal_time).label("min_goal_time"),
trial.c.course_id,
)
.where(and_(trial.c.version == version, trial.c.user == aime_id))
.group_by(trial.c.course_id)
.subquery()
)
# now get the full row for each best time
sql = select(trial).where(
and_(
trial.c.version == subquery.c.version,
trial.c.goal_time == subquery.c.min_goal_time,
trial.c.course_id == subquery.c.course_id,
trial.c.user == aime_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_time_trial_best_cars_by_course(
self, version: int, aime_id: int, course_id: int
) -> Optional[List[Row]]:
subquery = (
select(
trial.c.version,
func.min(trial.c.goal_time).label("min_goal_time"),
trial.c.style_car_id,
)
.where(
and_(
trial.c.version == version,
trial.c.user == aime_id,
trial.c.course_id == course_id,
)
)
.group_by(trial.c.style_car_id)
.subquery()
)
sql = select(trial).where(
and_(
trial.c.version == subquery.c.version,
trial.c.goal_time == subquery.c.min_goal_time,
trial.c.style_car_id == subquery.c.style_car_id,
trial.c.course_id == course_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_time_trial_ranking_by_course(
self,
version: int,
course_id: int,
style_car_id: Optional[int] = None,
limit: Optional[int] = 10,
) -> Optional[List[Row]]:
# get the top 10 ranking by goal_time for a given course which is grouped by user
subquery = select(
trial.c.version,
trial.c.user,
func.min(trial.c.goal_time).label("min_goal_time"),
).where(and_(trial.c.version == version, trial.c.course_id == course_id))
# if wantd filter only by style_car_id
if style_car_id is not None:
subquery = subquery.where(trial.c.style_car_id == style_car_id)
subquery = subquery.group_by(trial.c.user).subquery()
sql = (
select(trial)
.where(
and_(
trial.c.version == subquery.c.version,
trial.c.user == subquery.c.user,
trial.c.goal_time == subquery.c.min_goal_time,
),
)
.order_by(trial.c.goal_time)
)
# limit the result if needed
if limit is not None:
sql = sql.limit(limit)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_time_trial_best_ranking_by_course(
self, version: int, aime_id: int, course_id: int
) -> Optional[Row]:
sql = (
select(trial)
.where(
and_(
trial.c.version == version,
trial.c.user == aime_id,
trial.c.course_id == course_id,
),
)
.order_by(trial.c.goal_time)
.limit(1)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_challenge(
self, aime_id: int, vs_type: int, play_difficulty: int
) -> Optional[Row]:
sql = select(challenge).where(
and_(
challenge.c.user == aime_id,
challenge.c.vs_type == vs_type,
challenge.c.play_difficulty == play_difficulty,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_challenges(self, aime_id: int) -> Optional[List[Row]]:
sql = select(challenge).where(challenge.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_best_challenges_by_vs_type(
self, aime_id: int, story_type: int = 4
) -> Optional[List[Row]]:
sql = (
select(
challenge.c.story_type,
challenge.c.vs_type,
func.max(challenge.c.cleared_difficulty).label("max_clear_lv"),
func.max(challenge.c.play_difficulty).label("last_play_lv"),
challenge.c.course_id,
challenge.c.play_count,
)
.where(
and_(challenge.c.user == aime_id, challenge.c.story_type == story_type)
)
.group_by(challenge.c.vs_type, challenge.c.course_id, challenge.c.play_count)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_theory_courses(self, aime_id: int) -> Optional[List[Row]]:
sql = select(theory_course).where(theory_course.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_theory_course_by_powerhouse_lv(
self, aime_id: int, course_id: int, powerhouse_lv: int, count: int = 3
) -> Optional[List[Row]]:
sql = (
select(theory_course)
.where(
and_(
theory_course.c.user != aime_id,
theory_course.c.course_id == course_id,
theory_course.c.powerhouse_lv == powerhouse_lv,
)
)
.order_by(func.rand())
.limit(count)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_theory_course(self, aime_id: int, course_id: int) -> Optional[List[Row]]:
sql = select(theory_course).where(
and_(
theory_course.c.user == aime_id, theory_course.c.course_id == course_id
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_theory_partners(self, aime_id: int) -> Optional[List[Row]]:
sql = select(theory_partner).where(theory_partner.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_theory_running(self, aime_id: int) -> Optional[List[Row]]:
sql = select(theory_running).where(theory_running.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_theory_running_by_course(
self, aime_id: int, course_id: int
) -> Optional[Row]:
sql = select(theory_running).where(
and_(
theory_running.c.user == aime_id,
theory_running.c.course_id == course_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_vs_infos(self, aime_id: int) -> Optional[List[Row]]:
sql = select(vs_info).where(vs_info.c.user == aime_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_stamps(self, aime_id: int) -> Optional[List[Row]]:
sql = select(stamp).where(
and_(
stamp.c.user == aime_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_timetrial_event(self, aime_id: int, timetrial_event_id: int) -> Optional[Row]:
sql = select(timetrial_event).where(
and_(
timetrial_event.c.user == aime_id,
timetrial_event.c.timetrial_event_id == timetrial_event_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def put_car(self, aime_id: int, version: int, car_data: Dict) -> Optional[int]:
car_data["user"] = aime_id
car_data["version"] = version
sql = insert(car).values(**car_data)
conflict = sql.on_duplicate_key_update(**car_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_car: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_ticket(self, aime_id: int, ticket_data: Dict) -> Optional[int]:
ticket_data["user"] = aime_id
sql = insert(ticket).values(**ticket_data)
conflict = sql.on_duplicate_key_update(**ticket_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_ticket: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_story(self, aime_id: int, story_data: Dict) -> Optional[int]:
story_data["user"] = aime_id
sql = insert(story).values(**story_data)
conflict = sql.on_duplicate_key_update(**story_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_story: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_story_episode_play_status(
self, aime_id: int, chapter_id: int, play_status: int = 1
) -> Optional[int]:
sql = (
update(episode)
.where(and_(episode.c.user == aime_id, episode.c.chapter == chapter_id))
.values(play_status=play_status)
)
result = self.execute(sql)
if result is None:
self.logger.warn(
f"put_story_episode_play_status: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_story_episode(
self, aime_id: int, chapter_id: int, episode_data: Dict
) -> Optional[int]:
episode_data["user"] = aime_id
episode_data["chapter"] = chapter_id
sql = insert(episode).values(**episode_data)
conflict = sql.on_duplicate_key_update(**episode_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_story_episode: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_story_episode_difficulty(
self, aime_id: int, episode_id: int, difficulty_data: Dict
) -> Optional[int]:
difficulty_data["user"] = aime_id
difficulty_data["episode"] = episode_id
sql = insert(difficulty).values(**difficulty_data)
conflict = sql.on_duplicate_key_update(**difficulty_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_story_episode_difficulty: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]:
course_data["user"] = aime_id
sql = insert(course).values(**course_data)
conflict = sql.on_duplicate_key_update(**course_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_course: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_time_trial(
self, version: int, aime_id: int, time_trial_data: Dict
) -> Optional[int]:
time_trial_data["user"] = aime_id
time_trial_data["version"] = version
sql = insert(trial).values(**time_trial_data)
conflict = sql.on_duplicate_key_update(**time_trial_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_time_trial: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_challenge(self, aime_id: int, challenge_data: Dict) -> Optional[int]:
challenge_data["user"] = aime_id
sql = insert(challenge).values(**challenge_data)
conflict = sql.on_duplicate_key_update(**challenge_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_challenge: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_theory_course(
self, aime_id: int, theory_course_data: Dict
) -> Optional[int]:
theory_course_data["user"] = aime_id
sql = insert(theory_course).values(**theory_course_data)
conflict = sql.on_duplicate_key_update(**theory_course_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_theory_course: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_theory_partner(
self, aime_id: int, theory_partner_data: Dict
) -> Optional[int]:
theory_partner_data["user"] = aime_id
sql = insert(theory_partner).values(**theory_partner_data)
conflict = sql.on_duplicate_key_update(**theory_partner_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_theory_partner: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_theory_running(
self, aime_id: int, theory_running_data: Dict
) -> Optional[int]:
theory_running_data["user"] = aime_id
sql = insert(theory_running).values(**theory_running_data)
conflict = sql.on_duplicate_key_update(**theory_running_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_theory_running: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_vs_info(self, aime_id: int, vs_info_data: Dict) -> Optional[int]:
vs_info_data["user"] = aime_id
sql = insert(vs_info).values(**vs_info_data)
conflict = sql.on_duplicate_key_update(**vs_info_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_vs_info: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_stamp(
self, aime_id: int, stamp_data: Dict
) -> Optional[int]:
stamp_data["user"] = aime_id
sql = insert(stamp).values(**stamp_data)
conflict = sql.on_duplicate_key_update(**stamp_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"putstamp: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_timetrial_event(
self, aime_id: int, time_trial_event_id: int, point: int
) -> Optional[int]:
timetrial_event_data = {
"user": aime_id,
"timetrial_event_id": time_trial_event_id,
"point": point,
}
sql = insert(timetrial_event).values(**timetrial_event_data)
conflict = sql.on_duplicate_key_update(**timetrial_event_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_timetrial_event: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid

View File

@@ -0,0 +1,440 @@
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.schema import ForeignKey
from sqlalchemy.sql import func, select
from sqlalchemy.engine import Row
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
from core.config import CoreConfig
profile = Table(
"idac_profile",
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("username", String(8)),
Column("country", Integer),
Column("store", Integer),
Column("team_id", Integer, server_default="0"),
Column("total_play", Integer, server_default="0"),
Column("daily_play", Integer, server_default="0"),
Column("day_play", Integer, server_default="0"),
Column("mileage", Integer, server_default="0"),
Column("asset_version", Integer, server_default="1"),
Column("last_play_date", TIMESTAMP, server_default=func.now()),
Column("mytitle_id", Integer, server_default="0"),
Column("mytitle_efffect_id", Integer, server_default="0"),
Column("sticker_id", Integer, server_default="0"),
Column("sticker_effect_id", Integer, server_default="0"),
Column("papercup_id", Integer, server_default="0"),
Column("tachometer_id", Integer, server_default="0"),
Column("aura_id", Integer, server_default="0"),
Column("aura_color_id", Integer, server_default="0"),
Column("aura_line_id", Integer, server_default="0"),
Column("bgm_id", Integer, server_default="0"),
Column("keyholder_id", Integer, server_default="0"),
Column("start_menu_bg_id", Integer, server_default="0"),
Column("use_car_id", Integer, server_default="1"),
Column("use_style_car_id", Integer, server_default="1"),
Column("bothwin_count", Integer, server_default="0"),
Column("bothwin_score", Integer, server_default="0"),
Column("subcard_count", Integer, server_default="0"),
Column("vs_history", Integer, server_default="0"),
Column("stamp_key_assign_0", Integer),
Column("stamp_key_assign_1", Integer),
Column("stamp_key_assign_2", Integer),
Column("stamp_key_assign_3", Integer),
Column("name_change_category", Integer, server_default="0"),
Column("factory_disp", Integer, server_default="0"),
Column("create_date", TIMESTAMP, server_default=func.now()),
Column("cash", Integer, server_default="0"),
Column("dressup_point", Integer, server_default="0"),
Column("avatar_point", Integer, server_default="0"),
Column("total_cash", Integer, server_default="0"),
UniqueConstraint("user", "version", name="idac_profile_uk"),
mysql_charset="utf8mb4",
)
# No point setting defaults since the game sends everything on profile creation anyway
config = Table(
"idac_profile_config",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("config_id", Integer),
Column("steering_intensity", Integer),
Column("transmission_type", Integer),
Column("default_viewpoint", Integer),
Column("favorite_bgm", Integer),
Column("bgm_volume", Integer),
Column("se_volume", Integer),
Column("master_volume", Integer),
Column("store_battle_policy", Integer),
Column("battle_onomatope_display", Integer),
Column("cornering_guide", Integer),
Column("minimap", Integer),
Column("line_guide", Integer),
Column("ghost", Integer),
Column("race_exit", Integer),
Column("result_skip", Integer),
Column("stamp_select_skip", Integer),
UniqueConstraint("user", name="idac_profile_config_uk"),
mysql_charset="utf8mb4",
)
# No point setting defaults since the game sends everything on profile creation anyway
avatar = Table(
"idac_profile_avatar",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("sex", Integer),
Column("face", Integer),
Column("eye", Integer),
Column("mouth", Integer),
Column("hair", Integer),
Column("glasses", Integer),
Column("face_accessory", Integer),
Column("body", Integer),
Column("body_accessory", Integer),
Column("behind", Integer),
Column("bg", Integer),
Column("effect", Integer),
Column("special", Integer),
UniqueConstraint("user", name="idac_profile_avatar_uk"),
mysql_charset="utf8mb4",
)
rank = Table(
"idac_profile_rank",
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("story_rank_exp", Integer, server_default="0"),
Column("story_rank", Integer, server_default="1"),
Column("time_trial_rank_exp", Integer, server_default="0"),
Column("time_trial_rank", Integer, server_default="1"),
Column("online_battle_rank_exp", Integer, server_default="0"),
Column("online_battle_rank", Integer, server_default="1"),
Column("store_battle_rank_exp", Integer, server_default="0"),
Column("store_battle_rank", Integer, server_default="1"),
Column("theory_exp", Integer, server_default="0"),
Column("theory_rank", Integer, server_default="1"),
Column("pride_group_id", Integer, server_default="0"),
Column("pride_point", Integer, server_default="0"),
Column("grade_exp", Integer, server_default="0"),
Column("grade", Integer, server_default="1"),
Column("grade_reward_dist", Integer, server_default="0"),
Column("story_rank_reward_dist", Integer, server_default="0"),
Column("time_trial_rank_reward_dist", Integer, server_default="0"),
Column("online_battle_rank_reward_dist", Integer, server_default="0"),
Column("store_battle_rank_reward_dist", Integer, server_default="0"),
Column("theory_rank_reward_dist", Integer, server_default="0"),
Column("max_attained_online_battle_rank", Integer, server_default="1"),
Column("max_attained_pride_point", Integer, server_default="0"),
Column("is_last_max", Integer, server_default="0"),
UniqueConstraint("user", "version", name="idac_profile_rank_uk"),
mysql_charset="utf8mb4",
)
stock = Table(
"idac_profile_stock",
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("mytitle_list", String(1024), server_default=""),
Column("mytitle_new_list", String(1024), server_default=""),
Column("avatar_face_list", String(255), server_default=""),
Column("avatar_face_new_list", String(255), server_default=""),
Column("avatar_eye_list", String(255), server_default=""),
Column("avatar_eye_new_list", String(255), server_default=""),
Column("avatar_hair_list", String(255), server_default=""),
Column("avatar_hair_new_list", String(255), server_default=""),
Column("avatar_body_list", String(255), server_default=""),
Column("avatar_body_new_list", String(255), server_default=""),
Column("avatar_mouth_list", String(255), server_default=""),
Column("avatar_mouth_new_list", String(255), server_default=""),
Column("avatar_glasses_list", String(255), server_default=""),
Column("avatar_glasses_new_list", String(255), server_default=""),
Column("avatar_face_accessory_list", String(255), server_default=""),
Column("avatar_face_accessory_new_list", String(255), server_default=""),
Column("avatar_body_accessory_list", String(255), server_default=""),
Column("avatar_body_accessory_new_list", String(255), server_default=""),
Column("avatar_behind_list", String(255), server_default=""),
Column("avatar_behind_new_list", String(255), server_default=""),
Column("avatar_bg_list", String(255), server_default=""),
Column("avatar_bg_new_list", String(255), server_default=""),
Column("avatar_effect_list", String(255), server_default=""),
Column("avatar_effect_new_list", String(255), server_default=""),
Column("avatar_special_list", String(255), server_default=""),
Column("avatar_special_new_list", String(255), server_default=""),
Column("stamp_list", String(255), server_default=""),
Column("stamp_new_list", String(255), server_default=""),
Column("keyholder_list", String(256), server_default=""),
Column("keyholder_new_list", String(256), server_default=""),
Column("papercup_list", String(255), server_default=""),
Column("papercup_new_list", String(255), server_default=""),
Column("tachometer_list", String(255), server_default=""),
Column("tachometer_new_list", String(255), server_default=""),
Column("aura_list", String(255), server_default=""),
Column("aura_new_list", String(255), server_default=""),
Column("aura_color_list", String(255), server_default=""),
Column("aura_color_new_list", String(255), server_default=""),
Column("aura_line_list", String(255), server_default=""),
Column("aura_line_new_list", String(255), server_default=""),
Column("bgm_list", String(255), server_default=""),
Column("bgm_new_list", String(255), server_default=""),
Column("dx_color_list", String(255), server_default=""),
Column("dx_color_new_list", String(255), server_default=""),
Column("start_menu_bg_list", String(255), server_default=""),
Column("start_menu_bg_new_list", String(255), server_default=""),
Column("under_neon_list", String(255), server_default=""),
UniqueConstraint("user", "version", name="idac_profile_stock_uk"),
mysql_charset="utf8mb4",
)
theory = Table(
"idac_profile_theory",
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("play_count", Integer, server_default="0"),
Column("play_count_multi", Integer, server_default="0"),
Column("partner_id", Integer),
Column("partner_progress", Integer),
Column("partner_progress_score", Integer),
Column("practice_start_rank", Integer, server_default="0"),
Column("general_flag", Integer, server_default="0"),
Column("vs_history", Integer, server_default="0"),
Column("vs_history_multi", Integer, server_default="0"),
Column("win_count", Integer, server_default="0"),
Column("win_count_multi", Integer, server_default="0"),
UniqueConstraint("user", "version", name="idac_profile_theory_uk"),
mysql_charset="utf8mb4",
)
class IDACProfileData(BaseData):
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
super().__init__(cfg, conn)
self.date_time_format_ext = (
"%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
)
self.date_time_format_short = "%Y-%m-%d"
def get_profile(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(profile).where(
and_(
profile.c.user == aime_id,
profile.c.version == version,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_different_random_profiles(
self, aime_id: int, version: int, count: int = 9
) -> Optional[Row]:
sql = (
select(profile)
.where(
and_(
profile.c.user != aime_id,
profile.c.version == version,
)
)
.order_by(func.rand())
.limit(count)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_profile_config(self, aime_id: int) -> Optional[Row]:
sql = select(config).where(
and_(
config.c.user == aime_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_profile_avatar(self, aime_id: int) -> Optional[Row]:
sql = select(avatar).where(
and_(
avatar.c.user == aime_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_profile_rank(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(rank).where(
and_(
rank.c.user == aime_id,
rank.c.version == version,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_profile_stock(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(stock).where(
and_(
stock.c.user == aime_id,
stock.c.version == version,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_profile_theory(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(theory).where(
and_(
theory.c.user == aime_id,
theory.c.version == version,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def put_profile(
self, aime_id: int, version: int, profile_data: Dict
) -> Optional[int]:
profile_data["user"] = aime_id
profile_data["version"] = version
sql = insert(profile).values(**profile_data)
conflict = sql.on_duplicate_key_update(**profile_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_profile: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_profile_config(self, aime_id: int, config_data: Dict) -> Optional[int]:
config_data["user"] = aime_id
sql = insert(config).values(**config_data)
conflict = sql.on_duplicate_key_update(**config_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_profile_config: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_profile_avatar(self, aime_id: int, avatar_data: Dict) -> Optional[int]:
avatar_data["user"] = aime_id
sql = insert(avatar).values(**avatar_data)
conflict = sql.on_duplicate_key_update(**avatar_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_profile_avatar: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid
def put_profile_rank(
self, aime_id: int, version: int, rank_data: Dict
) -> Optional[int]:
rank_data["user"] = aime_id
rank_data["version"] = version
sql = insert(rank).values(**rank_data)
conflict = sql.on_duplicate_key_update(**rank_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_profile_rank: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_profile_stock(
self, aime_id: int, version: int, stock_data: Dict
) -> Optional[int]:
stock_data["user"] = aime_id
stock_data["version"] = version
sql = insert(stock).values(**stock_data)
conflict = sql.on_duplicate_key_update(**stock_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_profile_stock: Failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def put_profile_theory(
self, aime_id: int, version: int, theory_data: Dict
) -> Optional[int]:
theory_data["user"] = aime_id
theory_data["version"] = version
sql = insert(theory).values(**theory_data)
conflict = sql.on_duplicate_key_update(**theory_data)
result = self.execute(conflict)
if result is None:
self.logger.warn(
f"put_profile_theory: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid