Skip to content

Commit

Permalink
Merge pull request #115 from bomzheg/feature/#106
Browse files Browse the repository at this point in the history
feature/#106
  • Loading branch information
bomzheg authored Dec 4, 2024
2 parents 73579c6 + 12d92f0 commit 6840829
Show file tree
Hide file tree
Showing 52 changed files with 1,686 additions and 145 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
jobs:
lint-and-test:
uses: ./.github/workflows/test.yml
secrets: inherit
build:
needs: lint-and-test
runs-on: ubuntu-latest
Expand Down
13 changes: 5 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jobs:
run: source .venv/bin/activate && pytest --cov-report "xml:coverage.xml" --cov=shvatka tests/
- name: Coverage comment
id: coverageComment
continue-on-error: true
uses: MishaKav/pytest-coverage-comment@main
with:
pytest-xml-coverage-path: ./coverage.xml
Expand All @@ -74,20 +75,16 @@ jobs:
hide-comment: false
report-only-changed-files: false
remove-link-from-badge: false
coverage-badge:
needs: [test]
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Create the Badge
uses: schneegans/[email protected]
continue-on-error: true
with:
auth: ${{ secrets.BAGE_GIST }} // don't passed from direct push
auth: ${{ secrets.BAGE_GIST }}
gistID: 99469cb5f8a18784c1f03d229a799427
filename: bage.json
label: Coverage Report
message: ${{ steps.coverageComment.outputs.coverage }} // can't access from another job
color: ${{ steps.coverageComment.outputs.color }} // can't access from another job
message: ${{ steps.coverageComment.outputs.coverage }}
color: ${{ steps.coverageComment.outputs.color }}
namedLogo: python
docs:
needs: [build]
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ omit = [
".*",
"*/site-packages/*",
"shvatka/tgbot/*",
"shvatka/infrastructure/db/migrations/*",
"*/__main__.py",
]

Expand All @@ -97,6 +98,7 @@ exclude_lines = [
"if __name__ == .__main__.:",
"class .*\bProtocol\\):",
"@(abc\\.)?abstractmethod",
"(typing\\.)?assert_never",
]

[tool.black]
Expand Down
17 changes: 14 additions & 3 deletions shvatka/api/models/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@
from datetime import datetime
from typing import Sequence, Generic

from adaptix import Retort, dumper
from adaptix import Retort, dumper, P

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
from shvatka.core.models.dto import scn, action
from shvatka.core.models.enums import GameStatus

T = typing.TypeVar("T")
retort = Retort(recipe=[dumper(scn.HintsList, lambda x: x.hints)])
retort = Retort(
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],
),
]
)


