GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — develop (#307)
by
unknown
48s
created

Division   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 6
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
wmc 1
1
# coding=utf-8
2
import logging
3
from abc import ABCMeta, abstractmethod
4
from typing import List, Dict
5
6
logger = logging.getLogger(__name__)
7
8
9
class Division:
10
    def __init__(self, id: int, name: str, league: int, threshold: float):
11
        self.id = id
12
        self.name = name
13
        self.league = league
14
        self.threshold = threshold
15
16
17
class PlayerDivisionInfo:
18
    def __init__(self, user_id: int, current_league: int, current_score: float):
19
        self.user_id = user_id
20
        self.league = current_league
21
        self.score = current_score
22
23
    def is_in_inferior_league(self, compared_to: 'PlayerDivisionInfo') -> bool:
24
        return self.league < compared_to.league
25
26
    def is_in_superior_league(self, compared_to: 'PlayerDivisionInfo') -> bool:
27
        return self.league > compared_to.league
28
29
    def __str__(self):
30
        return "PlayerDivisionInfo(user_id=%s, league=%s, score=%s)" % (self.user_id, self.league, self.score)
31
32
33
class DivisionAccessor(metaclass=ABCMeta):
34
    """
35
    Interface for the persistance layer
36
    """
37
38
    @abstractmethod
39
    async def get_divisions(self) -> List['Division']:
40
        """
41
        :return list of divisions
42
        """
43
        pass  # pragma: no cover
44
45
    @abstractmethod
46
    async def add_player(self, player: 'PlayerDivisionInfo') -> None:
47
        """
48
        Add a new player to the persistance layer
49
        :param player: new player with zero score and initial league
50
        """
51
        pass  # pragma: no cover
52
53
    @abstractmethod
54
    async def update_player(self, player: 'PlayerDivisionInfo') -> None:
55
        """
56
        Update a player after a game (league, score, games)
57
        :param player: updated player
58
        """
59
        pass  # pragma: no cover
60
61
62
class DivisionService:
63
    """
64
    Division service calculates changes to the ladder leagues & divisions after each game
65
    """
66
67
    def __init__(self, player_division_infos: List['PlayerDivisionInfo'],
68
                 accessor: 'DivisionAccessor'):
69
        self._divisions = None
70
        self.players = dict()  # type: Dict[int, 'PlayerDivisionInfo']
71
        self.accessor = accessor
72
73
        for info in player_division_infos:
74
            self.players[info.user_id] = info
75
76
    async def get_divisions(self):
77
        if self._divisions is None:
78
            self._divisions = await self.accessor.get_divisions()
79
80
        return self._divisions
81
82
    async def add_player(self, player_id: int) -> None:
83
        logger.info("Added new player %s to divisions", player_id)
84
        self.players[player_id] = PlayerDivisionInfo(player_id, 1, 0.0)
85
        await self.accessor.add_player(self.players[player_id])
86
87
    async def update_player_stats(self, player: PlayerDivisionInfo, new_score: float) -> None:
88
        logger.debug("Update score for %s to %s", player)
89
        player.score = new_score
90
        await self.accessor.update_player(player)
91
92
    async def promote_player(self, player):
93
        logger.info("%s got promoted to league %s", player, player.league + 1)
94
        player.score = 0.0
95
        player.league += 1
96
        await self.accessor.update_player(player)
97
98
    async def post_result(self, player_one: int, player_two: int, winning_slot: int) -> None:
99
        """
100
        Post a ladder game result to the division system
101
        :param player_one: FAF User ID of 1st player
102
        :param player_two: FAF User ID of 2nd player
103
        :param winning_slot: 0 for draw, 1 for 1st player, 2 for 2nd player
104
        """
105
        if player_one not in self.players:
106
            await self.add_player(player_one)
107
108
        if player_two not in self.players:
109
            await self.add_player(player_two)
110
111
        if winning_slot == 0:
112
            logger.info("Game ended in a draw - no changes in score")
113
            await self.update_player_stats(self.players[player_one], self.players[player_one].score)
114
            await self.update_player_stats(self.players[player_two], self.players[player_two].score)
115
            return
116
117
        winner = self.players[player_one] if winning_slot == 1 else self.players[player_two]
118
        loser = self.players[player_two] if winning_slot == 1 else self.players[player_one]
119
120
        if winner.is_in_inferior_league(loser):
121
            gain = 1.5
122
            loss = 1.0
123
        elif winner.is_in_superior_league(loser):
124
            gain = 0.5
125
            loss = 0.5
126
        else:
127
            gain = 1.0
128
            loss = 0.5
129
130
        logger.info("%s won against %s - gain: %s - loss: %s", winner, loser, gain, loss)
131
132
        if winner.score + gain > await self.max_league_threshold(winner.league):
133
            await self.promote_player(winner)
134
        else:
135
            await self.update_player_stats(winner, winner.score + gain)
136
137
        await self.update_player_stats(loser, max(0.0, loser.score - loss))
138
139
    async def get_player_division(self, player_id: int) -> 'Division':
140
        player = self.players[player_id]
141
        return await self.get_division(player.league, player.score)
142
143
    async def max_league_threshold(self, league: int) -> float:
144
        return max([x.threshold for x in await self.get_divisions() if x.league == league])
145
146
    async def get_division(self, league: int, score: float) -> Division:
147
        divisions_of_league = [x for x in await self.get_divisions() if x.league == league]
148
149
        for division in sorted(divisions_of_league, key=lambda d: d.threshold):
150
            if division.threshold > score:
151
                return division
152
153
        logger.error("league %s has no division for score %s", league, score)
154
        return None
155