1755 lines
76 KiB
Python
1755 lines
76 KiB
Python
import logging, coloredlogs
|
|
from typing import Any, Dict, List
|
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
from sqlalchemy import create_engine
|
|
from logging.handlers import TimedRotatingFileHandler
|
|
from datetime import datetime
|
|
import importlib, os, json
|
|
|
|
from hashlib import sha256
|
|
|
|
from core.config import CoreConfig
|
|
from core.data.schema import *
|
|
from core.utils import Utils
|
|
|
|
class Data:
|
|
def __init__(self, cfg: CoreConfig) -> None:
|
|
self.config = cfg
|
|
|
|
if self.config.database.sha2_password:
|
|
passwd = sha256(self.config.database.password.encode()).digest()
|
|
self.__url = f"{self.config.database.protocol}://{self.config.database.username}:{passwd.hex()}@{self.config.database.host}/{self.config.database.name}?charset=utf8mb4"
|
|
else:
|
|
self.__url = f"{self.config.database.protocol}://{self.config.database.username}:{self.config.database.password}@{self.config.database.host}/{self.config.database.name}?charset=utf8mb4"
|
|
|
|
self.__engine = create_engine(self.__url, pool_recycle=3600)
|
|
session = sessionmaker(bind=self.__engine, autoflush=True, autocommit=True)
|
|
self.session = scoped_session(session)
|
|
|
|
self.user = UserData(self.config, self.session)
|
|
self.arcade = ArcadeData(self.config, self.session)
|
|
self.card = CardData(self.config, self.session)
|
|
self.base = BaseData(self.config, self.session)
|
|
self.schema_ver_latest = 2
|
|
|
|
log_fmt_str = "[%(asctime)s] %(levelname)s | Database | %(message)s"
|
|
log_fmt = logging.Formatter(log_fmt_str)
|
|
self.logger = logging.getLogger("database")
|
|
|
|
# Prevent the logger from adding handlers multiple times
|
|
if not getattr(self.logger, 'handler_set', None):
|
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "db"), encoding="utf-8",
|
|
when="d", backupCount=10)
|
|
fileHandler.setFormatter(log_fmt)
|
|
|
|
consoleHandler = logging.StreamHandler()
|
|
consoleHandler.setFormatter(log_fmt)
|
|
|
|
self.logger.addHandler(fileHandler)
|
|
self.logger.addHandler(consoleHandler)
|
|
|
|
self.logger.setLevel(self.config.database.loglevel)
|
|
coloredlogs.install(cfg.database.loglevel, logger=self.logger, fmt=log_fmt_str)
|
|
self.logger.handler_set = True # type: ignore
|
|
|
|
def create_database(self):
|
|
self.logger.info("Creating databases...")
|
|
try:
|
|
metadata.create_all(self.__engine.connect())
|
|
except SQLAlchemyError as e:
|
|
self.logger.error(f"Failed to create databases! {e}")
|
|
return
|
|
|
|
games = Utils.get_all_titles()
|
|
for game_dir, game_mod in games.items():
|
|
try:
|
|
title_db = game_mod.database(self.config)
|
|
metadata.create_all(self.__engine.connect())
|
|
|
|
self.base.set_schema_ver(game_mod.current_schema_version, game_mod.game_codes[0])
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Could not load database schema from {game_dir} - {e}")
|
|
|
|
self.logger.info(f"Setting base_schema_ver to {self.schema_ver_latest}")
|
|
self.base.set_schema_ver(self.schema_ver_latest)
|
|
|
|
self.logger.info(f"Setting user auto_incrememnt to {self.config.database.user_table_autoincrement_start}")
|
|
self.user.reset_autoincrement(self.config.database.user_table_autoincrement_start)
|
|
|
|
def recreate_database(self):
|
|
self.logger.info("Dropping all databases...")
|
|
self.base.execute("SET FOREIGN_KEY_CHECKS=0")
|
|
try:
|
|
metadata.drop_all(self.__engine.connect())
|
|
except SQLAlchemyError as e:
|
|
self.logger.error(f"Failed to drop databases! {e}")
|
|
return
|
|
|
|
for root, dirs, files in os.walk("./titles"):
|
|
for dir in dirs:
|
|
if not dir.startswith("__"):
|
|
try:
|
|
mod = importlib.import_module(f"titles.{dir}")
|
|
|
|
try:
|
|
title_db = mod.database(self.config)
|
|
metadata.drop_all(self.__engine.connect())
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Could not load database schema from {dir} - {e}")
|
|
|
|
except ImportError as e:
|
|
self.logger.warning(f"Failed to load database schema dir {dir} - {e}")
|
|
break
|
|
|
|
self.base.execute("SET FOREIGN_KEY_CHECKS=1")
|
|
|
|
self.create_database()
|
|
|
|
def migrate_database(self, game: str, version: int, action: str) -> None:
|
|
old_ver = self.base.get_schema_ver(game)
|
|
sql = ""
|
|
|
|
if old_ver is None:
|
|
self.logger.error(f"Schema for game {game} does not exist, did you run the creation script?")
|
|
return
|
|
|
|
if old_ver == version:
|
|
self.logger.info(f"Schema for game {game} is already version {old_ver}, nothing to do")
|
|
return
|
|
|
|
if not os.path.exists(f"core/data/schema/versions/{game.upper()}_{version}_{action}.sql"):
|
|
self.logger.error(f"Could not find {action} script {game.upper()}_{version}_{action}.sql in core/data/schema/versions folder")
|
|
return
|
|
|
|
with open(f"core/data/schema/versions/{game.upper()}_{version}_{action}.sql", "r", encoding="utf-8") as f:
|
|
sql = f.read()
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Error execuing sql script!")
|
|
return None
|
|
|
|
result = self.base.set_schema_ver(version, game)
|
|
if result is None:
|
|
self.logger.error("Error setting version in schema_version table!")
|
|
return None
|
|
|
|
self.logger.info(f"Successfully migrated {game} to schema version {version}")
|
|
|
|
def dump_db(self):
|
|
dbname = self.config.database.name
|
|
|
|
self.logger.info("Database dumper for use with the reworked schema")
|
|
self.logger.info("Dumping users...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`user`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
users = result.fetchall()
|
|
|
|
user_list: List[Dict[str, Any]] = []
|
|
for usr in users:
|
|
user_list.append({
|
|
"id": usr["id"],
|
|
"username": usr["username"],
|
|
"email": usr["email"],
|
|
"password": usr["password"],
|
|
"permissions": usr["permissions"],
|
|
"created_date": datetime.strftime(usr["created_date"], "%Y-%m-%d %H:%M:%S"),
|
|
"last_login_date": datetime.strftime(usr["accessed_date"], "%Y-%m-%d %H:%M:%S"),
|
|
})
|
|
|
|
self.logger.info(f"Done, found {len(user_list)} users")
|
|
with open("dbdump-user.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(user_list))
|
|
self.logger.info(f"Saved as dbdump-user.json")
|
|
|
|
self.logger.info("Dumping cards...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`card`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
cards = result.fetchall()
|
|
|
|
card_list: List[Dict[str, Any]] = []
|
|
for crd in cards:
|
|
card_list.append({
|
|
"id": crd["id"],
|
|
"user": crd["user"],
|
|
"access_code": crd["access_code"],
|
|
"is_locked": crd["is_locked"],
|
|
"is_banned": crd["is_banned"],
|
|
"created_date": datetime.strftime(crd["created_date"], "%Y-%m-%d %H:%M:%S"),
|
|
"last_login_date": datetime.strftime(crd["accessed_date"], "%Y-%m-%d %H:%M:%S"),
|
|
})
|
|
|
|
self.logger.info(f"Done, found {len(card_list)} cards")
|
|
with open("dbdump-card.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(card_list))
|
|
self.logger.info(f"Saved as dbdump-card.json")
|
|
|
|
self.logger.info("Dumping arcades...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`arcade`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
arcades = result.fetchall()
|
|
|
|
arcade_list: List[Dict[str, Any]] = []
|
|
for arc in arcades:
|
|
arcade_list.append({
|
|
"id": arc["id"],
|
|
"name": arc["name"],
|
|
"nickname": arc["name"],
|
|
"country": None,
|
|
"country_id": None,
|
|
"state": None,
|
|
"city": None,
|
|
"region_id": None,
|
|
"timezone": None,
|
|
})
|
|
|
|
self.logger.info(f"Done, found {len(arcade_list)} arcades")
|
|
with open("dbdump-arcade.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(arcade_list))
|
|
self.logger.info(f"Saved as dbdump-arcade.json")
|
|
|
|
self.logger.info("Dumping machines...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`machine`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
machines = result.fetchall()
|
|
|
|
machine_list: List[Dict[str, Any]] = []
|
|
for mech in machines:
|
|
if "country" in mech["data"]:
|
|
country = mech["data"]["country"]
|
|
else:
|
|
country = None
|
|
|
|
if "ota_enable" in mech["data"]:
|
|
ota_enable = mech["data"]["ota_enable"]
|
|
else:
|
|
ota_enable = None
|
|
|
|
machine_list.append({
|
|
"id": mech["id"],
|
|
"arcade": mech["arcade"],
|
|
"serial": mech["keychip"],
|
|
"game": mech["game"],
|
|
"board": None,
|
|
"country": country,
|
|
"timezone": None,
|
|
"ota_enable": ota_enable,
|
|
"is_cab": False,
|
|
})
|
|
|
|
self.logger.info(f"Done, found {len(machine_list)} machines")
|
|
with open("dbdump-machine.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(machine_list))
|
|
self.logger.info(f"Saved as dbdump-machine.json")
|
|
|
|
self.logger.info("Dumping arcade owners...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`arcade_owner`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
arcade_owners = result.fetchall()
|
|
|
|
owner_list: List[Dict[str, Any]] = []
|
|
for owner in owner_list:
|
|
owner_list.append(owner._asdict())
|
|
|
|
self.logger.info(f"Done, found {len(owner_list)} arcade owners")
|
|
with open("dbdump-arcade_owner.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(owner_list))
|
|
self.logger.info(f"Saved as dbdump-arcade_owner.json")
|
|
|
|
self.logger.info("Dumping profiles...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`profile`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
profiles = result.fetchall()
|
|
|
|
profile_list: Dict[List[Dict[str, Any]]] = {}
|
|
for pf in profiles:
|
|
game = pf["game"]
|
|
|
|
if game not in profile_list:
|
|
profile_list[game] = []
|
|
|
|
profile_list[game].append({
|
|
"id": pf["id"],
|
|
"user": pf["user"],
|
|
"version": pf["version"],
|
|
"use_count": pf["use_count"],
|
|
"name": pf["name"],
|
|
"game_id": pf["game_id"],
|
|
"mods": pf["mods"],
|
|
"data": pf["data"],
|
|
})
|
|
|
|
self.logger.info(f"Done, found profiles for {len(profile_list)} games")
|
|
with open("dbdump-profile.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(profile_list))
|
|
self.logger.info(f"Saved as dbdump-profile.json")
|
|
|
|
self.logger.info("Dumping scores...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`score`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
scores = result.fetchall()
|
|
|
|
score_list: Dict[List[Dict[str, Any]]] = {}
|
|
for sc in scores:
|
|
game = sc["game"]
|
|
|
|
if game not in score_list:
|
|
score_list[game] = []
|
|
|
|
score_list[game].append({
|
|
"id": sc["id"],
|
|
"user": sc["user"],
|
|
"version": sc["version"],
|
|
"song_id": sc["song_id"],
|
|
"chart_id": sc["chart_id"],
|
|
"score1": sc["score1"],
|
|
"score2": sc["score2"],
|
|
"fc1": sc["fc1"],
|
|
"fc2": sc["fc2"],
|
|
"cleared": sc["cleared"],
|
|
"grade": sc["grade"],
|
|
"data": sc["data"],
|
|
})
|
|
|
|
self.logger.info(f"Done, found scores for {len(score_list)} games")
|
|
with open("dbdump-score.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(score_list))
|
|
self.logger.info(f"Saved as dbdump-score.json")
|
|
|
|
self.logger.info("Dumping achievements...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`achievement`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
achievements = result.fetchall()
|
|
|
|
achievement_list: Dict[List[Dict[str, Any]]] = {}
|
|
for ach in achievements:
|
|
game = ach["game"]
|
|
|
|
if game not in achievement_list:
|
|
achievement_list[game] = []
|
|
|
|
achievement_list[game].append({
|
|
"id": ach["id"],
|
|
"user": ach["user"],
|
|
"version": ach["version"],
|
|
"type": ach["type"],
|
|
"achievement_id": ach["achievement_id"],
|
|
"data": ach["data"],
|
|
})
|
|
|
|
self.logger.info(f"Done, found achievements for {len(achievement_list)} games")
|
|
with open("dbdump-achievement.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(achievement_list))
|
|
self.logger.info(f"Saved as dbdump-achievement.json")
|
|
|
|
self.logger.info("Dumping items...")
|
|
|
|
sql = f"SELECT * FROM `{dbname}`.`item`"
|
|
|
|
result = self.base.execute(sql)
|
|
if result is None:
|
|
self.logger.error("Failed")
|
|
return None
|
|
items = result.fetchall()
|
|
|
|
item_list: Dict[List[Dict[str, Any]]] = {}
|
|
for itm in items:
|
|
game = itm["game"]
|
|
|
|
if game not in item_list:
|
|
item_list[game] = []
|
|
|
|
item_list[game].append({
|
|
"id": itm["id"],
|
|
"user": itm["user"],
|
|
"version": itm["version"],
|
|
"type": itm["type"],
|
|
"item_id": itm["item_id"],
|
|
"data": ach["data"],
|
|
})
|
|
|
|
self.logger.info(f"Done, found items for {len(item_list)} games")
|
|
with open("dbdump-item.json", "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(item_list))
|
|
self.logger.info(f"Saved as dbdump-item.json")
|
|
|
|
def restore_from_old_schema(self):
|
|
# Import the tables we expect to be there
|
|
from core.data.schema.user import aime_user
|
|
from core.data.schema.card import aime_card
|
|
from core.data.schema.arcade import arcade, machine, arcade_owner
|
|
from sqlalchemy.dialects.mysql import Insert
|
|
|
|
# Make sure that all the tables we're trying to access exist
|
|
self.create_database()
|
|
|
|
# Import the data, making sure that dependencies are accounted for
|
|
if os.path.exists("dbdump-user.json"):
|
|
users = []
|
|
with open("dbdump-user.json", "r", encoding="utf-8") as f:
|
|
users = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(users)} users")
|
|
|
|
for user in users:
|
|
sql = Insert(aime_user).values(**user)
|
|
|
|
conflict = sql.on_duplicate_key_update(**user)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert user {user['id']}")
|
|
continue
|
|
self.logger.info(f"Inserted user {user['id']} -> {result.lastrowid}")
|
|
|
|
if os.path.exists("dbdump-card.json"):
|
|
cards = []
|
|
with open("dbdump-card.json", "r", encoding="utf-8") as f:
|
|
cards = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(cards)} cards")
|
|
|
|
for card in cards:
|
|
sql = Insert(aime_card).values(**card)
|
|
|
|
conflict = sql.on_duplicate_key_update(**card)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert card {card['id']}")
|
|
continue
|
|
self.logger.info(f"Inserted card {card['id']} -> {result.lastrowid}")
|
|
|
|
if os.path.exists("dbdump-arcade.json"):
|
|
arcades = []
|
|
with open("dbdump-arcade.json", "r", encoding="utf-8") as f:
|
|
arcades = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(arcades)} arcades")
|
|
|
|
for ac in arcades:
|
|
sql = Insert(arcade).values(**ac)
|
|
|
|
conflict = sql.on_duplicate_key_update(**ac)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert arcade {ac['id']}")
|
|
continue
|
|
self.logger.info(f"Inserted arcade {ac['id']} -> {result.lastrowid}")
|
|
|
|
if os.path.exists("dbdump-arcade_owner.json"):
|
|
ac_owners = []
|
|
with open("dbdump-arcade_owner.json", "r", encoding="utf-8") as f:
|
|
ac_owners = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(ac_owners)} arcade owners")
|
|
|
|
for owner in ac_owners:
|
|
sql = Insert(arcade_owner).values(**owner)
|
|
|
|
conflict = sql.on_duplicate_key_update(**owner)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert arcade_owner {owner['user']}")
|
|
continue
|
|
self.logger.info(f"Inserted arcade_owner {owner['user']} -> {result.lastrowid}")
|
|
|
|
if os.path.exists("dbdump-machine.json"):
|
|
mechs = []
|
|
with open("dbdump-machine.json", "r", encoding="utf-8") as f:
|
|
mechs = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(mechs)} machines")
|
|
|
|
for mech in mechs:
|
|
sql = Insert(machine).values(**mech)
|
|
|
|
conflict = sql.on_duplicate_key_update(**mech)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert machine {mech['id']}")
|
|
continue
|
|
self.logger.info(f"Inserted machine {mech['id']} -> {result.lastrowid}")
|
|
|
|
# Now the fun part, grabbing all our scores, profiles, items, and achievements and trying
|
|
# to conform them to our current, freeform schema. This will be painful...
|
|
profiles = {}
|
|
items = {}
|
|
scores = {}
|
|
achievements = {}
|
|
|
|
if os.path.exists("dbdump-profile.json"):
|
|
with open("dbdump-profile.json", "r", encoding="utf-8") as f:
|
|
profiles = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(profiles)} profiles")
|
|
|
|
if os.path.exists("dbdump-item.json"):
|
|
with open("dbdump-item.json", "r", encoding="utf-8") as f:
|
|
items = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(items)} items")
|
|
|
|
if os.path.exists("dbdump-score.json"):
|
|
with open("dbdump-score.json", "r", encoding="utf-8") as f:
|
|
scores = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(scores)} scores")
|
|
|
|
if os.path.exists("dbdump-achievement.json"):
|
|
with open("dbdump-achievement.json", "r", encoding="utf-8") as f:
|
|
achievements = json.load(f)
|
|
|
|
self.logger.info(f"Load {len(achievements)} achievements")
|
|
|
|
# Chuni / Chusan
|
|
if os.path.exists("titles/chuni/schema"):
|
|
from titles.chuni.schema.item import character, item, duel, map, map_area
|
|
from titles.chuni.schema.profile import profile, profile_ex, option, option_ex
|
|
from titles.chuni.schema.profile import recent_rating, activity, charge, emoney
|
|
from titles.chuni.schema.profile import overpower
|
|
from titles.chuni.schema.score import best_score, course
|
|
|
|
chuni_profiles = []
|
|
chuni_items = []
|
|
chuni_scores = []
|
|
|
|
if "SDBT" in profiles:
|
|
chuni_profiles = profiles["SDBT"]
|
|
if "SDBT" in items:
|
|
chuni_items = items["SDBT"]
|
|
if "SDBT" in scores:
|
|
chuni_scores = scores["SDBT"]
|
|
if "SDHD" in profiles:
|
|
chuni_profiles += profiles["SDHD"]
|
|
if "SDHD" in items:
|
|
chuni_items += items["SDHD"]
|
|
if "SDHD" in scores:
|
|
chuni_scores += scores["SDHD"]
|
|
|
|
self.logger.info(f"Importing {len(chuni_profiles)} chunithm/chunithm new profiles")
|
|
|
|
for pf in chuni_profiles:
|
|
if type(pf["data"]) is not dict:
|
|
pf["data"] = json.loads(pf["data"])
|
|
pf_data = pf["data"]
|
|
|
|
# data
|
|
if "userData" in pf_data:
|
|
pf_data["userData"]["userName"] = bytes([ord(c) for c in pf_data["userData"]["userName"]]).decode("utf-8")
|
|
pf_data["userData"]["user"] = pf["user"]
|
|
pf_data["userData"]["version"] = pf["version"]
|
|
pf_data["userData"].pop("accessCode")
|
|
|
|
if pf_data["userData"]["lastRomVersion"].startswith("2."):
|
|
pf_data["userData"]["version"] += 10
|
|
|
|
pf_data["userData"] = self.base.fix_bools(pf_data["userData"])
|
|
|
|
sql = Insert(profile).values(**pf_data["userData"])
|
|
conflict = sql.on_duplicate_key_update(**pf_data["userData"])
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile data for {pf['user']}")
|
|
continue
|
|
self.logger.info(f"Inserted chuni profile for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# data_ex
|
|
if "userDataEx" in pf_data and len(pf_data["userDataEx"]) > 0:
|
|
pf_data["userDataEx"][0]["user"] = pf["user"]
|
|
pf_data["userDataEx"][0]["version"] = pf["version"]
|
|
|
|
pf_data["userDataEx"] = self.base.fix_bools(pf_data["userDataEx"][0])
|
|
|
|
sql = Insert(profile_ex).values(**pf_data["userDataEx"])
|
|
conflict = sql.on_duplicate_key_update(**pf_data["userDataEx"])
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile data_ex for {pf['user']}")
|
|
continue
|
|
self.logger.info(f"Inserted chuni profile data_ex for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# option
|
|
if "userGameOption" in pf_data:
|
|
pf_data["userGameOption"]["user"] = pf["user"]
|
|
|
|
pf_data["userGameOption"] = self.base.fix_bools(pf_data["userGameOption"])
|
|
|
|
sql = Insert(option).values(**pf_data["userGameOption"])
|
|
conflict = sql.on_duplicate_key_update(**pf_data["userGameOption"])
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile options for {pf['user']}")
|
|
continue
|
|
self.logger.info(f"Inserted chuni profile options for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# option_ex
|
|
if "userGameOptionEx" in pf_data and len(pf_data["userGameOptionEx"]) > 0:
|
|
pf_data["userGameOptionEx"][0]["user"] = pf["user"]
|
|
|
|
pf_data["userGameOptionEx"] = self.base.fix_bools(pf_data["userGameOptionEx"][0])
|
|
|
|
sql = Insert(option_ex).values(**pf_data["userGameOptionEx"])
|
|
conflict = sql.on_duplicate_key_update(**pf_data["userGameOptionEx"])
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile option_ex for {pf['user']}")
|
|
continue
|
|
self.logger.info(f"Inserted chuni profile option_ex for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# recent_rating
|
|
if "userRecentRatingList" in pf_data:
|
|
rr = {
|
|
"user": pf["user"],
|
|
"recentRating": pf_data["userRecentRatingList"]
|
|
}
|
|
|
|
sql = Insert(recent_rating).values(**rr)
|
|
conflict = sql.on_duplicate_key_update(**rr)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile recent_rating for {pf['user']}")
|
|
continue
|
|
self.logger.info(f"Inserted chuni profile recent_rating for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# activity
|
|
if "userActivityList" in pf_data:
|
|
for act in pf_data["userActivityList"]:
|
|
act["user"] = pf["user"]
|
|
|
|
sql = Insert(activity).values(**act)
|
|
conflict = sql.on_duplicate_key_update(**act)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile activity for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni profile activity for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# charge
|
|
if "userChargeList" in pf_data:
|
|
for cg in pf_data["userChargeList"]:
|
|
cg["user"] = pf["user"]
|
|
|
|
cg = self.base.fix_bools(cg)
|
|
|
|
sql = Insert(charge).values(**cg)
|
|
conflict = sql.on_duplicate_key_update(**cg)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile charge for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni profile charge for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# emoney
|
|
if "userEmoneyList" in pf_data:
|
|
for emon in pf_data["userEmoneyList"]:
|
|
emon["user"] = pf["user"]
|
|
|
|
sql = Insert(emoney).values(**emon)
|
|
conflict = sql.on_duplicate_key_update(**emon)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile emoney for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni profile emoney for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# overpower
|
|
if "userOverPowerList" in pf_data:
|
|
for op in pf_data["userOverPowerList"]:
|
|
op["user"] = pf["user"]
|
|
|
|
sql = Insert(overpower).values(**op)
|
|
conflict = sql.on_duplicate_key_update(**op)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni profile overpower for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni profile overpower for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# map_area
|
|
if "userMapAreaList" in pf_data:
|
|
for ma in pf_data["userMapAreaList"]:
|
|
ma["user"] = pf["user"]
|
|
|
|
ma = self.base.fix_bools(ma)
|
|
|
|
sql = Insert(map_area).values(**ma)
|
|
conflict = sql.on_duplicate_key_update(**ma)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni map_area for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni map_area for {pf['user']} ->{result.lastrowid}")
|
|
|
|
#duel
|
|
if "userDuelList" in pf_data:
|
|
for ma in pf_data["userDuelList"]:
|
|
ma["user"] = pf["user"]
|
|
|
|
ma = self.base.fix_bools(ma)
|
|
|
|
sql = Insert(duel).values(**ma)
|
|
conflict = sql.on_duplicate_key_update(**ma)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni duel for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni duel for {pf['user']} ->{result.lastrowid}")
|
|
|
|
# map
|
|
if "userMapList" in pf_data:
|
|
for ma in pf_data["userMapList"]:
|
|
ma["user"] = pf["user"]
|
|
|
|
ma = self.base.fix_bools(ma)
|
|
|
|
sql = Insert(map).values(**ma)
|
|
conflict = sql.on_duplicate_key_update(**ma)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni map for {pf['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni map for {pf['user']} ->{result.lastrowid}")
|
|
|
|
self.logger.info(f"Importing {len(chuni_items)} chunithm/chunithm new items")
|
|
|
|
for i in chuni_items:
|
|
if type(i["data"]) is not dict:
|
|
i["data"] = json.loads(i["data"])
|
|
i_data = i["data"]
|
|
|
|
i_data["user"] = i["user"]
|
|
|
|
i_data = self.base.fix_bools(i_data)
|
|
|
|
try: i_data.pop("assignIllust")
|
|
except: pass
|
|
|
|
try: i_data.pop("exMaxLv")
|
|
except: pass
|
|
|
|
if i["type"] == 20: #character
|
|
sql = Insert(character).values(**i_data)
|
|
else:
|
|
sql = Insert(item).values(**i_data)
|
|
|
|
conflict = sql.on_duplicate_key_update(**i_data)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert chuni item for user {i['user']}")
|
|
|
|
else:
|
|
self.logger.info(f"Inserted chuni item for user {i['user']} {i['item_id']} -> {result.lastrowid}")
|
|
|
|
self.logger.info(f"Importing {len(chuni_scores)} chunithm/chunithm new scores")
|
|
|
|
for sc in chuni_scores:
|
|
if type(sc["data"]) is not dict:
|
|
sc["data"] = json.loads(sc["data"])
|
|
|
|
score_data = self.base.fix_bools(sc["data"])
|
|
|
|
try: score_data.pop("theoryCount")
|
|
except: pass
|
|
|
|
try: score_data.pop("ext1")
|
|
except: pass
|
|
|
|
score_data["user"] = sc["user"]
|
|
|
|
sql = Insert(best_score).values(**score_data)
|
|
conflict = sql.on_duplicate_key_update(**score_data)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to put chuni score for user {sc['user']}")
|
|
else:
|
|
self.logger.info(f"Inserted chuni score for user {sc['user']} {sc['song_id']}/{sc['chart_id']} -> {result.lastrowid}")
|
|
|
|
else:
|
|
self.logger.info(f"Chuni/Chusan not found, skipping...")
|
|
|
|
# CXB
|
|
if os.path.exists("titles/cxb/schema"):
|
|
from titles.cxb.schema.item import energy
|
|
from titles.cxb.schema.profile import profile
|
|
from titles.cxb.schema.score import score, ranking
|
|
|
|
cxb_profiles = []
|
|
cxb_items = []
|
|
cxb_scores = []
|
|
|
|
if "SDCA" in profiles:
|
|
cxb_profiles = profiles["SDCA"]
|
|
if "SDCA" in items:
|
|
cxb_items = items["SDCA"]
|
|
if "SDCA" in scores:
|
|
cxb_scores = scores["SDCA"]
|
|
|
|
self.logger.info(f"Importing {len(cxb_profiles)} CXB profiles")
|
|
|
|
for pf in cxb_profiles:
|
|
user = pf["user"]
|
|
version = pf["version"]
|
|
pf_data = pf["data"]["data"]
|
|
pf_idx = pf["data"]["index"]
|
|
|
|
for x in range(len(pf_data)):
|
|
sql = Insert(profile).values(
|
|
user = user,
|
|
version = version,
|
|
index = int(pf_idx[x]),
|
|
data = json.loads(pf_data[x]) if type(pf_data[x]) is not dict else pf_data[x]
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
version = version,
|
|
index = int(pf_idx[x]),
|
|
data = json.loads(pf_data[x]) if type(pf_data[x]) is not dict else pf_data[x]
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert CXB profile for user {user} Index {pf_idx[x]}")
|
|
|
|
self.logger.info(f"Importing {len(cxb_scores)} CXB scores")
|
|
|
|
for sc in cxb_scores:
|
|
user = sc["user"]
|
|
version = sc["version"]
|
|
mcode = sc["data"]["mcode"]
|
|
index = sc["data"]["index"]
|
|
|
|
sql = Insert(score).values(
|
|
user = user,
|
|
game_version = version,
|
|
song_mcode = mcode,
|
|
song_index = index,
|
|
data = sc["data"]
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
game_version = version,
|
|
song_mcode = mcode,
|
|
song_index = index,
|
|
data = sc["data"]
|
|
)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert CXB score for user {user} mcode {mcode}")
|
|
|
|
self.logger.info(f"Importing {len(cxb_items)} CXB items")
|
|
|
|
for it in cxb_items:
|
|
user = it["user"]
|
|
|
|
if it["type"] == 3: # energy
|
|
sql = Insert(energy).values(
|
|
user = user,
|
|
energy = it["data"]["total"]
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
energy = it["data"]["total"]
|
|
)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert CXB energy for user {user}")
|
|
|
|
elif it["type"] == 2:
|
|
sql = Insert(ranking).values(
|
|
user = user,
|
|
rev_id = it["data"]["rid"],
|
|
song_id = it["data"]["sc"][1] if len(it["data"]["sc"]) > 1 else None,
|
|
score = it["data"]["sc"][0],
|
|
clear = it["data"]["clear"],
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
rev_id = it["data"]["rid"],
|
|
song_id = it["data"]["sc"][1] if len(it["data"]["sc"]) > 1 else None,
|
|
score = it["data"]["sc"][0],
|
|
clear = it["data"]["clear"],
|
|
)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert CXB ranking for user {user}")
|
|
|
|
else:
|
|
self.logger.error(f"Unknown CXB item type {it['type']} for user {user}")
|
|
|
|
else:
|
|
self.logger.info(f"CXB not found, skipping...")
|
|
|
|
# Diva
|
|
if os.path.exists("titles/diva/schema"):
|
|
from titles.diva.schema.profile import profile
|
|
from titles.diva.schema.score import score
|
|
from titles.diva.schema.item import shop
|
|
|
|
diva_profiles = []
|
|
diva_scores = []
|
|
|
|
if "SBZV" in profiles:
|
|
diva_profiles = profiles["SBZV"]
|
|
if "SBZV" in scores:
|
|
diva_scores = scores["SBZV"]
|
|
|
|
self.logger.info(f"Importing {len(diva_profiles)} Diva profiles")
|
|
|
|
for pf in diva_profiles:
|
|
pf["data"]["user"] = pf["user"]
|
|
pf["data"]["version"] = pf["version"]
|
|
pf_data = pf["data"]
|
|
|
|
if "mdl_eqp_ary" in pf["data"]:
|
|
sql = Insert(shop).values(
|
|
user = user,
|
|
version = version,
|
|
mdl_eqp_ary = pf["data"]["mdl_eqp_ary"],
|
|
)
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
version = version,
|
|
mdl_eqp_ary = pf["data"]["mdl_eqp_ary"]
|
|
)
|
|
self.base.execute(conflict)
|
|
pf["data"].pop("mdl_eqp_ary")
|
|
|
|
sql = Insert(profile).values(**pf_data)
|
|
conflict = sql.on_duplicate_key_update(**pf_data)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert diva profile for {pf['user']}")
|
|
|
|
self.logger.info(f"Importing {len(diva_scores)} Diva scores")
|
|
|
|
for sc in diva_scores:
|
|
user = sc["user"]
|
|
|
|
clr_kind = -1
|
|
for x in sc["data"]["stg_clr_kind"].split(","):
|
|
if x != "-1":
|
|
clr_kind = x
|
|
|
|
cool_ct = 0
|
|
for x in sc["data"]["stg_cool_cnt"].split(","):
|
|
if x != "0":
|
|
cool_ct = x
|
|
|
|
fine_ct = 0
|
|
for x in sc["data"]["stg_fine_cnt"].split(","):
|
|
if x != "0":
|
|
fine_ct = x
|
|
|
|
safe_ct = 0
|
|
for x in sc["data"]["stg_safe_cnt"].split(","):
|
|
if x != "0":
|
|
safe_ct = x
|
|
|
|
sad_ct = 0
|
|
for x in sc["data"]["stg_sad_cnt"].split(","):
|
|
if x != "0":
|
|
sad_ct = x
|
|
|
|
worst_ct = 0
|
|
for x in sc["data"]["stg_wt_wg_cnt"].split(","):
|
|
if x != "0":
|
|
worst_ct = x
|
|
|
|
max_cmb = 0
|
|
for x in sc["data"]["stg_max_cmb"].split(","):
|
|
if x != "0":
|
|
max_cmb = x
|
|
|
|
sql = Insert(score).values(
|
|
user = user,
|
|
version = sc["version"],
|
|
pv_id = sc["song_id"],
|
|
difficulty = sc["chart_id"],
|
|
score = sc["score1"],
|
|
atn_pnt = sc["score2"],
|
|
clr_kind = clr_kind,
|
|
sort_kind = sc["data"]["sort_kind"],
|
|
cool = cool_ct,
|
|
fine = fine_ct,
|
|
safe = safe_ct,
|
|
sad = sad_ct,
|
|
worst = worst_ct,
|
|
max_combo = max_cmb,
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(user = user,
|
|
version = sc["version"],
|
|
pv_id = sc["song_id"],
|
|
difficulty = sc["chart_id"],
|
|
score = sc["score1"],
|
|
atn_pnt = sc["score2"],
|
|
clr_kind = clr_kind,
|
|
sort_kind = sc["data"]["sort_kind"],
|
|
cool = cool_ct,
|
|
fine = fine_ct,
|
|
safe = safe_ct,
|
|
sad = sad_ct,
|
|
worst = worst_ct,
|
|
max_combo = max_cmb
|
|
)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert diva score for {pf['user']}")
|
|
|
|
else:
|
|
self.logger.info(f"Diva not found, skipping...")
|
|
|
|
# Ongeki
|
|
if os.path.exists("titles/ongeki/schema"):
|
|
from titles.ongeki.schema.item import card, deck, character, boss, story
|
|
from titles.ongeki.schema.item import chapter, item, music_item, login_bonus
|
|
from titles.ongeki.schema.item import event_point, mission_point, scenerio
|
|
from titles.ongeki.schema.item import trade_item, event_music, tech_event
|
|
from titles.ongeki.schema.profile import profile, option, activity, recent_rating
|
|
from titles.ongeki.schema.profile import rating_log, training_room, kop
|
|
from titles.ongeki.schema.score import score_best, tech_count, playlog
|
|
from titles.ongeki.schema.log import session_log
|
|
|
|
item_types = {
|
|
"character": 20,
|
|
"story": 21,
|
|
"card": 22,
|
|
"deck": 23,
|
|
"login": 24,
|
|
"chapter": 25
|
|
}
|
|
|
|
ongeki_profiles = []
|
|
ongeki_items = []
|
|
ongeki_scores = []
|
|
|
|
if "SDDT" in profiles:
|
|
ongeki_profiles = profiles["SDDT"]
|
|
if "SDDT" in items:
|
|
ongeki_items = items["SDDT"]
|
|
if "SDDT" in scores:
|
|
ongeki_scores = scores["SDDT"]
|
|
|
|
self.logger.info(f"Importing {len(ongeki_profiles)} ongeki profiles")
|
|
|
|
for pf in ongeki_profiles:
|
|
user = pf["user"]
|
|
version = pf["version"]
|
|
pf_data = pf["data"]
|
|
|
|
pf_data["userData"]["user"] = user
|
|
pf_data["userData"]["version"] = version
|
|
pf_data["userData"].pop("accessCode")
|
|
|
|
sql = Insert(profile).values(**pf_data["userData"])
|
|
conflict = sql.on_duplicate_key_update(**pf_data["userData"])
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile data for user {pf['user']}")
|
|
continue
|
|
|
|
pf_data["userOption"]["user"] = user
|
|
|
|
sql = Insert(option).values(**pf_data["userOption"])
|
|
conflict = sql.on_duplicate_key_update(**pf_data["userOption"])
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile options for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userActivityList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(activity).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile activity for user {pf['user']}")
|
|
continue
|
|
|
|
sql = Insert(recent_rating).values(
|
|
user = user,
|
|
recentRating = pf_data["userRecentRatingList"]
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
recentRating = pf_data["userRecentRatingList"]
|
|
)
|
|
result = self.base.execute(conflict)
|
|
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile recent rating for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userRatinglogList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(rating_log).values(**pf_list)
|
|
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile rating log for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userTrainingRoomList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(training_room).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile training room for user {pf['user']}")
|
|
continue
|
|
|
|
if "userKopList" in pf_data:
|
|
for pf_list in pf_data["userKopList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(kop).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki profile training room for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userBossList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(boss).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item boss for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userDeckList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(deck).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item deck for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userStoryList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(story).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item story for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userChapterList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(chapter).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item chapter for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userPlaylogList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(playlog).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki score playlog for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userMusicItemList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(music_item).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item music item for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userTechCountList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(tech_count).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item tech count for user {pf['user']}")
|
|
continue
|
|
|
|
if "userTechEventList" in pf_data:
|
|
for pf_list in pf_data["userTechEventList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(tech_event).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item tech event for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userTradeItemList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(trade_item).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item trade item for user {pf['user']}")
|
|
continue
|
|
|
|
if "userEventMusicList" in pf_data:
|
|
for pf_list in pf_data["userEventMusicList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(event_music).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item event music for user {pf['user']}")
|
|
continue
|
|
|
|
if "userEventPointList" in pf_data:
|
|
for pf_list in pf_data["userEventPointList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(event_point).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item event point for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userLoginBonusList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(login_bonus).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item login bonus for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userMissionPointList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(mission_point).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item mission point for user {pf['user']}")
|
|
continue
|
|
|
|
for pf_list in pf_data["userScenarioList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(scenerio).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item scenerio for user {pf['user']}")
|
|
continue
|
|
|
|
if "userSessionlogList" in pf_data:
|
|
for pf_list in pf_data["userSessionlogList"]:
|
|
pf_list["user"] = user
|
|
|
|
sql = Insert(session_log).values(**pf_list)
|
|
conflict = sql.on_duplicate_key_update(**pf_list)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki log session for user {pf['user']}")
|
|
continue
|
|
|
|
self.logger.info(f"Importing {len(ongeki_items)} ongeki items")
|
|
|
|
for it in ongeki_items:
|
|
user = it["user"]
|
|
it_type = it["type"]
|
|
it_id = it["item_id"]
|
|
it_data = it["data"]
|
|
it_data["user"] = user
|
|
|
|
if it_type == item_types["character"] and "characterId" in it_data:
|
|
sql = Insert(character).values(**it_data)
|
|
|
|
elif it_type == item_types["story"]:
|
|
sql = Insert(story).values(**it_data)
|
|
|
|
elif it_type == item_types["card"]:
|
|
sql = Insert(card).values(**it_data)
|
|
|
|
elif it_type == item_types["deck"]:
|
|
sql = Insert(deck).values(**it_data)
|
|
|
|
elif it_type == item_types["login"]: # login bonus
|
|
sql = Insert(login_bonus).values(**it_data)
|
|
|
|
elif it_type == item_types["chapter"]:
|
|
sql = Insert(chapter).values(**it_data)
|
|
|
|
else:
|
|
sql = Insert(item).values(**it_data)
|
|
|
|
conflict = sql.on_duplicate_key_update(**it_data)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki item {it_id} kind {it_type} for user {user}")
|
|
|
|
self.logger.info(f"Importing {len(ongeki_scores)} ongeki scores")
|
|
|
|
for sc in ongeki_scores:
|
|
user = sc["user"]
|
|
sc_data = sc["data"]
|
|
sc_data["user"] = user
|
|
|
|
sql = Insert(score_best).values(**sc_data)
|
|
conflict = sql.on_duplicate_key_update(**sc_data)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert ongeki score for user {user}: {sc['song_id']}/{sc['chart_id']}")
|
|
|
|
else:
|
|
self.logger.info(f"Ongeki not found, skipping...")
|
|
|
|
# Wacca
|
|
if os.path.exists("titles/wacca/schema"):
|
|
from titles.wacca.schema.profile import profile, option, bingo, gate, favorite
|
|
from titles.wacca.schema.item import item, ticket, song_unlock, trophy
|
|
from titles.wacca.schema.score import best_score, stageup
|
|
from titles.wacca.reverse import WaccaReverse
|
|
from titles.wacca.const import WaccaConstants
|
|
|
|
default_opts = WaccaReverse.OPTIONS_DEFAULTS
|
|
opts = WaccaConstants.OPTIONS
|
|
item_types = WaccaConstants.ITEM_TYPES
|
|
|
|
wacca_profiles = []
|
|
wacca_items = []
|
|
wacca_scores = []
|
|
wacca_achievements = []
|
|
|
|
if "SDFE" in profiles:
|
|
wacca_profiles = profiles["SDFE"]
|
|
if "SDFE" in items:
|
|
wacca_items = items["SDFE"]
|
|
if "SDFE" in scores:
|
|
wacca_scores = scores["SDFE"]
|
|
if "SDFE" in achievements:
|
|
wacca_achievements = achievements["SDFE"]
|
|
|
|
self.logger.info(f"Importing {len(wacca_profiles)} wacca profiles")
|
|
|
|
for pf in wacca_profiles:
|
|
if pf["version"] == 0 or pf["version"] == 1:
|
|
season = 1
|
|
elif pf["version"] == 2 or pf["version"] == 3:
|
|
season = 2
|
|
elif pf["version"] >= 4:
|
|
season = 3
|
|
|
|
if type(pf["data"]) is not dict:
|
|
pf["data"] = json.loads(pf["data"])
|
|
|
|
try:
|
|
sql = Insert(profile).values(
|
|
id = pf["id"],
|
|
user = pf["user"],
|
|
version = pf["version"],
|
|
season = season,
|
|
username = pf["data"]["profile"]["username"] if "username" in pf["data"]["profile"] else pf["name"],
|
|
xp = pf["data"]["profile"]["xp"],
|
|
xp_season = pf["data"]["profile"]["xp"],
|
|
wp = pf["data"]["profile"]["wp"],
|
|
wp_season = pf["data"]["profile"]["wp"],
|
|
wp_total = pf["data"]["profile"]["total_wp_gained"],
|
|
dan_type = pf["data"]["profile"]["dan_type"],
|
|
dan_level = pf["data"]["profile"]["dan_level"],
|
|
title_0 = pf["data"]["profile"]["title_part_ids"][0],
|
|
title_1 = pf["data"]["profile"]["title_part_ids"][1],
|
|
title_2 = pf["data"]["profile"]["title_part_ids"][2],
|
|
rating = pf["data"]["profile"]["rating"],
|
|
vip_expire_time = datetime.fromtimestamp(pf["data"]["profile"]["vip_expire_time"]) if "vip_expire_time" in pf["data"]["profile"] else None,
|
|
login_count = pf["use_count"],
|
|
playcount_single = pf["use_count"],
|
|
playcount_single_season = pf["use_count"],
|
|
last_game_ver = pf["data"]["profile"]["last_game_ver"],
|
|
last_song_id = pf["data"]["profile"]["last_song_info"][0] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_song_difficulty = pf["data"]["profile"]["last_song_info"][1] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_folder_order = pf["data"]["profile"]["last_song_info"][2] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_folder_id = pf["data"]["profile"]["last_song_info"][3] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_song_order = pf["data"]["profile"]["last_song_info"][4] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_login_date = datetime.fromtimestamp(pf["data"]["profile"]["last_login_timestamp"]),
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
id = pf["id"],
|
|
user = pf["user"],
|
|
version = pf["version"],
|
|
season = season,
|
|
username = pf["data"]["profile"]["username"] if "username" in pf["data"]["profile"] else pf["name"],
|
|
xp = pf["data"]["profile"]["xp"],
|
|
xp_season = pf["data"]["profile"]["xp"],
|
|
wp = pf["data"]["profile"]["wp"],
|
|
wp_season = pf["data"]["profile"]["wp"],
|
|
wp_total = pf["data"]["profile"]["total_wp_gained"],
|
|
dan_type = pf["data"]["profile"]["dan_type"],
|
|
dan_level = pf["data"]["profile"]["dan_level"],
|
|
title_0 = pf["data"]["profile"]["title_part_ids"][0],
|
|
title_1 = pf["data"]["profile"]["title_part_ids"][1],
|
|
title_2 = pf["data"]["profile"]["title_part_ids"][2],
|
|
rating = pf["data"]["profile"]["rating"],
|
|
vip_expire_time = datetime.fromtimestamp(pf["data"]["profile"]["vip_expire_time"]) if "vip_expire_time" in pf["data"]["profile"] else None,
|
|
login_count = pf["use_count"],
|
|
playcount_single = pf["use_count"],
|
|
playcount_single_season = pf["use_count"],
|
|
last_game_ver = pf["data"]["profile"]["last_game_ver"],
|
|
last_song_id = pf["data"]["profile"]["last_song_info"][0] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_song_difficulty = pf["data"]["profile"]["last_song_info"][1] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_folder_order = pf["data"]["profile"]["last_song_info"][2] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_folder_id = pf["data"]["profile"]["last_song_info"][3] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_song_order = pf["data"]["profile"]["last_song_info"][4] if "last_song_info" in pf["data"]["profile"] else 0,
|
|
last_login_date = datetime.fromtimestamp(pf["data"]["profile"]["last_login_timestamp"]),
|
|
)
|
|
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert wacca profile for user {pf['user']}")
|
|
continue
|
|
|
|
for opt, val in pf["data"]["option"].items():
|
|
if val != default_opts[opt]:
|
|
opt_id = opts[opt]
|
|
sql = Insert(option).values(
|
|
user = pf["user"],
|
|
opt_id = opt_id,
|
|
value = val,
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = pf["user"],
|
|
opt_id = opt_id,
|
|
value = val,
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert wacca option for user {pf['user']} {opt} -> {val}")
|
|
|
|
except KeyError as e:
|
|
self.logger.warn(f"Outdated wacca profile, skipping: {e}")
|
|
|
|
if "gate" in pf["data"]:
|
|
for profile_gate in pf["data"]["gate"]:
|
|
sql = Insert(gate).values(
|
|
user = pf["user"],
|
|
gate_id = profile_gate["id"],
|
|
page = profile_gate["page"],
|
|
loops = profile_gate["loops"],
|
|
progress = profile_gate["progress"],
|
|
last_used = datetime.fromtimestamp(profile_gate["last_used"]),
|
|
mission_flag = profile_gate["mission_flag"],
|
|
total_points = profile_gate["total_points"],
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = pf["user"],
|
|
gate_id = profile_gate["id"],
|
|
page = profile_gate["page"],
|
|
loops = profile_gate["loops"],
|
|
progress = profile_gate["progress"],
|
|
last_used = datetime.fromtimestamp(profile_gate["last_used"]),
|
|
mission_flag = profile_gate["mission_flag"],
|
|
total_points = profile_gate["total_points"],
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert wacca gate for user {pf['user']} -> {profile_gate['id']}")
|
|
continue
|
|
|
|
if "favorite" in pf["data"]:
|
|
for profile_favorite in pf["data"]["favorite"]:
|
|
sql = Insert(favorite).values(
|
|
user = pf["user"],
|
|
song_id = profile_favorite
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = pf["user"],
|
|
song_id = profile_favorite
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert wacca favorite songs for user {pf['user']} -> {profile_favorite}")
|
|
continue
|
|
|
|
for it in wacca_items:
|
|
user = it["user"]
|
|
item_type = it["type"]
|
|
item_id = it["item_id"]
|
|
|
|
if type(it["data"]) is not dict:
|
|
it["data"] = json.loads(it["data"])
|
|
|
|
if item_type == item_types["ticket"]:
|
|
if "quantity" in it["data"]:
|
|
for x in range(it["data"]["quantity"]):
|
|
sql = Insert(ticket).values(
|
|
user = user,
|
|
ticket_id = item_id,
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
ticket_id = item_id,
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.warn(f"Wacca: Failed to insert ticket {item_id} for user {user}")
|
|
|
|
elif item_type == item_types["music_unlock"] or item_type == item_types["music_difficulty_unlock"]:
|
|
diff = 0
|
|
if "difficulty" in it["data"]:
|
|
for x in it["data"]["difficulty"]:
|
|
if x == 1:
|
|
diff += 1
|
|
else:
|
|
break
|
|
|
|
sql = Insert(song_unlock).values(
|
|
user = user,
|
|
song_id = item_id,
|
|
highest_difficulty = diff,
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
song_id = item_id,
|
|
highest_difficulty = diff,
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.warn(f"Wacca: Failed to insert song unlock {item_id} {diff} for user {user}")
|
|
|
|
elif item_type == item_types["trophy"]:
|
|
season = int(item_id / 100000)
|
|
sql = Insert(trophy).values(
|
|
user = user,
|
|
trophy_id = item_id,
|
|
season = season,
|
|
progress = 0 if "progress" not in it["data"] else it["data"]["progress"],
|
|
badge_type = 0 # ???
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
trophy_id = item_id,
|
|
season = season,
|
|
progress = 0 if "progress" not in it["data"] else it["data"]["progress"],
|
|
badge_type = 0 # ???
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.warn(f"Wacca: Failed to insert trophy {item_id} for user {user}")
|
|
|
|
else:
|
|
sql = Insert(item).values(
|
|
user = user,
|
|
item_id = item_id,
|
|
type = item_type,
|
|
acquire_date = datetime.fromtimestamp(it["data"]["obtainedDate"]) if "obtainedDate" in it["data"] else datetime.now(),
|
|
use_count = it["data"]["uses"] if "uses" in it["data"] else 0,
|
|
use_count_season = it["data"]["season_uses"] if "season_uses" in it["data"] else 0
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = user,
|
|
item_id = item_id,
|
|
type = item_type,
|
|
acquire_date = datetime.fromtimestamp(it["data"]["obtainedDate"]) if "obtainedDate" in it["data"] else datetime.now(),
|
|
use_count = it["data"]["uses"] if "uses" in it["data"] else 0,
|
|
use_count_season = it["data"]["season_uses"] if "season_uses" in it["data"] else 0
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.warn(f"Wacca: Failed to insert trophy {item_id} for user {user}")
|
|
|
|
for sc in wacca_scores:
|
|
if type(sc["data"]) is not dict:
|
|
sc["data"] = json.loads(sc["data"])
|
|
|
|
sql = Insert(best_score).values(
|
|
user = sc["user"],
|
|
song_id = int(sc["song_id"]),
|
|
chart_id = sc["chart_id"],
|
|
score = sc["score1"],
|
|
play_ct = 1 if "play_count" not in sc["data"] else sc["data"]["play_count"],
|
|
clear_ct = 1 if sc["cleared"] & 0x01 else 0,
|
|
missless_ct = 1 if sc["cleared"] & 0x02 else 0,
|
|
fullcombo_ct = 1 if sc["cleared"] & 0x04 else 0,
|
|
allmarv_ct = 1 if sc["cleared"] & 0x08 else 0,
|
|
grade_d_ct = 1 if sc["grade"] & 0x01 else 0,
|
|
grade_c_ct = 1 if sc["grade"] & 0x02 else 0,
|
|
grade_b_ct = 1 if sc["grade"] & 0x04 else 0,
|
|
grade_a_ct = 1 if sc["grade"] & 0x08 else 0,
|
|
grade_aa_ct = 1 if sc["grade"] & 0x10 else 0,
|
|
grade_aaa_ct = 1 if sc["grade"] & 0x20 else 0,
|
|
grade_s_ct = 1 if sc["grade"] & 0x40 else 0,
|
|
grade_ss_ct = 1 if sc["grade"] & 0x80 else 0,
|
|
grade_sss_ct = 1 if sc["grade"] & 0x100 else 0,
|
|
grade_master_ct = 1 if sc["grade"] & 0x200 else 0,
|
|
grade_sp_ct = 1 if sc["grade"] & 0x400 else 0,
|
|
grade_ssp_ct = 1 if sc["grade"] & 0x800 else 0,
|
|
grade_sssp_ct = 1 if sc["grade"] & 0x1000 else 0,
|
|
best_combo = 0 if "max_combo" not in sc["data"] else sc["data"]["max_combo"],
|
|
lowest_miss_ct = 0 if "lowest_miss_count" not in sc["data"] else sc["data"]["lowest_miss_count"],
|
|
rating = 0 if "rating" not in sc["data"] else sc["data"]["rating"],
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = sc["user"],
|
|
song_id = int(sc["song_id"]),
|
|
chart_id = sc["chart_id"],
|
|
score = sc["score1"],
|
|
play_ct = 1 if "play_count" not in sc["data"] else sc["data"]["play_count"],
|
|
clear_ct = 1 if sc["cleared"] & 0x01 else 0,
|
|
missless_ct = 1 if sc["cleared"] & 0x02 else 0,
|
|
fullcombo_ct = 1 if sc["cleared"] & 0x04 else 0,
|
|
allmarv_ct = 1 if sc["cleared"] & 0x08 else 0,
|
|
grade_d_ct = 1 if sc["grade"] & 0x01 else 0,
|
|
grade_c_ct = 1 if sc["grade"] & 0x02 else 0,
|
|
grade_b_ct = 1 if sc["grade"] & 0x04 else 0,
|
|
grade_a_ct = 1 if sc["grade"] & 0x08 else 0,
|
|
grade_aa_ct = 1 if sc["grade"] & 0x10 else 0,
|
|
grade_aaa_ct = 1 if sc["grade"] & 0x20 else 0,
|
|
grade_s_ct = 1 if sc["grade"] & 0x40 else 0,
|
|
grade_ss_ct = 1 if sc["grade"] & 0x80 else 0,
|
|
grade_sss_ct = 1 if sc["grade"] & 0x100 else 0,
|
|
grade_master_ct = 1 if sc["grade"] & 0x200 else 0,
|
|
grade_sp_ct = 1 if sc["grade"] & 0x400 else 0,
|
|
grade_ssp_ct = 1 if sc["grade"] & 0x800 else 0,
|
|
grade_sssp_ct = 1 if sc["grade"] & 0x1000 else 0,
|
|
best_combo = 0 if "max_combo" not in sc["data"] else sc["data"]["max_combo"],
|
|
lowest_miss_ct = 0 if "lowest_miss_count" not in sc["data"] else sc["data"]["lowest_miss_count"],
|
|
rating = 0 if "rating" not in sc["data"] else sc["data"]["rating"],
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert wacca score for user {sc['user']} {int(sc['song_id'])} {sc['chart_id']}")
|
|
|
|
for ach in wacca_achievements:
|
|
if ach["version"] == 0 or ach["version"] == 1:
|
|
season = 1
|
|
elif ach["version"] == 2 or ach["version"] == 3:
|
|
season = 2
|
|
elif ach["version"] >= 4:
|
|
season = 3
|
|
|
|
if type(ach["data"]) is not dict:
|
|
ach["data"] = json.loads(ach["data"])
|
|
|
|
sql = Insert(stageup).values(
|
|
user = ach["user"],
|
|
season = season,
|
|
stage_id = ach["achievement_id"],
|
|
clear_status = 0 if "clear" not in ach["data"] else ach["data"]["clear"],
|
|
clear_song_ct = 0 if "clear_song_ct" not in ach["data"] else ach["data"]["clear_song_ct"],
|
|
song1_score = 0 if "score1" not in ach["data"] else ach["data"]["score1"],
|
|
song2_score = 0 if "score2" not in ach["data"] else ach["data"]["score2"],
|
|
song3_score = 0 if "score3" not in ach["data"] else ach["data"]["score3"],
|
|
play_ct = 1 if "attemps" not in ach["data"] else ach["data"]["attemps"],
|
|
)
|
|
|
|
conflict = sql.on_duplicate_key_update(
|
|
user = ach["user"],
|
|
season = season,
|
|
stage_id = ach["achievement_id"],
|
|
clear_status = 0 if "clear" not in ach["data"] else ach["data"]["clear"],
|
|
clear_song_ct = 0 if "clear_song_ct" not in ach["data"] else ach["data"]["clear_song_ct"],
|
|
song1_score = 0 if "score1" not in ach["data"] else ach["data"]["score1"],
|
|
song2_score = 0 if "score2" not in ach["data"] else ach["data"]["score2"],
|
|
song3_score = 0 if "score3" not in ach["data"] else ach["data"]["score3"],
|
|
play_ct = 1 if "attemps" not in ach["data"] else ach["data"]["attemps"],
|
|
)
|
|
result = self.base.execute(conflict)
|
|
if result is None:
|
|
self.logger.error(f"Failed to insert wacca achievement for user {ach['user']}")
|
|
|
|
else:
|
|
self.logger.info(f"Wacca not found, skipping...")
|