Passed
Pull Request — master (#7)
by manny
04:09
created

LadderTimer.check_racer_finish()   A

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nop 1
dl 0
loc 10
rs 9.95
c 0
b 0
f 0
1
import asyncio
2
import logging
3
from datetime import datetime, timedelta
4
import pytz
5
from typing import List
6
from threading import Thread
7
8
from helpers import timer_to_str
9
import clients.ladder_client as ladder_client
10
from models.ladder import Flag, Racer, ScheduleItem, Season, Standings
11
12
13
def str_to_timer(str_timer):
14
    return timedelta(
15
        hours=float(str_timer[0:1]), minutes=float(str_timer[2:4]),
16
        seconds=float(str_timer[5:7]))
17
18
19
class LadderTimer:
20
    logger: logging.Logger = None
21
    enabled: bool = False
22
    source_name = ""
23
    timer_text = ""
24
    racer_id: int = 0
25
    racer_name: str = ""
26
    stats_source: str = ""
27
    stats: Standings = None
28
    season_for_stats: int = 0
29
    mode_for_stats: int = 0
30
    show_season_name: bool = False
31
    show_mode_name: bool = False
32
    show_rating: bool = False
33
    show_rank: bool = False
34
    show_change: bool = False
35
    show_win_loss_tie: bool = False
36
    pre_color: int = 0xFFFFFF
37
    racing_color: int = 0xFFFFFF
38
    winner_color: int = 0xFF0000
39
    loser_color: int = 0x00FF00
40
    ff_color: int = 0x00FF00
41
    started_at: datetime = None
42
    finish_time: timedelta = None
43
    result: str = ""
44
    next_race: ScheduleItem = None
45
    active_racers: List[Racer] = None
46
    current_season: Season = None
47
    flags: List[Flag] = []
48
    all_seasons: List[Season] = []
49
    schedule: List[ScheduleItem] = []
50
    last_timer_update: datetime = None
51
52
    @staticmethod
53
    def ladder_timezone():
54
        return pytz.timezone('US/Eastern')
55
56
    def __init__(self, logger: logging.Logger, racer_id: int = None):
57
        self.logger = logger
58
        if racer_id is not None:
59
            self.racer_id = racer_id
60
61
        ladder_update_t = Thread(target=self.ladder_update_thread)
62
        ladder_update_t.daemon = True
63
        ladder_update_t.start()
64
65
    def ladder_update_thread(self):
66
        self.logger.debug("starting ladder race update")
67
        loop = asyncio.new_event_loop()
68
        loop.run_until_complete(self.race_updater())
69
        loop.run_forever()
70
71
    async def race_updater(self):
72
        await self.update_active_racers()
73
        await self.update_flags()
74
        await self.update_season()
75
        await self.update_schedule(self.current_season.season_id)
76
        await self.update_timer()
77
        await self.update_stats()
78
        while True:
79
            if not self.enabled or self.finish_time is not None:
80
                await asyncio.sleep(5.0)
81
            else:
82
                now = datetime.now(self.ladder_timezone())
83
                if self.started_at is not None and now > self.started_at:
84
                    self.logger.debug("checking if racer finished")
85
                    await self.check_racer_finish()
86
                await asyncio.sleep(5.0)
87
88
    async def check_racer_finish(self):
89
        racer_history = ladder_client.get_racer_history(self.racer_id)
90
        if racer_history[-1].race_id == self.next_race.race_id:
91
            if racer_history[-1].FinishTime == "FF":
92
                self.result = "FF"
93
                self.finish_time = timedelta(seconds=0)
94
                self.logger.debug("racer forfeited")
95
            else:
96
                self.finish_time = str_to_timer(racer_history[-1].FinishTime)
97
                self.result = racer_history[-1].Result
98
99
    def get_timer_text(self):
100
        if self.finish_time:
101
            if self.result == "W":
102
                return self.winner_color, timer_to_str(self.finish_time)
103
            elif self.result == "L":
104
                return self.loser_color, timer_to_str(self.finish_time)
105
            elif self.result == "FF":
106
                return self.ff_color, "-:--:--.-"
107
        current_timer = timedelta(seconds=0)
108
        color = self.pre_color
109
        now = datetime.now(self.ladder_timezone())
110
        if self.started_at is None:
111
            current_timer = now - self.next_race.StartTime
112
            if (abs(current_timer.total_seconds()) < 600.0):
113
                loop = asyncio.new_event_loop()
114
                loop.run_until_complete(self.update_timer())
115
                loop.close()
116
            color = self.pre_color
117
        else:
118
            current_timer = now - self.started_at
119
            if current_timer.total_seconds() > 0:
120
                color = self.racing_color
121
        return color, timer_to_str(current_timer)
122
123
    def get_stats(self):
124
        stats = ""
125
        if self.enabled:
126
            if self.show_season_name:
127
                stats = stats + self.stats.Season + " "
128
            if self.show_mode_name:
129
                stats = stats + self.stats.Mode + " "
130
            if self.show_rank:
131
                stats = stats + "#" + str(self.stats.Rank) + " "
132
            if self.show_rating:
133
                stats = stats + str(self.stats.Rating) + " "
134
            if self.show_change:
135
                stats = stats + str(self.stats.Change) + " "
136
            if self.show_win_loss_tie:
137
                stats = (
138
                    stats + str(self.stats.Wins) + "/" +
139
                    str(self.stats.Losses) + "/" + str(self.stats.Ties)
140
                )
141
        return stats
142
143
    def update_settings(self, racer_name: str):
144
        if racer_name is None or racer_name == "":
145
            return False
146
        racer_id = self.get_racer_id(racer_name)
147
        if racer_id == 0:
148
            return False
149
        else:
150
            self.racer_id = racer_id
151
            self.racer_name = racer_name
152
            loop = asyncio.new_event_loop()
153
            loop.run_until_complete(self.update_stats())
154
            loop.close()
155
            return True
156
157
    def get_racer_id(self, racer_name: str) -> Racer:
158
        loop = asyncio.new_event_loop()
159
        loop.run_until_complete(self.update_active_racers())
160
        loop.close()
161
        for racer in self.active_racers:
162
            if racer.RacerName.lower() == racer_name.lower():
163
                return racer.racer_id
164
        return 0
165
166
    async def update_active_racers(self):
167
        if self.active_racers is None or self.active_racers == []:
168
            self.active_racers = ladder_client.get_active_racers()
169
170
    async def update_season(self):
171
        if self.current_season is None:
172
            self.all_seasons = ladder_client.get_seasons()
173
            if self.all_seasons is not None:
174
                self.current_season = (
175
                    next(filter(
176
                        lambda x: x.IsCurrentSeason, self.all_seasons
177
                    ), None)
178
                )
179
        self.logger.info(f"current_season = {self.current_season}")
180
181
    async def update_flags(self):
182
        self.flags = ladder_client.get_flags()
183
184
    async def update_schedule(self, season_id: int):
185
        if self.schedule is None or self.schedule == []:
186
            self.schedule = ladder_client.get_schedule(season_id)
187
            self.next_race = (
188
                next(x for x in self.schedule if not x.HasCompleted)
189
            )
190
            self.logger.info(f"next_race = {self.next_race}")
191
192
    async def update_timer(self):
193
        if (
194
            self.last_timer_update is not None and
195
            (datetime.now() - self.last_timer_update) < timedelta(seconds=20)
196
        ):
197
            return
198
        self.logger.debug("calling get_current_race_time")
199
        str_timer = ladder_client.get_current_race_time()
200
        self.last_timer_update = datetime.now()
201
        self.logger.info(f"str_timer= {str_timer}")
202
        timer_sign = 1.0
203
        if str_timer[0] == '-':
204
            timer_sign = -1.0
205
            str_timer = str_timer[1:]
206
        timer = str_to_timer(str_timer)
207
        timer = timer * timer_sign
208
        if timer == timedelta(seconds=0):
209
            self.started_at = None
210
        else:
211
            self.started_at = datetime.now(self.ladder_timezone()) - timer
212
213
    async def update_stats(self):
214
        if self.season_for_stats == -1:
215
            self.season_for_stats = self.current_season.season_id
216
        if self.mode_for_stats == -1:
217
            self.mode_for_stats = self.get_mode_id_from_name(
218
                self.next_race.Mode)
219
        self.logger.info(f"season_for_stats: {self.season_for_stats}")
220
        self.logger.info(f"mode_for_stats: {self.mode_for_stats}")
221
        standings = ladder_client.get_standings(
222
            self.season_for_stats, self.mode_for_stats)
223
        for stats in standings:
224
            if stats.RacerName.lower() == self.racer_name.lower():
225
                self.stats = stats
226
                return
227
        # default standings
228
        self.stats = Standings(
229
            self.racer_name, self.current_season.SeasonName,
230
            self.next_race.Mode, 1600, 0, 0, 0, 0, 0)
231
232
    def get_mode_id_from_name(self, mode_name: str):
233
        for flag in self.flags:
234
            if flag.Mode == mode_name:
235
                return flag.flag_id
236