From 2b8b13ca95a0e8af50e991b6259e8adf40a7702e Mon Sep 17 00:00:00 2001 From: 283375 Date: Sat, 31 May 2025 18:12:58 +0800 Subject: [PATCH] fix: adapt to new model and tests - I actually forgot I wrote tests lol --- .../external/importers/arcaea/lists.py | 76 ++++++++++--------- .../external/importers/arcaea/online.py | 2 +- .../external/importers/arcaea/st3.py | 27 ++++--- tests/db/models/relationships/test_common.py | 29 ++++--- tests/db/models/relationships/test_pack.py | 27 +++---- tests/db/models/test_chart.py | 22 ++++-- tests/db/models/test_custom_types.py | 37 ++------- .../external/importers/arcaea/test_online.py | 6 +- tests/external/importers/arcaea/test_st3.py | 4 +- 9 files changed, 108 insertions(+), 122 deletions(-) diff --git a/src/arcaea_offline/external/importers/arcaea/lists.py b/src/arcaea_offline/external/importers/arcaea/lists.py index d397d23..28087b0 100644 --- a/src/arcaea_offline/external/importers/arcaea/lists.py +++ b/src/arcaea_offline/external/importers/arcaea/lists.py @@ -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) diff --git a/src/arcaea_offline/external/importers/arcaea/online.py b/src/arcaea_offline/external/importers/arcaea/online.py index 4192dd4..e268b65 100644 --- a/src/arcaea_offline/external/importers/arcaea/online.py +++ b/src/arcaea_offline/external/importers/arcaea/online.py @@ -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"]) diff --git a/src/arcaea_offline/external/importers/arcaea/st3.py b/src/arcaea_offline/external/importers/arcaea/st3.py index 485ac34..0aa8886 100644 --- a/src/arcaea_offline/external/importers/arcaea/st3.py +++ b/src/arcaea_offline/external/importers/arcaea/st3.py @@ -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 diff --git a/tests/db/models/relationships/test_common.py b/tests/db/models/relationships/test_common.py index 3e44688..a209b69 100644 --- a/tests/db/models/relationships/test_common.py +++ b/tests/db/models/relationships/test_common.py @@ -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 diff --git a/tests/db/models/relationships/test_pack.py b/tests/db/models/relationships/test_pack.py index 5a8f685..1815eb8 100644 --- a/tests/db/models/relationships/test_pack.py +++ b/tests/db/models/relationships/test_pack.py @@ -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 diff --git a/tests/db/models/test_chart.py b/tests/db/models/test_chart.py index 113a4cf..92e9943 100644 --- a/tests/db/models/test_chart.py +++ b/tests/db/models/test_chart.py @@ -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) diff --git a/tests/db/models/test_custom_types.py b/tests/db/models/test_custom_types.py index 077fdd2..bbd497e 100644 --- a/tests/db/models/test_custom_types.py +++ b/tests/db/models/test_custom_types.py @@ -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() diff --git a/tests/external/importers/arcaea/test_online.py b/tests/external/importers/arcaea/test_online.py index 3b474c4..e228594 100644 --- a/tests/external/importers/arcaea/test_online.py +++ b/tests/external/importers/arcaea/test_online.py @@ -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) diff --git a/tests/external/importers/arcaea/test_st3.py b/tests/external/importers/arcaea/test_st3.py index 0db19e8..4b7293b 100644 --- a/tests/external/importers/arcaea/test_st3.py +++ b/tests/external/importers/arcaea/test_st3.py @@ -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")