forked from Hay1tsme/artemis
mai2: add opts to reader
This commit is contained in:
@ -1,20 +1,16 @@
|
|||||||
from decimal import Decimal
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
import zlib
|
import zlib
|
||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.data import Data
|
|
||||||
from read import BaseReader
|
from read import BaseReader
|
||||||
from titles.mai2.const import Mai2Constants
|
from titles.mai2.const import Mai2Constants
|
||||||
from titles.mai2.database import Mai2Data
|
from titles.mai2.database import Mai2Data
|
||||||
|
|
||||||
|
|
||||||
class Mai2Reader(BaseReader):
|
class Mai2Reader(BaseReader):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -46,10 +42,11 @@ class Mai2Reader(BaseReader):
|
|||||||
|
|
||||||
for dir in data_dirs:
|
for dir in data_dirs:
|
||||||
self.logger.info(f"Read from {dir}")
|
self.logger.info(f"Read from {dir}")
|
||||||
await self.get_events(f"{dir}/event")
|
this_opt_id = await self.read_opt_info(dir)
|
||||||
|
await self.get_events(f"{dir}/event", this_opt_id)
|
||||||
await self.disable_events(f"{dir}/information", f"{dir}/scoreRanking")
|
await self.disable_events(f"{dir}/information", f"{dir}/scoreRanking")
|
||||||
await self.read_music(f"{dir}/music")
|
await self.read_music(f"{dir}/music", this_opt_id)
|
||||||
await self.read_tickets(f"{dir}/ticket")
|
await self.read_tickets(f"{dir}/ticket", this_opt_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not os.path.exists(f"{self.bin_dir}/tables"):
|
if not os.path.exists(f"{self.bin_dir}/tables"):
|
||||||
@ -179,7 +176,7 @@ class Mai2Reader(BaseReader):
|
|||||||
self.logger.warning("Failed load table content, skipping")
|
self.logger.warning("Failed load table content, skipping")
|
||||||
return
|
return
|
||||||
|
|
||||||
async def get_events(self, base_dir: str) -> None:
|
async def get_events(self, base_dir: str, opt_id: int = None) -> None:
|
||||||
self.logger.info(f"Reading events from {base_dir}...")
|
self.logger.info(f"Reading events from {base_dir}...")
|
||||||
|
|
||||||
for root, dirs, files in os.walk(base_dir):
|
for root, dirs, files in os.walk(base_dir):
|
||||||
@ -193,7 +190,7 @@ class Mai2Reader(BaseReader):
|
|||||||
event_type = int(troot.find("infoType").text)
|
event_type = int(troot.find("infoType").text)
|
||||||
|
|
||||||
await self.data.static.put_game_event(
|
await self.data.static.put_game_event(
|
||||||
self.version, event_type, id, name
|
self.version, event_type, id, name, opt_id
|
||||||
)
|
)
|
||||||
self.logger.info(f"Added event {id}...")
|
self.logger.info(f"Added event {id}...")
|
||||||
|
|
||||||
@ -255,7 +252,7 @@ class Mai2Reader(BaseReader):
|
|||||||
await self.data.static.toggle_game_event(self.version, event_id, toggle=False)
|
await self.data.static.toggle_game_event(self.version, event_id, toggle=False)
|
||||||
self.logger.info(f"Disabled event {event_id}...")
|
self.logger.info(f"Disabled event {event_id}...")
|
||||||
|
|
||||||
async def read_music(self, base_dir: str) -> None:
|
async def read_music(self, base_dir: str, opt_id: int = None) -> None:
|
||||||
self.logger.info(f"Reading music from {base_dir}...")
|
self.logger.info(f"Reading music from {base_dir}...")
|
||||||
|
|
||||||
for root, dirs, files in os.walk(base_dir):
|
for root, dirs, files in os.walk(base_dir):
|
||||||
@ -296,13 +293,14 @@ class Mai2Reader(BaseReader):
|
|||||||
added_ver,
|
added_ver,
|
||||||
diff_num,
|
diff_num,
|
||||||
note_designer,
|
note_designer,
|
||||||
|
opt_id
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Added music id {song_id} chart {chart_id}"
|
f"Added music id {song_id} chart {chart_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
async def read_tickets(self, base_dir: str) -> None:
|
async def read_tickets(self, base_dir: str, opt_id: int = None) -> None:
|
||||||
self.logger.info(f"Reading tickets from {base_dir}...")
|
self.logger.info(f"Reading tickets from {base_dir}...")
|
||||||
|
|
||||||
for root, dirs, files in os.walk(base_dir):
|
for root, dirs, files in os.walk(base_dir):
|
||||||
@ -317,7 +315,7 @@ class Mai2Reader(BaseReader):
|
|||||||
price = int(troot.find("creditNum").text)
|
price = int(troot.find("creditNum").text)
|
||||||
|
|
||||||
await self.data.static.put_game_ticket(
|
await self.data.static.put_game_ticket(
|
||||||
self.version, id, ticket_type, price, name
|
self.version, id, ticket_type, price, name, opt_id
|
||||||
)
|
)
|
||||||
self.logger.info(f"Added ticket {id}...")
|
self.logger.info(f"Added ticket {id}...")
|
||||||
|
|
||||||
@ -341,3 +339,51 @@ class Mai2Reader(BaseReader):
|
|||||||
if scores is None or text is None:
|
if scores is None or text is None:
|
||||||
return
|
return
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
async def read_opt_info(self, directory: str) -> Optional[int]:
|
||||||
|
datacfg_file = os.path.join(directory, "DataConfig.xml")
|
||||||
|
if not os.path.exists(datacfg_file):
|
||||||
|
self.logger.warning(f"{datacfg_file} does not contain DataConfig.xml, opt info will not be read")
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(datacfg_file, encoding="utf-8") as f:
|
||||||
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
|
if troot.find("DataConfig/version") is None:
|
||||||
|
self.logger.warning(f"{directory}/DataConfig.xml contains no Version section, opt info will not be read")
|
||||||
|
return None
|
||||||
|
|
||||||
|
ver_maj = troot.find("DataConfig/version/major")
|
||||||
|
ver_min = troot.find("DataConfig/version/minor")
|
||||||
|
ver_rel = troot.find("DataConfig/version/release")
|
||||||
|
cm_maj = troot.find("DataConfig/cardMakerVersion/major")
|
||||||
|
cm_min = troot.find("DataConfig/cardMakerVersion/minor")
|
||||||
|
cm_rel = troot.find("DataConfig/cardMakerVersion/release")
|
||||||
|
|
||||||
|
if ver_maj is None: # Probably not worth checking that the other sections exist
|
||||||
|
self.logger.warning(f"{datacfg_file} contains no major item in the Version section, opt info will not be read")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if ver_min is None: # Probably not worth checking that the other sections exist
|
||||||
|
self.logger.warning(f"{datacfg_file} contains no minor item in the Version section, opt info will not be read")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if ver_rel is None: # Probably not worth checking that the other sections exist
|
||||||
|
self.logger.warning(f"{datacfg_file} contains no release item in the Version section, opt info will not be read")
|
||||||
|
return None
|
||||||
|
|
||||||
|
opt_folder = os.path.basename(os.path.normpath(directory))
|
||||||
|
opt_id = await self.data.static.get_opt_by_version_folder(self.version, opt_folder)
|
||||||
|
|
||||||
|
if not opt_id:
|
||||||
|
opt_id = await self.data.static.put_opt(self.version, opt_folder, int(ver_rel.text), int(cm_rel.text) if cm_rel else None)
|
||||||
|
if not opt_id:
|
||||||
|
self.logger.error(f"Failed to put opt folder info for {opt_folder}")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
opt_id = opt_id['id']
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"Opt folder {opt_folder} (Database ID {opt_id}) contains v{ver_maj.text}.{ver_min.text}.{ver_rel.text} (cm v{cm_maj.text if cm_maj else 'None'}.{cm_min.text if cm_min else 'None'}.{cm_rel.text if cm_rel else 'None'})"
|
||||||
|
)
|
||||||
|
return opt_id
|
||||||
|
@ -7,6 +7,7 @@ from sqlalchemy.schema import ForeignKey
|
|||||||
from sqlalchemy.sql import func, select
|
from sqlalchemy.sql import func, select
|
||||||
from sqlalchemy.engine import Row
|
from sqlalchemy.engine import Row
|
||||||
from sqlalchemy.dialects.mysql import insert
|
from sqlalchemy.dialects.mysql import insert
|
||||||
|
from sqlalchemy.sql.functions import coalesce
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
opts = Table(
|
opts = Table(
|
||||||
@ -92,16 +93,17 @@ cards = Table(
|
|||||||
|
|
||||||
class Mai2StaticData(BaseData):
|
class Mai2StaticData(BaseData):
|
||||||
async def put_game_event(
|
async def put_game_event(
|
||||||
self, version: int, type: int, event_id: int, name: str
|
self, version: int, type: int, event_id: int, name: str, opt_id: int = None
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
sql = insert(event).values(
|
sql = insert(event).values(
|
||||||
version=version,
|
version=version,
|
||||||
type=type,
|
type=type,
|
||||||
eventId=event_id,
|
eventId=event_id,
|
||||||
name=name,
|
name=name,
|
||||||
|
opt=coalesce(event.c.opt, opt_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(eventId=event_id)
|
conflict = sql.on_duplicate_key_update(eventId=event_id, opt=coalesce(event.c.opt, opt_id))
|
||||||
|
|
||||||
result = await self.execute(conflict)
|
result = await self.execute(conflict)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -154,6 +156,7 @@ class Mai2StaticData(BaseData):
|
|||||||
added_version: str,
|
added_version: str,
|
||||||
difficulty: float,
|
difficulty: float,
|
||||||
note_designer: str,
|
note_designer: str,
|
||||||
|
opt_id: int = None
|
||||||
) -> None:
|
) -> None:
|
||||||
sql = insert(music).values(
|
sql = insert(music).values(
|
||||||
version=version,
|
version=version,
|
||||||
@ -166,6 +169,7 @@ class Mai2StaticData(BaseData):
|
|||||||
addedVersion=added_version,
|
addedVersion=added_version,
|
||||||
difficulty=difficulty,
|
difficulty=difficulty,
|
||||||
noteDesigner=note_designer,
|
noteDesigner=note_designer,
|
||||||
|
opt=coalesce(music.c.opt, opt_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(
|
conflict = sql.on_duplicate_key_update(
|
||||||
@ -176,6 +180,7 @@ class Mai2StaticData(BaseData):
|
|||||||
addedVersion=added_version,
|
addedVersion=added_version,
|
||||||
difficulty=difficulty,
|
difficulty=difficulty,
|
||||||
noteDesigner=note_designer,
|
noteDesigner=note_designer,
|
||||||
|
opt=coalesce(music.c.opt, opt_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await self.execute(conflict)
|
result = await self.execute(conflict)
|
||||||
@ -191,6 +196,7 @@ class Mai2StaticData(BaseData):
|
|||||||
ticket_type: int,
|
ticket_type: int,
|
||||||
ticket_price: int,
|
ticket_price: int,
|
||||||
name: str,
|
name: str,
|
||||||
|
opt_id: int = None
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
sql = insert(ticket).values(
|
sql = insert(ticket).values(
|
||||||
version=version,
|
version=version,
|
||||||
@ -198,11 +204,10 @@ class Mai2StaticData(BaseData):
|
|||||||
kind=ticket_type,
|
kind=ticket_type,
|
||||||
price=ticket_price,
|
price=ticket_price,
|
||||||
name=name,
|
name=name,
|
||||||
|
opt=coalesce(ticket.c.opt, opt_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(price=ticket_price)
|
conflict = sql.on_duplicate_key_update(price=ticket_price, opt=coalesce(ticket.c.opt, opt_id))
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(price=ticket_price)
|
|
||||||
|
|
||||||
result = await self.execute(conflict)
|
result = await self.execute(conflict)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -247,12 +252,12 @@ class Mai2StaticData(BaseData):
|
|||||||
return None
|
return None
|
||||||
return result.fetchone()
|
return result.fetchone()
|
||||||
|
|
||||||
async def put_card(self, version: int, card_id: int, card_name: str, **card_data) -> int:
|
async def put_card(self, version: int, card_id: int, card_name: str, opt_id: int = None, **card_data) -> int:
|
||||||
sql = insert(cards).values(
|
sql = insert(cards).values(
|
||||||
version=version, cardId=card_id, cardName=card_name, **card_data
|
version=version, cardId=card_id, cardName=card_name, opt=coalesce(cards.c.opt, opt_id) **card_data
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(**card_data)
|
conflict = sql.on_duplicate_key_update(opt=coalesce(cards.c.opt, opt_id), **card_data)
|
||||||
|
|
||||||
result = await self.execute(conflict)
|
result = await self.execute(conflict)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -282,3 +287,85 @@ class Mai2StaticData(BaseData):
|
|||||||
result = await self.execute(event.update(event.c.id == table_id).values(enabled=is_enable, startDate = start_date))
|
result = await self.execute(event.update(event.c.id == table_id).values(enabled=is_enable, startDate = start_date))
|
||||||
if not result:
|
if not result:
|
||||||
self.logger.error(f"Failed to update event {table_id} - {is_enable} {start_date}")
|
self.logger.error(f"Failed to update event {table_id} - {is_enable} {start_date}")
|
||||||
|
|
||||||
|
async def put_opt(self, version: int, folder: str, sequence: int, cm_seq: int = None) -> Optional[int]:
|
||||||
|
sql = insert(opts).values(version=version, name=folder, sequence=sequence, cmReleaseVer=cm_seq)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(sequence=sequence, whenRead=datetime.now())
|
||||||
|
|
||||||
|
result = await self.execute(conflict)
|
||||||
|
if result is None:
|
||||||
|
self.logger.warning(f"Failed to insert opt! version {version} folder {folder} sequence {sequence}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
|
async def get_opt_by_version_folder(self, version: int, folder: str) -> Optional[Row]:
|
||||||
|
result = await self.execute(opts.select(and_(
|
||||||
|
opts.c.version == version,
|
||||||
|
opts.c.name == folder,
|
||||||
|
)))
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
async def get_opt_by_version_sequence(self, version: int, sequence: str) -> Optional[Row]:
|
||||||
|
result = await self.execute(opts.select(and_(
|
||||||
|
opts.c.version == version,
|
||||||
|
opts.c.sequence == sequence,
|
||||||
|
)))
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
async def get_opts_by_version(self, version: int) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(opts.select(opts.c.version == version))
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def get_opts_enabled_by_version(self, version: int) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(opts.select(and_(
|
||||||
|
opts.c.version == version,
|
||||||
|
opts.c.isEnable == True,
|
||||||
|
)))
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def get_latest_enabled_opt_by_version(self, version: int) -> Optional[Row]:
|
||||||
|
result = await self.execute(
|
||||||
|
opts.select(and_(
|
||||||
|
opts.c.version == version,
|
||||||
|
opts.c.isEnable == True,
|
||||||
|
)).order_by(opts.c.sequence.desc())
|
||||||
|
)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
async def get_opts(self) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(opts.select())
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def get_opts(self) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(opts.select())
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def set_opt_enabled(self, opt_id: int, enabled: bool) -> bool:
|
||||||
|
result = await self.execute(opts.update(opts.c.id == opt_id).values(isEnable=enabled))
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.logger.error(f"Failed to set opt enabled status to {enabled} for opt {opt_id}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
Reference in New Issue
Block a user