diff --git a/pyproject.toml b/pyproject.toml index 5f99b637..299d8023 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ "redis>=5.0.1,<6.0", "pyrogram>=2.0.57,<3.0", "dataclass_factory>=2.16,<3.0", - "adaptix==3.0.0b7", + "adaptix==3.0.0b9", "apscheduler>=3.9.1,<4.0", "fastapi==0.111.1", "uvicorn>=0.30.0,<0.31.0", diff --git a/shvatka/api/models/responses.py b/shvatka/api/models/responses.py index d1c7d92b..70e780ee 100644 --- a/shvatka/api/models/responses.py +++ b/shvatka/api/models/responses.py @@ -3,12 +3,12 @@ from datetime import datetime from typing import Sequence, Generic -from adaptix import Retort, dumper, P +from adaptix import Retort, dumper from shvatka.common.factory import REQUIRED_GAME_RECIPES from shvatka.core.games.dto import CurrentHints from shvatka.core.models import dto, enums -from shvatka.core.models.dto import scn, action +from shvatka.core.models.dto import scn from shvatka.core.models.enums import GameStatus T = typing.TypeVar("T") @@ -16,11 +16,6 @@ recipe=[ *REQUIRED_GAME_RECIPES, dumper(scn.HintsList, lambda x: x.hints), - # TODO https://github.com/reagento/adaptix/issues/348 - dumper( - P[action.KeyBonusCondition].keys, - lambda keys: [{"text": x.text, "bonus_minutes": x.bonus_minutes} for x in keys], - ), ] ) diff --git a/shvatka/common/factory.py b/shvatka/common/factory.py index f79ddccc..ba61cf4f 100644 --- a/shvatka/common/factory.py +++ b/shvatka/common/factory.py @@ -9,7 +9,6 @@ name_mapping, loader, Chain, - dumper, ) from adaptix.load_error import LoadError from adaptix._internal.morphing.provider_template import ABCProxy @@ -18,7 +17,7 @@ from telegraph.aio import Telegraph from shvatka.common.url_factory import UrlFactory -from shvatka.core.models.dto import scn, action +from shvatka.core.models.dto import scn from shvatka.core.models.dto.action import AnyCondition from shvatka.core.models.dto.scn import HintsList, TimeHint, Conditions from shvatka.core.models.schems import schemas @@ -45,7 +44,6 @@ def create_telegraph(self, bot_config: BotConfig) -> Telegraph: ABCProxy( Conditions, list[AnyCondition] ), # internal class, can be broken in next adaptix version - dumper(P[action.KeyWinCondition].keys, list), ] @@ -68,13 +66,6 @@ def create_retort(self) -> Retort: name_style=adaptix.NameStyle.LOWER_KEBAB, ), *REQUIRED_GAME_RECIPES, - # TODO https://github.com/reagento/adaptix/issues/348 - dumper( - P[action.KeyBonusCondition].keys, - lambda keys: [ - {"text": x.text, "bonus-minutes": x.bonus_minutes} for x in keys - ], - ), validator( pred=P[scn.LevelScenario].id, func=lambda x: validate_level_id(x) is not None, diff --git a/shvatka/infrastructure/db/models/level.py b/shvatka/infrastructure/db/models/level.py index f0d68b86..393085ff 100644 --- a/shvatka/infrastructure/db/models/level.py +++ b/shvatka/infrastructure/db/models/level.py @@ -2,14 +2,14 @@ import typing from typing import Any -from adaptix import Retort, dumper, P +from adaptix import Retort from sqlalchemy import Integer, Text, ForeignKey, JSON, TypeDecorator, UniqueConstraint from sqlalchemy.engine import Dialect from sqlalchemy.orm import relationship, mapped_column, Mapped from shvatka.common.factory import REQUIRED_GAME_RECIPES from shvatka.core.models import dto -from shvatka.core.models.dto import scn, action +from shvatka.core.models.dto import scn from shvatka.infrastructure.db.models import Base if typing.TYPE_CHECKING: @@ -24,11 +24,6 @@ class ScenarioField(TypeDecorator): cache_ok = True retort = Retort( recipe=[ - # TODO https://github.com/reagento/adaptix/issues/348 - dumper( - P[action.KeyBonusCondition].keys, - lambda keys: [{"text": x.text, "bonus_minutes": x.bonus_minutes} for x in keys], - ), *REQUIRED_GAME_RECIPES, ], ) diff --git a/tests/unit/serialization/test_deserialize.py b/tests/unit/serialization/test_deserialize.py index bf8c53e5..a3f552a0 100644 --- a/tests/unit/serialization/test_deserialize.py +++ b/tests/unit/serialization/test_deserialize.py @@ -3,6 +3,7 @@ import pytest from adaptix import Retort +from shvatka.common.data_examples import game_example, GAME_START_EXAMPLE from shvatka.core.models.dto.scn import TextHint, GPSHint, PhotoHint, ContactHint from shvatka.core.models.dto.scn.game import RawGameScenario from shvatka.core.models.dto.scn.hint_part import ( @@ -84,3 +85,315 @@ def test_render_all_types(all_types_scn: RawGameScenario, retort: Retort): hints = [time_hint.hint[0] for time_hint in game_scn.levels[0].time_hints] assert 12 == len(hints) assert "πŸ“ƒπŸ“‘πŸ§­πŸ“·πŸŽΌπŸŽ¬πŸ“ŽπŸŒ€πŸŽ€πŸ€³πŸͺͺ🏷" == render_hints(hints) + + +def test_serialize_simple(retort: Retort): + serialized = retort.dump(game_example) + assert serialized == { + "name": "Funny game", + "author": {"can-be-author": True, "id": 100, "is-dummy": False}, + "id": 10, + "number": 20, + "manage-token": "", + "start-at": GAME_START_EXAMPLE.isoformat(), + "status": "complete", + "results": { + "published-chanel-id": None, + "results-picture-file-id": None, + "keys-url": None, + }, + "levels": [ + { + "db-id": 100, + "author": {"can-be-author": True, "id": 100, "is-dummy": False}, + "name-id": "level_100", + "game-id": 10, + "number-in-game": 0, + "scenario": { + "id": "level_100", + "__model_version__": 1, + "conditions": [ + { + "type": "WIN_KEY", + "keys": ("SH1",), + } + ], + "time-hints": [ + { + "time": 0, + "hint": [ + { + "type": "text", + "text": "level_100_0", + } + ], + }, + { + "time": 10, + "hint": [ + { + "type": "text", + "text": "level_100_10", + } + ], + }, + { + "time": 20, + "hint": [ + { + "type": "text", + "text": "level_100_20", + } + ], + }, + { + "time": 30, + "hint": [ + { + "type": "text", + "text": "level_100_20", + } + ], + }, + { + "time": 40, + "hint": [ + { + "type": "text", + "text": "level_100_20", + } + ], + }, + { + "time": 60, + "hint": [ + { + "type": "text", + "text": "level_100_20", + } + ], + }, + ], + }, + }, + { + "db-id": 101, + "author": {"can-be-author": True, "id": 100, "is-dummy": False}, + "name-id": "level_101", + "game-id": 10, + "number-in-game": 1, + "scenario": { + "id": "level_101", + "__model_version__": 1, + "conditions": [ + { + "type": "WIN_KEY", + "keys": ("SH2",), + } + ], + "time-hints": [ + { + "time": 0, + "hint": [ + { + "type": "text", + "text": "level_101_0", + } + ], + }, + { + "time": 10, + "hint": [ + { + "type": "text", + "text": "level_101_10", + } + ], + }, + { + "time": 20, + "hint": [ + { + "type": "text", + "text": "level_101_20", + } + ], + }, + { + "time": 30, + "hint": [ + { + "type": "text", + "text": "level_101_20", + } + ], + }, + { + "time": 40, + "hint": [ + { + "type": "text", + "text": "level_101_20", + } + ], + }, + { + "time": 60, + "hint": [ + { + "type": "text", + "text": "level_101_20", + } + ], + }, + ], + }, + }, + { + "db-id": 102, + "author": {"can-be-author": True, "id": 100, "is-dummy": False}, + "name-id": "level_102", + "game-id": 10, + "number-in-game": 0, + "scenario": { + "id": "level_102", + "__model_version__": 1, + "conditions": [ + { + "type": "WIN_KEY", + "keys": ("SH3",), + } + ], + "time-hints": [ + { + "time": 0, + "hint": [ + { + "type": "text", + "text": "level_102_0", + } + ], + }, + { + "time": 10, + "hint": [ + { + "type": "text", + "text": "level_102_10", + } + ], + }, + { + "time": 20, + "hint": [ + { + "type": "text", + "text": "level_102_20", + } + ], + }, + { + "time": 30, + "hint": [ + { + "type": "text", + "text": "level_102_20", + } + ], + }, + { + "time": 40, + "hint": [ + { + "type": "text", + "text": "level_102_20", + } + ], + }, + { + "time": 60, + "hint": [ + { + "type": "text", + "text": "level_102_20", + } + ], + }, + ], + }, + }, + { + "db-id": 103, + "author": {"can-be-author": True, "id": 100, "is-dummy": False}, + "name-id": "level_103", + "game-id": 10, + "number-in-game": 0, + "scenario": { + "id": "level_103", + "__model_version__": 1, + "conditions": [ + { + "type": "WIN_KEY", + "keys": ("SH4",), + } + ], + "time-hints": [ + { + "time": 0, + "hint": [ + { + "type": "text", + "text": "level_103_0", + } + ], + }, + { + "time": 10, + "hint": [ + { + "type": "text", + "text": "level_103_10", + } + ], + }, + { + "time": 20, + "hint": [ + { + "type": "text", + "text": "level_103_20", + } + ], + }, + { + "time": 30, + "hint": [ + { + "type": "text", + "text": "level_103_20", + } + ], + }, + { + "time": 40, + "hint": [ + { + "type": "text", + "text": "level_103_20", + } + ], + }, + { + "time": 60, + "hint": [ + { + "type": "text", + "text": "level_103_20", + } + ], + }, + ], + }, + }, + ], + } diff --git a/tests/unit/serialization/test_retort.py b/tests/unit/serialization/test_retort.py new file mode 100644 index 00000000..91104f27 --- /dev/null +++ b/tests/unit/serialization/test_retort.py @@ -0,0 +1,27 @@ +from dataclasses import dataclass + +import pytest +from adaptix import Retort + + +@dataclass(frozen=True) +class B: + b: str + + +@dataclass +class A: + a: set[B] + + +@pytest.fixture +def retort(): + return Retort(recipe=[]) + + +def test_retort(retort: Retort): + assert retort.dump(set(), set) == () + assert retort.load([], set) == set() + assert retort.dump({1, 2}, set) == (1, 2) + assert retort.load([1, 2], set) == {1, 2} + assert retort.dump(A({B("a")})) == {"a": ({"b": "a"},)}