Skip to content

Commit

Permalink
Merge pull request #121 from bomzheg/feature/#106
Browse files Browse the repository at this point in the history
#106 added routing for key condition
  • Loading branch information
bomzheg authored Mar 6, 2025
2 parents 8358fb2 + baa0d03 commit b7770a1
Show file tree
Hide file tree
Showing 54 changed files with 1,122 additions and 409 deletions.
2 changes: 1 addition & 1 deletion shvatka/core/games/interactors.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ async def __call__(self, user: dto.User) -> CurrentHints:
level_time = await self.dao.get_current_level_time(team, game)
level = await self.dao.get_level_by_game_and_number(game, level_time.level_number)
hints = level.get_hints_for_timedelta(datetime.now(tz=tz_utc) - level_time.start_at)
keys = await self.dao.get_team_typed_keys(game, team, level_time.level_number)
keys = await self.dao.get_team_typed_keys(game, team, level_time)
return CurrentHints(
hints=hints,
typed_keys=keys,
Expand Down
18 changes: 13 additions & 5 deletions shvatka/core/interfaces/dal/game_play.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async def get_poll_msg(self, team: dto.Team, game: dto.Game) -> int | None:


class GamePlayerDao(Committer, WaiverChecker, GameOrgsGetter, LevelByTeamGetter, Protocol):
async def is_key_duplicate(self, level: dto.Level, team: dto.Team, key: str) -> bool:
async def is_key_duplicate(self, level: dto.LevelTime, team: dto.Team, key: str) -> bool:
raise NotImplementedError

async def get_played_teams(self, game: dto.Game) -> Iterable[dto.Team]:
Expand All @@ -36,7 +36,7 @@ async def get_current_level(self, team: dto.Team, game: dto.Game) -> dto.Level:

async def get_correct_typed_keys(
self,
level: dto.Level,
level_time: dto.LevelTime,
game: dto.Game,
team: dto.Team,
) -> set[str]:
Expand All @@ -46,7 +46,7 @@ async def save_key(
self,
key: str,
team: dto.Team,
level: dto.Level,
level_time: dto.LevelTime,
game: dto.Game,
player: dto.Player,
type_: enums.KeyType,
Expand All @@ -55,12 +55,20 @@ async def save_key(
raise NotImplementedError

async def get_team_typed_keys(
self, game: dto.Game, team: dto.Team, level_number: int
self, game: dto.Game, team: dto.Team, level_time: dto.LevelTime
) -> list[dto.KeyTime]:
raise NotImplementedError

async def level_up(self, team: dto.Team, level: dto.Level, game: dto.Game) -> None:
async def level_up(
self, team: dto.Team, level: dto.Level, game: dto.Game, next_level_number: int
) -> None:
raise NotImplementedError

async def finish(self, game: dto.Game) -> None:
raise NotImplementedError

async def get_next_level(self, level: dto.Level, game: dto.Game) -> dto.Level:
raise NotImplementedError

async def get_level_by_name(self, level_name: str, game: dto.Game) -> dto.Level | None:
raise NotImplementedError
2 changes: 1 addition & 1 deletion shvatka/core/interfaces/dal/key_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async def get_typed_keys_grouped(self, game: dto.Game) -> dict[dto.Team, list[dt

class GameTeamKeyGetter(Protocol):
async def get_team_typed_keys(
self, game: dto.Game, team: dto.Team, level_number: int
self, game: dto.Game, team: dto.Team, level_time: dto.LevelTime
) -> list[dto.KeyTime]:
raise NotImplementedError

Expand Down
12 changes: 6 additions & 6 deletions shvatka/core/interfaces/dal/level_times.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ async def set_game_started(self, game: dto.Game) -> None:
async def get_played_teams(self, game: dto.Game) -> Iterable[dto.Team]:
raise NotImplementedError

async def set_teams_to_first_level(self, game: dto.Game, teams: Iterable[dto.Team]) -> None:
raise NotImplementedError


class LevelTimeChecker(Protocol):
async def is_team_on_level(self, team: dto.Team, level: dto.Level) -> bool:
async def set_to_level(
self,
team: dto.Team,
game: dto.Game,
level_number: int,
) -> dto.LevelTime:
raise NotImplementedError


Expand Down
1 change: 1 addition & 0 deletions shvatka/core/interfaces/scheduler/sheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ async def plain_hint(
level: dto.Level,
team: dto.Team,
hint_number: int,
lt_id: int,
run_at: datetime,
):
raise NotImplementedError
Expand Down
2 changes: 1 addition & 1 deletion shvatka/core/models/dto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .common import DateRange
from .forum_team import ForumTeam
from .forum_user import ForumUser
from .game import Game, PreviewGame, FullGame, GameResults
from .game import Game, PreviewGame, FullGame, GameResults, GameFinished
from .level import Level
from .level_testing import (
LevelTestSuite,
Expand Down
1 change: 1 addition & 0 deletions shvatka/core/models/dto/action/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
BonusKeyDecision,
KeyBonusCondition,
WrongKeyDecision,
LevelUpDecision,
)
from .state_holder import InMemoryStateHolder

Expand Down
16 changes: 12 additions & 4 deletions shvatka/core/models/dto/action/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,21 @@ def get(self, state_class: type[T]) -> T:
raise NotImplementedError


class Decision(Protocol):
type: DecisionType


class DecisionType(enum.StrEnum):
NOT_IMPLEMENTED = enum.auto()
LEVEL_UP = enum.auto()
SIGNIFICANT_ACTION = enum.auto()
NO_ACTION = enum.auto()
BONUS_TIME = enum.auto()


class Decision(Protocol):
type: DecisionType

def is_level_up(self) -> bool:
return self.type == DecisionType.LEVEL_UP


class LevelUpDecision(Decision):
type: typing.Literal[DecisionType.LEVEL_UP] = DecisionType.LEVEL_UP
next_level: str | None = None
41 changes: 34 additions & 7 deletions shvatka/core/models/dto/action/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
from typing import Literal

from shvatka.core.models import enums
from shvatka.core.utils.input_validation import is_key_valid
from . import StateHolder
from .decisions import NotImplementedActionDecision
from .interface import Action, State, Decision, Condition, DecisionType, ConditionType
from .interface import (
Action,
State,
Decision,
Condition,
DecisionType,
ConditionType,
LevelUpDecision,
)

SHKey: typing.TypeAlias = str

Expand All @@ -15,6 +24,12 @@ class BonusKey:
text: SHKey
bonus_minutes: float

def __post_init__(self):
if not is_key_valid(self.text):
raise ValueError
if not (-600 < self.bonus_minutes < 60):
raise ValueError("bonus out of available range")

def __eq__(self, other: object) -> bool:
if not isinstance(other, BonusKey):
return NotImplemented
Expand Down Expand Up @@ -50,25 +65,29 @@ def key_text(self) -> str:
return self.key


@dataclass
@dataclass(kw_only=True)
class KeyDecision(Decision):
type: DecisionType
key_type: enums.KeyType
duplicate: bool
key: SHKey

def is_level_up(self) -> bool:
return self.type == DecisionType.LEVEL_UP

@property
def key_text(self) -> str:
return self.key


@dataclass(kw_only=True)
class LevelUpKeyDecision(KeyDecision, LevelUpDecision):
type: typing.Literal[DecisionType.LEVEL_UP] = DecisionType.LEVEL_UP
next_level: str | None = None


@dataclass
class KeyWinCondition(Condition):
keys: set[SHKey]
type: Literal["WIN_KEY"] = ConditionType.WIN_KEY.name
next_level: str | None = None

def check(self, action: Action, state_holder: StateHolder) -> Decision:
if not isinstance(action, TypedKeyAction):
Expand All @@ -79,18 +98,26 @@ def check(self, action: Action, state_holder: StateHolder) -> Decision:
return WrongKeyDecision(duplicate=state.is_duplicate(action), key=action.key)
if not state.is_duplicate(action):
if self._is_all_typed(action, state):
type_ = DecisionType.LEVEL_UP
return LevelUpKeyDecision(
key_type=self._get_key_type(action), # TODO always simple
duplicate=state.is_duplicate(action),
key=action.key,
next_level=self.next_level,
)
else:
type_ = DecisionType.SIGNIFICANT_ACTION
else:
type_ = DecisionType.NO_ACTION
return KeyDecision(
type=type_,
key_type=enums.KeyType.simple if self._is_correct(action) else enums.KeyType.wrong,
key_type=self._get_key_type(action), # TODO always simple
duplicate=state.is_duplicate(action),
key=action.key,
)

def _get_key_type(self, action: TypedKeyAction):
return enums.KeyType.simple if self._is_correct(action) else enums.KeyType.wrong

def _is_correct(self, action: TypedKeyAction) -> bool:
return action.key in self.keys

Expand Down
4 changes: 2 additions & 2 deletions shvatka/core/models/dto/forum_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
@dataclass
class ForumUser:
db_id: int
forum_id: int
forum_id: int | None
name: str
registered: date
registered: date | None
player_id: int

@property
Expand Down
5 changes: 5 additions & 0 deletions shvatka/core/models/dto/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,8 @@ class GameResults:
published_chanel_id: int | None
results_picture_file_id: str | None
keys_url: str | None


@dataclass
class GameFinished:
game: Game
2 changes: 1 addition & 1 deletion shvatka/core/models/dto/team_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class TeamPlayer:
date_joined: datetime
date_left: datetime | None
role: str
emoji: str
emoji: str | None

_can_manage_waivers: bool
_can_manage_players: bool
Expand Down
43 changes: 32 additions & 11 deletions shvatka/core/services/game_play.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import timedelta, datetime

from shvatka.core.interfaces.dal.game_play import GamePreparer, GamePlayerDao
from shvatka.core.interfaces.dal.level_times import GameStarter, LevelTimeChecker
from shvatka.core.interfaces.dal.level_times import GameStarter, LevelByTeamGetter
from shvatka.core.interfaces.scheduler import Scheduler
from shvatka.core.models import dto, enums
from shvatka.core.models.dto import scn
Expand Down Expand Up @@ -70,13 +70,18 @@ async def start_game(
logger.info("game %s started", game.id)
teams = await dao.get_played_teams(game)

await dao.set_teams_to_first_level(game, teams)
level_times = {}
for team in teams:
level_times[team.id] = await dao.set_to_level(team=team, game=game, level_number=0)
await dao.commit()

await asyncio.gather(*[view.send_puzzle(team, game.levels[0]) for team in teams])

await asyncio.gather(
*[schedule_first_hint(scheduler, team, game.levels[0], now) for team in teams]
*[
schedule_first_hint(scheduler, team, game.levels[0], level_times[team.id].id, now)
for team in teams
]
)

await game_log.log(GameLogEvent(GameLogType.GAME_STARTED, {"game": game.name}))
Expand Down Expand Up @@ -168,9 +173,16 @@ async def process_level_up(
await finish_team(team, game, view, game_log, dao, locker)
return
next_level = await dao.get_current_level(team, game)
lt = await dao.get_current_level_time(team, game)

await view.send_puzzle(team=team, level=next_level)
await schedule_first_hint(scheduler, team, next_level)
await schedule_first_hint(
scheduler=scheduler,
team=team,
next_level=next_level,
lt_id=lt.id,
now=datetime.now(tz=tz_utc),
)
level_up_event = LevelUp(
team=team, new_level=next_level, orgs_list=await get_spying_orgs(game, dao)
)
Expand Down Expand Up @@ -209,9 +221,11 @@ async def finish_team(

async def send_hint(
level: dto.Level,
lt_id: int,
hint_number: int,
team: dto.Team,
dao: LevelTimeChecker,
game: dto.Game,
dao: LevelByTeamGetter,
view: GameView,
scheduler: Scheduler,
):
Expand All @@ -220,17 +234,22 @@ async def send_hint(
Если команда уже на следующем уровне - отправлять не надо.
:param level: Подсказка относится к уровню.
:param lt_id: Идентификатор соответствия уровня и команды.
:param hint_number: Номер подсказки, которую надо отправить.
:param team: Какой команде надо отправить подсказку.
:param game: Текущая игра.
:param dao: Слой доступа к данным.
:param view: Слой отображения.
:param scheduler: Планировщик.
"""
if not await dao.is_team_on_level(team, level):
lt = await dao.get_current_level_time(team, game)
if lt.id != lt_id:
logger.debug(
"team %s is not on level %s, skip sending hint #%s",
"team %s is not on level %s (should %s, actually %s), skip sending hint #%s",
team.id,
level.db_id,
level.number_in_game,
lt_id,
lt.id,
hint_number,
)
return
Expand All @@ -248,24 +267,26 @@ async def send_hint(
level.get_hint(hint_number),
level.get_hint(next_hint_number),
)
await scheduler.plain_hint(level, team, next_hint_number, next_hint_time)
await scheduler.plain_hint(level, team, next_hint_number, lt_id, next_hint_time)


async def schedule_first_hint(
scheduler: Scheduler,
team: dto.Team,
next_level: dto.Level,
now: datetime | None = None,
lt_id: int,
now: datetime,
):
await scheduler.plain_hint(
level=next_level,
team=team,
hint_number=1,
lt_id=lt_id,
run_at=calculate_first_hint_time(next_level, now),
)


def calculate_first_hint_time(next_level: dto.Level, now: datetime | None = None) -> datetime:
def calculate_first_hint_time(next_level: dto.Level, now: datetime) -> datetime:
return calculate_next_hint_time(next_level.get_hint(0), next_level.get_hint(1), now)


Expand Down
Loading

1 comment on commit b7770a1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coverage

Let's see coverage
FileStmtsMissCoverMissing
__init__.py00100% 
api
   __init__.py00100% 
   main_factory.py16193%31
   password_hash.py13130%1–2, 4–5, 8–9, 11, 14–15, 17–20
api/config
   __init__.py00100% 
api/config/models
   __init__.py00100% 
   auth.py150100% 
   main.py120100% 
api/config/parser
   __init__.py00100% 
   auth.py40100% 
   main.py100100% 
api/dependencies
   __init__.py18666%13–14, 18–19, 23, 30
   auth.py1083765%52–56, 88–89, 91–96, 99–101, 105–112, 138–142, 146–150, 154–156
   config.py130100% 
   player.py90100% 
   team.py13376%19–21
api/middlewares
   __init__.py13469%11, 13, 17, 21
   log.py9455%10–12, 15
api/models
   __init__.py00100% 
   auth.py40490%44, 53–55
   responses.py1161587%72, 95, 118, 141–143, 164–166, 181–183, 200–202
api/routes
   __init__.py110100% 
   auth.py602066%46–48, 74–78, 89–93, 104–109, 114
   game.py611378%34, 43–45, 72–73, 82–83, 95–98, 106
   team.py120100% 
   user.py27388%36–38
   version.py12191%11
api/utils
   __init__.py00100% 
   cookie_auth.py21195%28
   error_converter.py50100% 
common
   __init__.py20100% 
   data_examples.py90100% 
   factory.py380100% 
   log_utils.py4175%6
   url_factory.py6266%6, 9
common/config
   __init__.py30100% 
common/config/models
   __init__.py30100% 
   main.py34391%21, 25, 29
   paths.py18288%21, 25
common/config/parser
   __init__.py20100% 
   config_file_reader.py50100% 
   logging_config.py15380%19–21
   main.py120100% 
   paths.py7185%10
core
   __init__.py00100% 
core/config
   __init__.py00100% 
   constants.py10100% 
core/games
   __init__.py00100% 
   adapters.py170100% 
   dto.py100100% 
   interactors.py571573%22, 25–28, 33, 36–38, 49–51, 61–62, 79
core/interfaces
   __init__.py00100% 
core/interfaces/clients
   __init__.py00100% 
   file_storage.py100100% 
core/interfaces/dal
   __init__.py00100% 
   achievement.py70100% 
   base.py30100% 
   chat.py100100% 
   complex.py220100% 
   file_info.py70100% 
   game.py440100% 
   game_play.py230100% 
   key_log.py100100% 
   level.py270100% 
   level_testing.py140100% 
   level_times.py150100% 
   organizer.py210100% 
   player.py670100% 
   secure_invite.py90100% 
   team.py270100% 
   user.py110100% 
   waiver.py260100% 
core/interfaces/scheduler
   __init__.py10100% 
   sheduler.py17382%34–35, 38
core/migration_utils
   __init__.py00100% 
core/migration_utils/from_1_to_2
   __init__.py00100% 
   migrators.py43430%1–2, 5–6, 9–16, 24–39, 45–48, 51–52, 58–59, 62–65, 68, 76–77
core/migration_utils/models_0
   __init__.py660%1, 12, 20, 37–39
   file_content.py49490%1, 3, 5–6, 9–14, 17–20, 23–32, 34–36, 38–40, 43–45, 48–50, 53–55, 58–60, 63–66, 69–71
   game.py34340%1–3, 5–9, 12–16, 19–21, 24–26, 29–31, 34–40, 43–47
   hint_part.py1011010%1, 3–6, 8, 11–12, 19–22, 24–25, 28–31, 34–36, 38–39, 42–48, 50–51, 54–56, 59–61, 64–66, 68–69, 72–74, 76–77, 80–82, 84–87, 90–92, 94–97, 100–102, 104–107, 110–112, 114–117, 120–122, 124–125, 128–130, 132–133, 136–142, 144–145, 148–150, 152–153, 156
   level.py54540%1–3, 6–8, 10, 13–16, 18–21, 23–24, 27–30, 32–34, 36–43, 45–51, 54–61, 64–70
   parsed_zip.py30300%1–5, 7, 9, 12–16, 18–27, 33–36, 38–40, 42
   time_hint.py990%1, 3, 6–9, 12–14
core/models
   __init__.py00100% 
core/models/dto
   __init__.py180100% 
   achievement.py100100% 
   chat.py25292%23, 28
   common.py60100% 
   export_stat.py681775%10, 30–31, 35–44, 46, 56, 71, 88
   forum_team.py70100% 
   forum_user.py12191%15
   game.py861384%27, 30, 33, 42, 45, 49, 53, 57, 61, 65, 71, 124, 128
   level.py34294%34, 37
   level_testing.py260100% 
   levels_times.py270100% 
   organizer.py200100% 
   player.py572064%26–28, 30, 38, 41–45, 49, 54–57, 60–63, 66
   poll.py130100% 
   team.py34488%28, 39, 44, 47
   team_player.py721283%29–31, 34, 37, 73, 77, 107–109, 112, 122
   time_key.py440100% 
   user.py31293%20, 23
   version.py50100% 
   waiver.py140100% 
core/models/dto/action
   __init__.py60100% 
   decisions.py38489%22, 26, 45, 59
   interface.py300100% 
   keys.py104496%29, 31, 94, 147
   state_holder.py100100% 
core/models/dto/scn
   __init__.py60100% 
   file_content.py490100% 
   game.py340100% 
   hint_part.py1011882%51, 77, 85–87, 95–97, 105–107, 115–117, 125, 133, 145, 153
   level.py1892288%47, 57, 61, 65, 90–92, 104, 106, 113, 117, 128–130, 178, 182, 195, 223, 229, 238, 244, 269
   parsed_zip.py301550%20–27, 33–36, 39–40, 42
   time_hint.py36197%27
core/models/enums
   __init__.py100100% 
   achievement.py30100% 
   chat_type.py60100% 
   game_status.py120100% 
   hint_type.py140100% 
   invite_type.py50100% 
   key_type.py50100% 
   org_permission.py50100% 
   played.py24579%19, 23, 27, 31, 35
   team_player_permission.py70100% 
core/models/schems
   __init__.py40100% 
   level.py17664%13–15, 19–21
core/rules
   __init__.py00100% 
   game.py100100% 
   level.py15660%7–8, 16–17, 25, 31
core/services
   __init__.py00100% 
   achievement.py80100% 
   chat.py90100% 
   game.py1245456%47, 70–79, 95–101, 111, 127–131, 137–139, 156–157, 167–170, 189–195, 197–198, 200, 206–211, 216, 223–224, 232, 240
   game_play.py1213174%34–35, 40–41, 47, 67–68, 128, 134, 142–143, 157, 247, 255, 259, 265, 310, 315, 317–319, 323–329, 331–333
   game_stat.py180100% 
   key.py59886%26, 73–75, 77–78, 91, 118
   level.py371559%33–38, 46–48, 52–54, 60–62
   level_testing.py60591%46, 52, 56, 63, 84
   organizers.py641182%41, 46, 72, 77, 91, 97–99, 107, 129, 138
   player.py1638249%46, 54, 58, 62, 74–75, 79–80, 85–86, 90–92, 128, 134–138, 148–152, 158, 160, 163, 184, 192–193, 221–224, 228, 234–236, 240–241, 248, 255, 264–265, 270–271, 276–284, 286–287, 299–314, 323–325, 329–330, 334–335, 341, 352
   team.py893857%36–41, 43, 90, 98, 102, 108–112, 122–123, 127–128, 133–140, 153–154, 157–161, 170, 177–179
   user.py110100% 
   waiver.py631477%90, 102, 106–109, 119–121, 127–129, 138, 143
core/services/scenario
   __init__.py00100% 
   files.py361655%29–34, 40–44, 50–52, 60–61
   game_ops.py12191%18
   level_ops.py16193%23
   scn_zip.py342429%15–22, 25–27, 29–32, 36–44
core/utils
   __init__.py00100% 
   datetime_utils.py22386%30, 34–35
   defaults_constants.py40100% 
   exceptions.py1311092%5, 57, 75–78, 237–238, 263–264
   input_validation.py28871%36–39, 46–49
   key_checker_lock.py160100% 
core/views
   __init__.py00100% 
   game.py480100% 
   level.py80100% 
   texts.py70100% 
infrastructure
   __init__.py00100% 
   file_id_updater.py26260%1–2, 4, 6–12, 15–16, 18–19, 22–25, 27, 30–34, 37–38
   version.py8275%11–12
infrastructure/clients
   __init__.py00100% 
   factory.py5180%7
   file_gateway.py421466%33, 40–41, 44, 53–54, 59–62, 65–68
   file_storage.py25196%21
infrastructure/crawler
   __init__.py00100% 
   auth.py291934%13–16, 20–23, 27, 41–44, 51–52, 56–57, 61–62
   constants.py60100% 
   factory.py440%1–2, 5–6
infrastructure/crawler/game_scn
   __init__.py00100% 
   common.py220%1–2
infrastructure/crawler/game_scn/loader
   __init__.py00100% 
   load_scns.py1331330%1–7, 9–10, 12–18, 24–31, 33, 36–37, 39–40, 43–49, 57, 60, 67–72, 79–84, 86–87, 89, 92–102, 108–118, 120–121, 133, 136–143, 146–157, 162, 165, 175, 178–183, 192–197, 200–210, 213, 220–227
infrastructure/crawler/game_scn/parser
   __init__.py00100% 
   parser.py2082080%1–8, 10, 17–19, 21–28, 30–32, 35–36, 39–49, 52–53, 56, 59–74, 76–81, 85–86, 89–94, 99–123, 130, 138–142, 144–157, 162–163, 165–171, 173–175, 177–190, 193–194, 196–203, 208–210, 221–224, 226–231, 234–235, 242–243, 245–250, 252–254, 262, 264–266, 271–276, 278–281, 297, 300–307, 312–315, 318–319, 322–324
   parser_svast_engine.py1621620%1–6, 8, 15–16, 18–23, 29, 32–33, 36, 39–40, 43–56, 58–59, 61–62, 66–67, 69–77, 82–102, 109, 117–121, 123–136, 139–140, 142–148, 150–152, 154–155, 159–172, 177–179, 187–190, 192–197, 200–201, 208–209, 211–216, 218–220, 228, 230–232, 237–242, 244–247, 262, 265–266
   resourses.py440%1, 4–5, 9
infrastructure/crawler/game_scn/uploader
   __init__.py00100% 
   forum_scenario_uploader.py402927%15–22, 26, 30–31, 35, 46–47, 51–52, 56, 65–66, 70–71, 75–81, 85
   game_mapper.py372629%11, 15–18, 26–28, 36, 40–49, 51–52, 54, 61–64
infrastructure/crawler/models
   __init__.py20100% 
   credentials.py50100% 
   team.py24483%26, 29–31
   uploadable_game.py230100% 
infrastructure/crawler/teams
   __init__.py00100% 
   loader.py86860%1–5, 7–8, 10–19, 21–22, 25–26, 28–29, 32–37, 39, 42–48, 51–68, 70–72, 74, 78, 87, 90–95, 101–105, 108–109, 115–117, 127–129, 132, 134, 137–138, 141–142
   parse_teams.py74740%1–7, 9–11, 13–17, 19, 22–26, 29–32, 35–39, 41–47, 54–55, 57–62, 66–72, 80–81, 83–87, 91, 97, 99–101, 104–105, 108–115
infrastructure/db
   __init__.py00100% 
   factory.py25484%21–22, 46–47
infrastructure/db/config
   __init__.py00100% 
infrastructure/db/config/models
   __init__.py00100% 
   db.py34779%32–33, 38–39, 41–43
   storage.py130100% 
infrastructure/db/config/parser
   __init__.py00100% 
   db.py70100% 
   storage.py8187%10
infrastructure/db/dao
   __init__.py20100% 
   holder.py106694%102, 106, 118, 122, 138, 142
infrastructure/db/dao/complex
   __init__.py10100% 
   game.py931781%12, 35, 58, 61, 64, 67, 75, 78, 81, 84, 87, 92, 97, 100, 103, 114, 120
   game_play.py72987%11, 19, 22, 27, 30–32, 76, 80
   key_log.py341167%10, 21, 26, 31, 34, 37, 42, 45, 48, 51, 54
   level_testing.py30390%9, 38, 41
   level_times.py511962%9, 17, 20, 23, 28, 33, 41, 44, 52, 55, 58, 61, 66, 71, 76, 79, 82, 85, 88
   orgs.py25484%8, 25, 31, 39
   player.py461860%8, 16, 19, 22, 25, 28, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69
   team.py571082%10, 32, 58, 69, 72, 75, 78, 81, 84, 87
   waiver.py37391%10, 57, 60
infrastructure/db/dao/memory
   __init__.py00100% 
   level_testing.py47491%16, 56, 64, 67
   locker.py240100% 
infrastructure/db/dao/rdb
   __init__.py140100% 
   achievement.py260100% 
   base.py37197%41
   chat.py36683%30–31, 34, 37, 42–43
   file_info.py54885%38, 45–46, 55, 62, 75, 80, 90
   forum_team.py23673%23, 28, 38, 41, 48, 56
   forum_user.py24866%20, 26, 36, 39–40, 43, 46, 49
   game.py1282084%92, 127, 134, 160, 165–167, 178, 183, 197, 206, 210, 215, 225–226, 229, 249, 256, 263, 265
   level.py832372%69–70, 80, 87, 90, 100, 116–117, 122–124, 127, 133–136, 154, 159–160, 163–164, 167, 174
   level_times.py50688%88–92, 116
   log_keys.py42490%62, 72, 136, 143
   organizer.py50982%47–50, 53, 59–60, 80, 104
   player.py1014357%40, 58, 63, 86, 101, 110–113, 116–121, 124–128, 130–132, 135–138, 141–144, 147, 153–158, 161–164, 194
   team.py872867%36–37, 52–55, 57, 63–68, 72, 89, 114, 117–118, 121, 127–128, 131, 137, 142, 145–146, 171–172
   team_player.py1203471%50, 70, 108, 116–117, 140, 147, 154, 161, 170–171, 179–186, 196–199, 202, 219–220, 223, 228–229, 232, 237–238, 247, 273
   user.py471568%26, 29, 32, 35–36, 40, 46–52, 78, 81
   waiver.py631477%32–33, 61–64, 82, 98, 150, 157, 163, 166, 172, 175
infrastructure/db/dao/redis
   __init__.py20100% 
   poll.py53786%46–47, 50–52, 58, 90
   secure_invite.py330100% 
infrastructure/db/models
   __init__.py160100% 
   achievement.py21195%13
   base.py70100% 
   chat.py170100% 
   file_info.py250100% 
   forum_team.py15193%28
   forum_user.py17194%31
   game.py36197%22
   level.py49981%17–18, 33–35, 40–42, 47
   levels_times.py180100% 
   log_keys.py240100% 
   organizer.py190100% 
   player.py260100% 
   team.py250100% 
   team_player.py27196%60
   user.py180100% 
   waiver.py200100% 
infrastructure/di
   __init__.py7185%9
   bot.py18477%18, 26–27, 31
   config.py46589%34, 46, 50, 58, 62
   db.py38197%47
   files.py16193%17
   interactors.py25292%26, 32
   visualizalization.py43430%1–3, 5–8, 11–17, 20–23, 25–27, 36–37, 40–41, 44–46, 53–67
infrastructure/picture
   __init__.py30100% 
   picture.py705028%25–32, 36–42, 47–52, 56–75, 82, 84, 91–93, 99–102
   results_painter.py251252%18–20, 25–27, 30–35
infrastructure/scheduler
   __init__.py20100% 
   context.py10550%15–17, 23, 25
   factory.py11281%17–18
   scheduler.py492548%21–22, 29–30, 35–36, 43, 54–55, 66–69, 75–78, 90, 110, 125, 128–130, 134, 138
   wrappers.py43430%1, 3–12, 15–16, 22–24, 31–32, 41–44, 51–53, 56–57, 67–71, 73, 83–85, 88–89, 98–101, 103
TOTAL7641230769% 

Please sign in to comment.