diff --git a/src/arcaea_offline/calculators/world/legacy.py b/src/arcaea_offline/calculators/world/legacy.py index e6a0d79..6b2a5b2 100644 --- a/src/arcaea_offline/calculators/world/legacy.py +++ b/src/arcaea_offline/calculators/world/legacy.py @@ -5,6 +5,13 @@ from ._common import StepBooster class LegacyMapStepBooster(StepBooster): + __fragment_boost_multipliers = { + None: Decimal("1.0"), + 100: Decimal("1.1"), + 250: Decimal("1.25"), + 500: Decimal("1.5"), + } + def __init__( self, stamina: Literal[2, 4, 6], @@ -35,11 +42,5 @@ class LegacyMapStepBooster(StepBooster): def final_value(self) -> Decimal: stamina_multiplier = Decimal(self.stamina) - fragments_multiplier = Decimal(1) - if self.fragments == 100: - fragments_multiplier = Decimal("1.1") - elif self.fragments == 250: - fragments_multiplier = Decimal("1.25") - elif self.fragments == 500: - fragments_multiplier = Decimal("1.5") + fragments_multiplier = self.__fragment_boost_multipliers[self.fragments] return stamina_multiplier * fragments_multiplier diff --git a/src/arcaea_offline/database/db.py b/src/arcaea_offline/database/db.py index 9947aa3..6b7dee5 100644 --- a/src/arcaea_offline/database/db.py +++ b/src/arcaea_offline/database/db.py @@ -75,7 +75,7 @@ class Database(metaclass=Singleton): # region init - def init(self, checkfirst: bool = True): + def init(self, *, checkfirst: bool = True): # create tables & views if checkfirst: # > https://github.com/kvesteri/sqlalchemy-utils/issues/396 @@ -320,7 +320,7 @@ class Database(metaclass=Singleton): for constant in range(base_constant - 20, base_constant + 1): # from Pure Memory(EX+) to AA score_modifier = (play_result * 10 - constant) / 10 - if score_modifier >= 2.0: + if score_modifier >= 2.0: # noqa: PLR2004 min_score = 10000000 elif score_modifier >= 1.0: min_score = 200000 * (score_modifier - 1) + 9800000 @@ -383,7 +383,7 @@ class Database(metaclass=Singleton): stmt = ( select(func.count()) .select_from(ChartInfo) - .where((ChartInfo.constant != None) & (ChartInfo.notes != None)) + .where((ChartInfo.constant != None) & (ChartInfo.notes != None)) # noqa: E711 ) with self.sessionmaker() as session: result = session.scalar(stmt) diff --git a/src/arcaea_offline/database/models/v4/scores.py b/src/arcaea_offline/database/models/v4/scores.py index c2936ff..fb4b58e 100644 --- a/src/arcaea_offline/database/models/v4/scores.py +++ b/src/arcaea_offline/database/models/v4/scores.py @@ -105,9 +105,9 @@ class ScoreCalculated(ScoresViewBase): Score.modifier, Score.clear_type, case( - (Score.score >= 10000000, ChartInfo.constant / 10.0 + 2), + (Score.score >= 10000000, ChartInfo.constant / 10.0 + 2), # noqa: PLR2004 ( - Score.score >= 9800000, + Score.score >= 9800000, # noqa: PLR2004 ChartInfo.constant / 10.0 + 1 + (Score.score - 9800000) / 200000.0, ), else_=func.max( diff --git a/src/arcaea_offline/utils/formatters/play_result.py b/src/arcaea_offline/utils/formatters/play_result.py index c8d97db..ba1c2e9 100644 --- a/src/arcaea_offline/utils/formatters/play_result.py +++ b/src/arcaea_offline/utils/formatters/play_result.py @@ -1,4 +1,4 @@ -from typing import Any, Literal, overload +from typing import Any, Dict, Literal, overload from arcaea_offline.constants.enums import ( ArcaeaPlayResultClearType, @@ -10,8 +10,8 @@ from arcaea_offline.constants.play_result import ScoreLowerLimits class PlayResultFormatter: SCORE_GRADE_FORMAT_RESULTS = Literal["EX+", "EX", "AA", "A", "B", "C", "D"] - @staticmethod - def score_grade(score: int) -> SCORE_GRADE_FORMAT_RESULTS: + @classmethod + def score_grade(cls, score: int) -> SCORE_GRADE_FORMAT_RESULTS: """ Returns the score grade, e.g. EX+. @@ -20,23 +20,21 @@ class PlayResultFormatter: if not isinstance(score, int): raise TypeError(f"Unsupported type {type(score)}, cannot format") - if score >= ScoreLowerLimits.EX_PLUS: - return "EX+" - elif score >= ScoreLowerLimits.EX: - return "EX" - elif score >= ScoreLowerLimits.AA: - return "AA" - elif score >= ScoreLowerLimits.A: - return "A" - elif score >= ScoreLowerLimits.B: - return "B" - elif score >= ScoreLowerLimits.C: - return "C" - elif score >= ScoreLowerLimits.D: - return "D" - else: + if score < 0: raise ValueError("score cannot be negative") + score_grades: Dict[int, Literal["EX+", "EX", "AA", "A", "B", "C", "D"]] = { + ScoreLowerLimits.EX_PLUS: "EX+", + ScoreLowerLimits.EX: "EX", + ScoreLowerLimits.AA: "AA", + ScoreLowerLimits.A: "A", + ScoreLowerLimits.B: "B", + ScoreLowerLimits.C: "C", + ScoreLowerLimits.D: "D", + } + + return next(value for limit, value in score_grades.items() if score >= limit) + CLEAR_TYPE_FORMAT_RESULTS = Literal[ "TRACK LOST", "NORMAL CLEAR", @@ -56,7 +54,6 @@ class PlayResultFormatter: """ Returns the uppercased clear type name, e.g. NORMAL CLEAR. """ - ... @overload @classmethod @@ -69,7 +66,6 @@ class PlayResultFormatter: Raises `ValueError` if the integer is negative. """ - ... @overload @classmethod @@ -77,7 +73,6 @@ class PlayResultFormatter: """ Returns "None" """ - ... @classmethod def clear_type(cls, clear_type: Any) -> CLEAR_TYPE_FORMAT_RESULTS: @@ -103,7 +98,6 @@ class PlayResultFormatter: """ Returns the uppercased clear type name, e.g. NORMAL CLEAR. """ - ... @overload @classmethod @@ -116,7 +110,6 @@ class PlayResultFormatter: Raises `ValueError` if the integer is negative. """ - ... @overload @classmethod @@ -124,7 +117,6 @@ class PlayResultFormatter: """ Returns "None" """ - ... @classmethod def modifier(cls, modifier: Any) -> MODIFIER_FORMAT_RESULTS: diff --git a/src/arcaea_offline/utils/partner.py b/src/arcaea_offline/utils/partner.py index 3ec4a61..5730ec3 100644 --- a/src/arcaea_offline/utils/partner.py +++ b/src/arcaea_offline/utils/partner.py @@ -3,13 +3,9 @@ from enum import IntEnum class KanaeDayNight(IntEnum): - Day = 0 - Night = 1 + DAY = 0 + NIGHT = 1 - -def kanae_day_night(timestamp: int) -> KanaeDayNight: - """ - :param timestamp: POSIX timestamp, which is passed to `datetime.fromtimestamp(timestamp)`. - """ - dt = datetime.fromtimestamp(timestamp) - return KanaeDayNight.Day if 6 <= dt.hour <= 19 else KanaeDayNight.Night + @staticmethod + def from_datetime(dt: datetime) -> "KanaeDayNight": + return KanaeDayNight.DAY if 6 <= dt.hour <= 19 else KanaeDayNight.NIGHT # noqa: PLR2004 diff --git a/tests/calculators/test_play_result.py b/tests/calculators/test_play_result.py index d099594..f258175 100644 --- a/tests/calculators/test_play_result.py +++ b/tests/calculators/test_play_result.py @@ -21,7 +21,8 @@ class TestPlayResultCalculators: Decimal("-0.00") ) == Decimal("-31.67") - pytest.raises(ValueError, PlayResultCalculators.score_modifier, -1) + with pytest.raises(ValueError, match="negative"): + PlayResultCalculators.score_modifier(-1) pytest.raises(TypeError, PlayResultCalculators.score_modifier, "9800000") pytest.raises(TypeError, PlayResultCalculators.score_modifier, None) @@ -38,5 +39,8 @@ class TestPlayResultCalculators: pytest.raises(TypeError, PlayResultCalculators.play_rating, 10002221, None) - pytest.raises(ValueError, PlayResultCalculators.play_rating, -1, 120) - pytest.raises(ValueError, PlayResultCalculators.play_rating, 10002221, -1) + with pytest.raises(ValueError, match="negative"): + PlayResultCalculators.play_rating(-1, 120) + + with pytest.raises(ValueError, match="negative"): + PlayResultCalculators.play_rating(10002221, -1) diff --git a/tests/conftest.py b/tests/conftest.py index 56fc95c..9892e0b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,7 +14,7 @@ def db_conn(): conn.close() -@pytest.fixture() +@pytest.fixture def db_session(db_conn): session = Session(bind=db_conn) yield session diff --git a/tests/db/models/v4/test_songs.py b/tests/db/models/v4/test_songs.py index 3a2f80f..f57f186 100644 --- a/tests/db/models/v4/test_songs.py +++ b/tests/db/models/v4/test_songs.py @@ -21,7 +21,7 @@ def _difficulty(**kw): return Difficulty(**defaults) -class Test_Chart: +class TestChart: def init_db(self, session): SongsBase.metadata.create_all(session.bind, checkfirst=False) SongsViewBase.metadata.create_all(session.bind, checkfirst=False) diff --git a/tests/utils/test_formatters.py b/tests/utils/test_formatters.py index 3e35832..e6ccc4e 100644 --- a/tests/utils/test_formatters.py +++ b/tests/utils/test_formatters.py @@ -73,7 +73,9 @@ class TestPlayResultFormatter: assert PlayResultFormatter.score_grade(5500000) == "D" assert PlayResultFormatter.score_grade(0) == "D" - pytest.raises(ValueError, PlayResultFormatter.score_grade, -1) + with pytest.raises(ValueError, match="negative"): + PlayResultFormatter.score_grade(-1) + pytest.raises(TypeError, PlayResultFormatter.score_grade, "10001284") pytest.raises(TypeError, PlayResultFormatter.score_grade, []) pytest.raises(TypeError, PlayResultFormatter.score_grade, None) @@ -108,7 +110,9 @@ class TestPlayResultFormatter: assert PlayResultFormatter.clear_type(1) == "NORMAL CLEAR" assert PlayResultFormatter.clear_type(6) == "UNKNOWN" - pytest.raises(ValueError, PlayResultFormatter.clear_type, -1) + with pytest.raises(ValueError, match="negative"): + PlayResultFormatter.clear_type(-1) + pytest.raises(TypeError, PlayResultFormatter.clear_type, "1") pytest.raises(TypeError, PlayResultFormatter.clear_type, []) @@ -121,6 +125,8 @@ class TestPlayResultFormatter: assert PlayResultFormatter.modifier(1) == "EASY" assert PlayResultFormatter.modifier(6) == "UNKNOWN" - pytest.raises(ValueError, PlayResultFormatter.modifier, -1) + with pytest.raises(ValueError, match="negative"): + PlayResultFormatter.modifier(-1) + pytest.raises(TypeError, PlayResultFormatter.modifier, "1") pytest.raises(TypeError, PlayResultFormatter.modifier, [])