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): async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async def get_tickets(self, aime_id: int) -> Optional[List[Row]]: sql = select(ticket).where(ticket.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async def get_stories(self, aime_id: int) -> Optional[List[Row]]: sql = select(story).where(story.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await self.execute(sql) if result is None: return None return result.fetchall() async def get_courses(self, aime_id: int) -> Optional[List[Row]]: sql = select(course).where(course.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async def get_time_trial_courses(self, version: int) -> Optional[List[Row]]: sql = select(trial.c.course_id).where(trial.c.version == version).distinct() result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await self.execute(sql) if result is None: return None return result.fetchall() async def get_time_trial_best_cars_by_course( self, version: int, course_id: int, aime_id: Optional[int] = None ) -> 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.course_id == course_id, ) ) ) if aime_id is not None: subquery = subquery.where(trial.c.user == aime_id) subquery = subquery.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 = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async def get_challenges(self, aime_id: int) -> Optional[List[Row]]: sql = select(challenge).where(challenge.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async def get_best_challenges_by_vs_type( self, aime_id: int, story_type: int = 4 ) -> Optional[List[Row]]: subquery = ( select( challenge.c.story_type, challenge.c.user, challenge.c.vs_type, func.max(challenge.c.play_difficulty).label("last_play_lv"), ) .where( and_(challenge.c.user == aime_id, challenge.c.story_type == story_type) ) .group_by(challenge.c.vs_type) ) sql = ( select( challenge.c.story_type, challenge.c.vs_type, challenge.c.cleared_difficulty.label("max_clear_lv"), challenge.c.play_difficulty.label("last_play_lv"), challenge.c.course_id, challenge.c.play_count, ) .where( and_( challenge.c.user == subquery.c.user, challenge.c.vs_type == subquery.c.vs_type, challenge.c.play_difficulty == subquery.c.last_play_lv, ), ) .order_by(challenge.c.vs_type) ) result = await self.execute(sql) if result is None: return None return result.fetchall() async def get_theory_courses(self, aime_id: int) -> Optional[List[Row]]: sql = select(theory_course).where(theory_course.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async def get_theory_partners(self, aime_id: int) -> Optional[List[Row]]: sql = select(theory_partner).where(theory_partner.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async def get_theory_running(self, aime_id: int) -> Optional[List[Row]]: sql = select(theory_running).where(theory_running.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async def get_vs_infos(self, aime_id: int) -> Optional[List[Row]]: sql = select(vs_info).where(vs_info.c.user == aime_id) result = await self.execute(sql) if result is None: return None return result.fetchall() async def get_stamps(self, aime_id: int) -> Optional[List[Row]]: sql = select(stamp).where( and_( stamp.c.user == aime_id, ) ) result = await self.execute(sql) if result is None: return None return result.fetchall() async 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 = await self.execute(sql) if result is None: return None return result.fetchone() async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await 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 async 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 = await self.execute(conflict) if result is None: self.logger.warn( f"putstamp: Failed to update! aime_id: {aime_id}" ) return None return result.lastrowid async 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 = await 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