fix: adapt to new model and tests

- I actually forgot I wrote tests lol
This commit is contained in:
2025-05-31 18:12:58 +08:00
parent 743bbe209f
commit 2b8b13ca95
9 changed files with 108 additions and 122 deletions

View File

@ -3,6 +3,7 @@ packlist and songlist parsers
"""
import json
from datetime import datetime, timezone
from typing import List, Union
from arcaea_offline.constants.enums import (
@ -12,12 +13,11 @@ from arcaea_offline.constants.enums import (
)
from arcaea_offline.database.models import (
Difficulty,
DifficultyLocalized,
DifficultyLocalization,
Pack,
PackLocalized,
PackLocalization,
Song,
SongLocalized,
SongSearchWord,
SongLocalization,
)
@ -27,11 +27,11 @@ class ArcaeaListParser:
class ArcaeaPacklistParser(ArcaeaListParser):
def parse(self) -> List[Union[Pack, PackLocalized]]:
def parse(self) -> List[Union[Pack, PackLocalization]]:
root = json.loads(self.list_text)
packs = root["packs"]
results: List[Union[Pack, PackLocalized]] = [
results: List[Union[Pack, PackLocalization]] = [
Pack(id="single", name="Memory Archive")
]
for item in packs:
@ -48,8 +48,8 @@ class ArcaeaPacklistParser(ArcaeaListParser):
)
if name_localized or description_localized:
pack_localized = PackLocalized(id=pack.id)
pack_localized.lang = ArcaeaLanguage(key.value)
pack_localized = PackLocalization(id=pack.id)
pack_localized.lang = key.value
pack_localized.name = name_localized
pack_localized.description = description_localized
results.append(pack_localized)
@ -58,7 +58,7 @@ class ArcaeaPacklistParser(ArcaeaListParser):
class ArcaeaSonglistParser(ArcaeaListParser):
def parse_songs(self) -> List[Union[Song, SongLocalized, SongSearchWord]]:
def parse_songs(self) -> List[Union[Song, SongLocalization]]:
root = json.loads(self.list_text)
songs = root["songs"]
@ -72,11 +72,9 @@ class ArcaeaSonglistParser(ArcaeaListParser):
song.bpm = item["bpm"]
song.bpm_base = item["bpm_base"]
song.pack_id = item["set"]
song.audio_preview = item["audioPreview"]
song.audio_preview_end = item["audioPreviewEnd"]
song.side = ArcaeaSongSide(item["side"])
song.version = item["version"]
song.date = item["date"]
song.added_at = datetime.fromtimestamp(item["date"], tz=timezone.utc)
song.bg = item.get("bg")
song.bg_inverse = item.get("bg_inverse")
if item.get("bg_daynight"):
@ -95,32 +93,32 @@ class ArcaeaSonglistParser(ArcaeaListParser):
)
if title_localized or source_localized:
song_localized = SongLocalized(id=song.id)
song_localized.lang = ArcaeaLanguage(lang.value)
song_localized = SongLocalization(id=song.id)
song_localized.lang = lang.value
song_localized.title = title_localized
song_localized.source = source_localized
results.append(song_localized)
# SongSearchTitle
search_titles = item.get("search_title", {}).get(lang.value, None)
if search_titles:
for search_title in search_titles:
song_search_word = SongSearchWord(
id=song.id, lang=lang.value, type=1, value=search_title
)
results.append(song_search_word)
# TODO: SongSearchTitle?
# search_titles = item.get("search_title", {}).get(lang.value, None)
# if search_titles:
# for search_title in search_titles:
# song_search_word = SongSearchWord(
# id=song.id, lang=lang.value, type=1, value=search_title
# )
# results.append(song_search_word)
search_artists = item.get("search_artist", {}).get(lang.value, None)
if search_artists:
for search_artist in search_artists:
song_search_word = SongSearchWord(
id=song.id, lang=lang.value, type=2, value=search_artist
)
results.append(song_search_word)
# search_artists = item.get("search_artist", {}).get(lang.value, None)
# if search_artists:
# for search_artist in search_artists:
# song_search_word = SongSearchWord(
# id=song.id, lang=lang.value, type=2, value=search_artist
# )
# results.append(song_search_word)
return results
def parse_difficulties(self) -> List[Union[Difficulty, DifficultyLocalized]]:
def parse_difficulties(self) -> List[Union[Difficulty, DifficultyLocalization]]:
root = json.loads(self.list_text)
songs = root["songs"]
@ -138,11 +136,11 @@ class ArcaeaSonglistParser(ArcaeaListParser):
difficulty.song_id = song["id"]
difficulty.rating_class = ArcaeaRatingClass(item["ratingClass"])
difficulty.rating = item["rating"]
difficulty.rating_plus = item.get("ratingPlus") or False
difficulty.is_rating_plus = item.get("ratingPlus") or False
difficulty.chart_designer = item["chartDesigner"]
difficulty.jacket_desginer = item.get("jacketDesigner") or None
difficulty.audio_override = item.get("audioOverride") or False
difficulty.jacket_override = item.get("jacketOverride") or False
difficulty.jacket_designer = item.get("jacketDesigner") or None
difficulty.has_overriding_audio = item.get("audioOverride") or False
difficulty.has_overriding_jacket = item.get("jacketOverride") or False
difficulty.jacket_night = item.get("jacketNight") or None
difficulty.title = item.get("title_localized", {}).get("en") or None
difficulty.artist = item.get("artist") or None
@ -151,7 +149,11 @@ class ArcaeaSonglistParser(ArcaeaListParser):
difficulty.bpm = item.get("bpm") or None
difficulty.bpm_base = item.get("bpm_base") or None
difficulty.version = item.get("version") or None
difficulty.date = item.get("date") or None
difficulty.added_at = (
datetime.fromtimestamp(item["date"], tz=timezone.utc)
if item.get("date") is not None
else None
)
results.append(difficulty)
for lang in ArcaeaLanguage:
@ -163,11 +165,11 @@ class ArcaeaSonglistParser(ArcaeaListParser):
)
if title_localized or artist_localized:
difficulty_localized = DifficultyLocalized(
difficulty_localized = DifficultyLocalization(
song_id=difficulty.song_id,
rating_class=difficulty.rating_class,
)
difficulty_localized.lang = ArcaeaLanguage(lang.value)
difficulty_localized.lang = lang.value
difficulty_localized.title = title_localized
difficulty_localized.artist = artist_localized
results.append(difficulty_localized)

View File

@ -85,7 +85,7 @@ class ArcaeaOnlineApiParser:
play_result.pure = item["perfect_count"]
play_result.far = item["near_count"]
play_result.lost = item["miss_count"]
play_result.date = date
play_result.played_at = date
play_result.modifier = ArcaeaPlayResultModifier(item["modifier"])
play_result.clear_type = ArcaeaPlayResultClearType(item["clear_type"])

View File

@ -100,19 +100,18 @@ class ArcaeaSt3Parser:
else:
date = None
entities.append(
PlayResult(
song_id=song_id,
rating_class=rating_class_enum,
score=score,
pure=pure,
far=far,
lost=lost,
date=date,
modifier=modifier_enum,
clear_type=clear_type_enum,
comment=import_comment,
)
)
play_result = PlayResult()
play_result.song_id = song_id
play_result.rating_class = rating_class_enum
play_result.score = score
play_result.pure = pure
play_result.far = far
play_result.lost = lost
play_result.played_at = date
play_result.modifier = modifier_enum
play_result.clear_type = clear_type_enum
play_result.comment = import_comment
entities.append(play_result)
return entities

View File

@ -10,11 +10,13 @@ Database model v5 common relationships
└───────────┘
"""
from datetime import datetime, timezone
from arcaea_offline.constants.enums import ArcaeaRatingClass
from arcaea_offline.database.models import (
ChartInfo,
Difficulty,
ModelsV5Base,
ModelBase,
Pack,
PlayResult,
Song,
@ -24,7 +26,7 @@ from arcaea_offline.database.models import (
class TestSongRelationships:
@staticmethod
def init_db(session):
ModelsV5Base.metadata.create_all(session.bind)
ModelBase.metadata.create_all(session.bind)
def test_relationships(self, db_session):
self.init_db(db_session)
@ -45,52 +47,56 @@ class TestSongRelationships:
title=title_en,
artist=artist_en,
pack_id=pack.id,
added_at=datetime(2024, 7, 5, tzinfo=timezone.utc),
)
difficulty_pst = Difficulty(
song_id=song.id,
rating_class=ArcaeaRatingClass.PAST,
rating=2,
rating_plus=False,
is_rating_plus=False,
)
chart_info_pst = ChartInfo(
song_id=song.id,
rating_class=ArcaeaRatingClass.PAST,
constant=20,
notes=200,
added_at=datetime(2024, 7, 12, tzinfo=timezone.utc),
)
difficulty_prs = Difficulty(
song_id=song.id,
rating_class=ArcaeaRatingClass.PRESENT,
rating=7,
rating_plus=True,
is_rating_plus=True,
)
chart_info_prs = ChartInfo(
song_id=song.id,
rating_class=ArcaeaRatingClass.PRESENT,
constant=78,
notes=780,
added_at=datetime(2024, 7, 12, tzinfo=timezone.utc),
)
difficulty_ftr = Difficulty(
song_id=song.id,
rating_class=ArcaeaRatingClass.FUTURE,
rating=10,
rating_plus=True,
is_rating_plus=True,
)
chart_info_ftr = ChartInfo(
song_id=song.id,
rating_class=ArcaeaRatingClass.FUTURE,
constant=109,
notes=1090,
added_at=datetime(2024, 7, 12, tzinfo=timezone.utc),
)
difficulty_etr = Difficulty(
song_id=song.id,
rating_class=ArcaeaRatingClass.ETERNAL,
rating=9,
rating_plus=True,
is_rating_plus=True,
)
play_result_ftr = PlayResult(
@ -124,20 +130,19 @@ class TestSongRelationships:
difficulty_ftr,
difficulty_etr,
]
assert song.charts_info == [chart_info_pst, chart_info_prs, chart_info_ftr]
assert difficulty_pst.song == song
assert difficulty_prs.song == song
assert difficulty_ftr.song == song
assert difficulty_etr.song == song
assert difficulty_pst.chart_info == chart_info_pst
assert difficulty_prs.chart_info == chart_info_prs
assert difficulty_ftr.chart_info == chart_info_ftr
assert difficulty_etr.chart_info is None
assert difficulty_pst.chart_info_list == [chart_info_pst]
assert difficulty_prs.chart_info_list == [chart_info_prs]
assert difficulty_ftr.chart_info_list == [chart_info_ftr]
assert difficulty_etr.chart_info_list == []
assert chart_info_pst.difficulty == difficulty_pst
assert chart_info_prs.difficulty == difficulty_prs
assert chart_info_ftr.difficulty == difficulty_ftr
assert play_result_ftr.difficulty == difficulty_ftr
# assert play_result_ftr.difficulty == difficulty_ftr

View File

@ -5,13 +5,13 @@ Pack <> PackLocalized
"""
from arcaea_offline.constants.enums import ArcaeaLanguage
from arcaea_offline.database.models import ModelsV5Base, Pack, PackLocalized
from arcaea_offline.database.models import ModelBase, Pack, PackLocalization
class TestPackRelationships:
@staticmethod
def init_db(session):
ModelsV5Base.metadata.create_all(session.bind)
ModelBase.metadata.create_all(session.bind)
def test_localized_objects(self, db_session):
self.init_db(db_session)
@ -26,20 +26,16 @@ class TestPackRelationships:
description=description_en,
)
pkid_ja = 1
description_ja = "普通のパートナー「∅」と一緒に、\n不人気フレームワーク「Arcaea Offline」より、\n一般的なデータベース・モデルを通過する。"
pack_localized_ja = PackLocalized(
pkid=pkid_ja,
pack_localized_ja = PackLocalization(
id=pack_id,
lang=ArcaeaLanguage.JA.value,
name=None,
description=description_ja,
)
pkid_zh_hans = 2
description_zh_hans = "与平凡的「∅」一起,\n在没人用的「Arcaea Offline」框架里\n一同探索随处可见的数据库模型。"
pack_localized_zh_hans = PackLocalized(
pkid=pkid_zh_hans,
pack_localized_zh_hans = PackLocalization(
id=pack_id,
lang=ArcaeaLanguage.ZH_HANS.value,
name=None,
@ -49,11 +45,11 @@ class TestPackRelationships:
db_session.add_all([pack, pack_localized_ja])
db_session.commit()
assert len(pack.localized_objects) == len([pack_localized_ja])
assert len(pack.localized_entries) == len([pack_localized_ja])
assert pack_localized_ja.parent.description == pack.description
assert pack_localized_ja.pack.description == pack.description
# relationships should be viewonly
# test back populates
new_pack = Pack(
id=f"{pack_id}_new",
name="NEW",
@ -61,9 +57,10 @@ class TestPackRelationships:
)
db_session.add(new_pack)
pack_localized_ja.parent = new_pack
pack.localized_objects.append(pack_localized_zh_hans)
pack_localized_ja.pack = new_pack
pack.localized_entries.append(pack_localized_zh_hans)
db_session.commit()
assert pack_localized_ja.parent == pack
assert len(pack.localized_objects) == 1
assert pack_localized_ja.pack.id == new_pack.id
# TODO: this failes but why
assert len(pack.localized_entries) == 2

View File

@ -6,13 +6,15 @@ Chart functionalities
- Difficulty song info overriding
"""
from datetime import datetime, timezone
from arcaea_offline.constants.enums.arcaea import ArcaeaRatingClass
from arcaea_offline.database.models import (
Chart,
ChartInfo,
Difficulty,
ModelsV5Base,
ModelsV5ViewBase,
ModelBase,
ModelViewBase,
Pack,
Song,
)
@ -20,8 +22,8 @@ from arcaea_offline.database.models import (
class TestChart:
def init_db(self, session):
ModelsV5Base.metadata.create_all(session.bind)
ModelsV5ViewBase.metadata.create_all(session.bind)
ModelBase.metadata.create_all(session.bind)
ModelViewBase.metadata.create_all(session.bind)
def test_basic(self, db_session):
self.init_db(db_session)
@ -37,20 +39,23 @@ class TestChart:
title="~TEST~",
artist="~test~",
pack_id=pack_id,
added_at=datetime(2024, 7, 4, tzinfo=timezone.utc),
)
difficulty = Difficulty(
song_id=song_id,
rating_class=rating_class,
rating=9,
rating_plus=True,
is_rating_plus=True,
)
chart_info = ChartInfo(
song_id=song_id,
rating_class=rating_class,
constant=98,
notes=980,
added_at=datetime(2024, 7, 12, tzinfo=timezone.utc),
)
db_session.add_all([pack, song, difficulty, chart_info])
db_session.commit()
chart: Chart = (
db_session.query(Chart)
@ -64,7 +69,7 @@ class TestChart:
assert chart.artist == song.artist
assert chart.pack_id == song.pack_id
assert chart.rating == difficulty.rating
assert chart.rating_plus == difficulty.rating_plus
assert chart.is_rating_plus == difficulty.is_rating_plus
assert chart.constant == chart_info.constant
assert chart.notes == chart_info.notes
@ -82,12 +87,13 @@ class TestChart:
title="~TEST~",
artist="~test~",
pack_id=pack_id,
added_at=datetime(2024, 7, 4, tzinfo=timezone.utc),
)
difficulty = Difficulty(
song_id=song_id,
rating_class=rating_class,
rating=9,
rating_plus=True,
is_rating_plus=True,
title="~TEST DIFF~",
artist="~diff~",
)
@ -96,8 +102,10 @@ class TestChart:
rating_class=rating_class,
constant=98,
notes=980,
added_at=datetime(2024, 7, 12, tzinfo=timezone.utc),
)
db_session.add_all([pack, song, difficulty, chart_info])
db_session.commit()
chart: Chart = (
db_session.query(Chart)

View File

@ -2,10 +2,9 @@ from datetime import datetime, timedelta, timezone
from enum import IntEnum
from typing import Optional
from sqlalchemy import text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from arcaea_offline.database.models._custom_types import DbIntEnum, TZDateTime
from arcaea_offline.database.models._types import ForceTimezoneDateTime
class TestIntEnum(IntEnum):
@ -22,43 +21,19 @@ class TestBase(DeclarativeBase):
id: Mapped[int] = mapped_column(primary_key=True)
class IntEnumTestModel(TestBase):
__tablename__ = "test_int_enum"
value: Mapped[Optional[TestIntEnum]] = mapped_column(DbIntEnum(TestIntEnum))
class TZDatetimeTestModel(TestBase):
class ForceTimezoneDatetimeTestModel(TestBase):
__tablename__ = "test_tz_datetime"
value: Mapped[Optional[datetime]] = mapped_column(TZDateTime)
value: Mapped[Optional[datetime]] = mapped_column(ForceTimezoneDateTime)
class TestCustomTypes:
def test_int_enum(self, db_session):
def _query_value(_id: int):
return db_session.execute(
text(
f"SELECT value FROM {IntEnumTestModel.__tablename__} WHERE id = {_id}"
)
).one()[0]
TestBase.metadata.create_all(db_session.bind, checkfirst=False)
basic_obj = IntEnumTestModel(id=1, value=TestIntEnum.TWO)
null_obj = IntEnumTestModel(id=2, value=None)
db_session.add(basic_obj)
db_session.add(null_obj)
db_session.commit()
assert _query_value(1) == TestIntEnum.TWO.value
assert _query_value(2) is None
def test_tz_datetime(self, db_session):
def test_force_timezone_datetime(self, db_session):
TestBase.metadata.create_all(db_session.bind, checkfirst=False)
dt1 = datetime.now(tz=timezone(timedelta(hours=8)))
basic_obj = TZDatetimeTestModel(id=1, value=dt1)
null_obj = TZDatetimeTestModel(id=2, value=None)
basic_obj = ForceTimezoneDatetimeTestModel(id=1, value=dt1)
null_obj = ForceTimezoneDatetimeTestModel(id=2, value=None)
db_session.add(basic_obj)
db_session.add(null_obj)
db_session.commit()

View File

@ -6,7 +6,7 @@ from arcaea_offline.constants.enums.arcaea import (
ArcaeaPlayResultModifier,
ArcaeaRatingClass,
)
from arcaea_offline.database.models.play_results import PlayResult
from arcaea_offline.database.models.play_result import PlayResult
from arcaea_offline.external.importers.arcaea.online import ArcaeaOnlineApiParser
API_RESULT = {
@ -80,9 +80,9 @@ class TestArcaeaOnlineApiParser:
assert test1.pure == 1234
assert test1.far == 12
assert test1.lost == 4
assert test1.date == datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
assert test1.played_at == datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
assert test1.clear_type is ArcaeaPlayResultClearType.NORMAL_CLEAR
assert test1.modifier is ArcaeaPlayResultModifier.NORMAL
test2 = next(filter(lambda x: x.song_id == "test2", play_results))
assert test2.date == datetime(2024, 1, 2, 0, 0, 0, tzinfo=timezone.utc)
assert test2.played_at == datetime(2024, 1, 2, 0, 0, 0, tzinfo=timezone.utc)

View File

@ -31,7 +31,7 @@ class TestArcaeaSt3Parser:
assert test1.pure == 895
assert test1.far == 32
assert test1.lost == 22
assert test1.date == datetime.fromtimestamp(1722100000).astimezone()
assert test1.played_at == datetime.fromtimestamp(1722100000).astimezone()
assert test1.clear_type is ArcaeaPlayResultClearType.TRACK_LOST
assert test1.modifier is ArcaeaPlayResultModifier.HARD
@ -48,7 +48,7 @@ class TestArcaeaSt3Parser:
assert corrupt2.modifier is None
date1 = next(filter(lambda x: x.song_id == "date1", play_results))
assert date1.date is None
assert date1.played_at is None
def test_invalid_input(self):
pytest.raises(TypeError, ArcaeaSt3Parser.parse, "abcdefghijklmn")