@dataclass
Expand Down
8 changes: 4 additions & 4 deletions shvatka/common/data_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
name_id="level_100",
game_id=10,
number_in_game=0,
scenario=scn.LevelScenario(
scenario=scn.LevelScenario.legacy_factory(
id="level_100",
keys={"SH1"},
time_hints=scn.HintsList(
Expand Down Expand Up @@ -102,7 +102,7 @@
name_id="level_101",
game_id=10,
number_in_game=1,
scenario=scn.LevelScenario(
scenario=scn.LevelScenario.legacy_factory(
id="level_101",
keys={"SH2"},
time_hints=scn.HintsList(
Expand Down Expand Up @@ -165,7 +165,7 @@
name_id="level_102",
game_id=10,
number_in_game=0,
scenario=scn.LevelScenario(
scenario=scn.LevelScenario.legacy_factory(
id="level_102",
keys={"SH3"},
time_hints=scn.HintsList(
Expand Down Expand Up @@ -228,7 +228,7 @@
name_id="level_103",
game_id=10,
number_in_game=0,
scenario=scn.LevelScenario(
scenario=scn.LevelScenario.legacy_factory(
id="level_103",
keys={"SH4"},
time_hints=scn.HintsList(
Expand Down
21 changes: 17 additions & 4 deletions shvatka/common/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
from telegraph.aio import Telegraph

from shvatka.common.url_factory import UrlFactory
from shvatka.core.models.dto import scn
from shvatka.core.models.dto.scn import HintsList, TimeHint
from shvatka.core.models.dto import scn, action
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
from shvatka.core.utils import exceptions
from shvatka.core.utils.input_validation import validate_level_id, is_multiple_keys_normal
Expand All @@ -37,9 +38,14 @@ def create_telegraph(self, bot_config: BotConfig) -> Telegraph:


REQUIRED_GAME_RECIPES = [
name_mapping(map={"__model_version__": "__model_version__"}),
loader(HintsList, lambda x: HintsList.parse(x), Chain.LAST),
ABCProxy(HintsList, list[TimeHint]), # internal class, can be broken in next version adaptix
dumper(set, lambda x: tuple(x)),
ABCProxy(HintsList, list[TimeHint]), # internal class, can be broken in next adaptix version
loader(Conditions, lambda x: Conditions(x), Chain.LAST),
ABCProxy(
Conditions, list[AnyCondition]
), # internal class, can be broken in next adaptix version
dumper(P[action.KeyWinCondition].keys, list),
]


Expand All @@ -62,6 +68,13 @@ 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,
Expand Down
6 changes: 6 additions & 0 deletions shvatka/common/log_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from base64 import b64encode
from typing import Any


def obfuscate_sensitive(information: Any) -> str:
return b64encode(str(information).encode("utf8")).decode("utf8")
5 changes: 5 additions & 0 deletions shvatka/core/interfaces/dal/game_play.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ async def save_key(
) -> dto.KeyTime:
raise NotImplementedError

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

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

Expand Down
Empty file.
Empty file.
79 changes: 79 additions & 0 deletions shvatka/core/migration_utils/from_1_to_2/migrators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from shvatka.core.migration_utils import models_0
from shvatka.core.models.dto import scn, action


def bonus_key_0_to_1(bonus_key: models_0.BonusKey) -> action.BonusKey:
return action.BonusKey(text=bonus_key.text, bonus_minutes=bonus_key.bonus_minutes)


def hint_0_to_1(hint: models_0.AnyHint) -> scn.AnyHint:
match hint:
case models_0.TextHint(text=text):
return scn.TextHint(text=text)
case models_0.GPSHint(latitude=latitude, longitude=longitude):
return scn.GPSHint(latitude=latitude, longitude=longitude)
case models_0.VenueHint as vh:
return scn.VenueHint(
latitude=vh.latitude,
longitude=vh.longitude,
title=vh.title,
address=vh.address,
foursquare_id=vh.foursquare_id,
foursquare_type=vh.foursquare_type,
)
case models_0.PhotoHint(file_guid=file_guid, caption=caption):
return scn.PhotoHint(file_guid=file_guid, caption=caption)
case models_0.AudioHint(file_guid=file_guid, caption=caption, thumb_guid=thumb_guid):
return scn.AudioHint(file_guid=file_guid, thumb_guid=thumb_guid, caption=caption)
case models_0.VideoHint(file_guid=file_guid, caption=caption, thumb_guid=thumb_guid):
return scn.VideoHint(file_guid=file_guid, thumb_guid=thumb_guid, caption=caption)
case models_0.DocumentHint(file_guid=file_guid, caption=caption, thumb_guid=thumb_guid):
return scn.DocumentHint(file_guid=file_guid, caption=caption, thumb_guid=thumb_guid)
case models_0.AnimationHint(file_guid=file_guid, caption=caption, thumb_guid=thumb_guid):
return scn.AnimationHint(file_guid=file_guid, caption=caption, thumb_guid=thumb_guid)
case models_0.VoiceHint(file_guid=file_guid, caption=caption):
return scn.VoiceHint(file_guid=file_guid, caption=caption)
case models_0.VideoNoteHint(file_guid=guid):
return scn.VideoNoteHint(file_guid=guid)
case models_0.ContactHint as ch:
return scn.ContactHint(
phone_number=ch.phone_number,
first_name=ch.first_name,
last_name=ch.last_name,
vcard=ch.vcard,
)
case models_0.StickerHint(file_guid=guid):
return scn.StickerHint(file_guid=guid)
case _:
raise RuntimeError("unknown hint type")


def time_hint_0_to_1(time_hint: models_0.TimeHint) -> scn.TimeHint:
return scn.TimeHint(
time=time_hint.time,
hint=[hint_0_to_1(hint) for hint in time_hint.hint],
)


def hints_0_to_1(hints: models_0.HintsList) -> scn.HintsList:
return scn.HintsList([time_hint_0_to_1(hint) for hint in hints])


def level_0_to_1(level: models_0.LevelScenario) -> scn.LevelScenario:
conditions: list[action.AnyCondition] = [action.KeyWinCondition(set(level.keys))]
if level.bonus_keys:
conditions.append(
action.KeyBonusCondition({bonus_key_0_to_1(b) for b in level.bonus_keys})
)
return scn.LevelScenario(
id=level.id,
time_hints=hints_0_to_1(level.time_hints),
conditions=scn.Conditions(conditions),
__model_version__=1,
)


def game_0_to_1(game: models_0.GameScenario) -> scn.GameScenario:
return scn.GameScenario(
name=game.name, levels=[level_0_to_1(lvl) for lvl in game.levels], __model_version__=1
)
39 changes: 39 additions & 0 deletions shvatka/core/migration_utils/models_0/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from .file_content import (
FileMeta,
FileContentLink,
TgLink,
ParsedTgLink,
SavedFileMeta,
StoredFileMeta,
FileMetaLightweight,
UploadedFileMeta,
VerifiableFileMeta,
)
from .game import (
GameScenario,
FullGameScenario,
ParsedGameScenario,
ParsedCompletedGameScenario,
RawGameScenario,
UploadedGameScenario,
)
from .hint_part import (
AnyHint,
BaseHint,
FileMixin,
TextHint,
GPSHint,
VenueHint,
AudioHint,
VideoHint,
DocumentHint,
AnimationHint,
VoiceHint,
VideoNoteHint,
StickerHint,
PhotoHint,
ContactHint,
)
from .level import LevelScenario, SHKey, BonusKey, HintsList
from .parsed_zip import ParsedZip
from .time_hint import TimeHint
71 changes: 71 additions & 0 deletions shvatka/core/migration_utils/models_0/file_content.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from __future__ import annotations

from dataclasses import dataclass, field

from shvatka.core.models import dto
from shvatka.core.models.enums.hint_type import HintType


@dataclass
class TgLink:
file_id: str
"""telegram file_id"""
content_type: HintType
"""type of content"""


@dataclass
class FileContentLink:
file_path: str
"""path to file in file system"""


@dataclass
class FileMetaLightweight:
guid: str
"""GUID for filename in file storage, DB and in archive"""
original_filename: str
"""Filename from user before renamed to guid"""
extension: str
"""extension with leading dot: ".zip" ".tar.gz" etc"""
content_type: HintType | None = field(kw_only=True, default=None)
"""type of content"""

@property
def local_file_name(self):
return self.guid + (self.extension or "")

@property
def public_filename(self):
return self.original_filename + (self.extension or "")


@dataclass
class StoredFileMeta(FileMetaLightweight):
file_content_link: FileContentLink


@dataclass
class UploadedFileMeta(FileMetaLightweight):
tg_link: TgLink | None = None


@dataclass
class FileMeta(StoredFileMeta):
tg_link: TgLink


@dataclass
class VerifiableFileMeta(FileMeta):
author_id: int


@dataclass
class SavedFileMeta(VerifiableFileMeta):
id: int
author: dto.Player


@dataclass
class ParsedTgLink(TgLink):
filename: str | None = None
Loading

1 comment on commit 6840829

@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%77, 100, 123, 146–148, 169–171, 186–188, 205–207
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.py210100% 
   key_log.py100100% 
   level.py270100% 
   level_testing.py140100% 
   level_times.py170100% 
   organizer.py210100% 
   player.py670100% 
   secure_invite.py90100% 
   team.py270100% 
   user.py110100% 
   waiver.py260100% 
core/interfaces/scheduler
   __init__.py10100% 
   sheduler.py17382%33–34, 37
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.py831384%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.py250100% 
   keys.py93297%75, 120
   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.py1173173%34–35, 40–41, 47, 67–68, 123, 129, 137–138, 152, 230, 236, 240, 246, 289, 294, 296–298, 302–308, 310–312
   game_stat.py180100% 
   key.py46784%26, 70–72, 74–75, 98
   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.py332330%15–22, 25–27, 29–32, 36–38, 42–46
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.py1311310%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–101, 104–113, 115–116, 128, 131–138, 141–152, 157, 160, 170, 173–178, 187–192, 195–205, 208, 215–222
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%106, 110, 122, 126, 156, 160
infrastructure/db/dao/complex
   __init__.py10100% 
   game.py971782%13, 38, 62, 65, 68, 71, 79, 82, 85, 88, 91, 96, 101, 104, 107, 118, 124
   game_play.py77988%25, 28, 33, 36–38, 84, 88, 140
   key_log.py341167%10, 21, 26, 31, 34, 37, 42, 45, 48, 51, 54
   level_testing.py30293%37, 40
   level_times.py511962%9, 17, 20, 23, 28, 33, 41, 44, 52, 55, 58, 61, 66, 71, 76, 79, 82, 85, 88
   orgs.py25388%24, 30, 38
   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.py39294%58, 61
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.py832569%67–70, 80, 87, 90, 100, 116–117, 122–124, 127, 133–136, 154, 159–160, 163–164, 167, 174
   level_times.py51688%88–92, 116
   log_keys.py42490%62, 72, 135, 142
   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.py48981%16–17, 37–39, 44–46, 51
   levels_times.py190100% 
   log_keys.py230100% 
   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, 89, 104, 119, 122–124, 128, 132
   wrappers.py41410%1, 3–12, 15–16, 22–24, 31–32, 41–44, 51–53, 56–57, 66–68, 70, 78–80, 83–84, 93–96, 98
TOTAL7611229869% 

Please sign in to comment.