Coverage for bundesliga_tippspiel/background/season_events.py: 88%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2017 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of bundesliga-tippspiel.
6bundesliga-tippspiel is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11bundesliga-tippspiel is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with bundesliga-tippspiel. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20from typing import List
21from flask import render_template
22from bs4 import BeautifulSoup
23from smtplib import SMTPAuthenticationError
24from datetime import datetime, timedelta
25from puffotter.smtp import send_email
26from jerrycan.base import db, app
27from jerrycan.db.User import User
28from bundesliga_tippspiel.Config import Config
29from bundesliga_tippspiel.db.SeasonEvent import SeasonEvent, SeasonEventType
30from bundesliga_tippspiel.db.match_data.Match import Match
33def handle_season_events():
34 """
35 Handles any events that happen once every season
36 :return: None
37 """
38 for event in load_season_events():
40 try:
41 if event.executed:
42 continue
43 elif event.event_type == SeasonEventType.PRE_SEASON_MAIL:
44 event.executed = handle_preseason_reminder()
45 elif event.event_type == SeasonEventType.MID_SEASON_REMINDER:
46 event.executed = handle_midseason_reminder()
47 elif event.event_type == SeasonEventType.POST_SEASON_WRAPUP: 47 ↛ 52line 47 didn't jump to line 52, because the condition on line 47 was never false
48 event.executed = handle_postseason_wrapup()
49 else: # pragma: no cover
50 pass
52 db.session.commit()
53 except SMTPAuthenticationError:
54 app.logger.error("Failed to send email (Authentication failed)")
57def load_season_events() -> List[SeasonEvent]:
58 """
59 Loads all event states from the database
60 :return: The event states
61 """
62 existing = {
63 x.event_type: x
64 for x in SeasonEvent.query.filter_by(
65 season=Config.season(),
66 league=Config.OPENLIGADB_LEAGUE
67 ).all()
68 }
69 for event_type in SeasonEventType:
70 if event_type not in existing:
71 new = SeasonEvent(
72 event_type=event_type,
73 executed=False,
74 season=Config.season(),
75 league=Config.OPENLIGADB_LEAGUE
76 )
77 db.session.add(new)
78 existing[event_type] = new
80 db.session.commit()
81 return list(existing.values())
84def handle_preseason_reminder() -> bool:
85 """
86 Sends a reminder to existing users a week before the start of the season
87 :return: Whether or not the reminder was sent
88 """
89 return __handle_reminder(
90 1,
91 timedelta(days=7),
92 "email/preseason.html",
93 True
94 )
97def handle_midseason_reminder() -> bool:
98 """
99 Handles sending out midseason reminders
100 :return: Whether the reminder was sent or not
101 """
102 return __handle_reminder(
103 18,
104 timedelta(days=7),
105 "email/midseason.html",
106 True
107 )
110def handle_postseason_wrapup() -> bool:
111 """
112 Handles the post-season wrapup
113 :return: None
114 """
115 return __handle_reminder(
116 34,
117 timedelta(days=1),
118 "email/postseason.html",
119 False
120 )
123def __handle_reminder(
124 matchday: int,
125 delta: timedelta,
126 message_file: str,
127 before: bool
128) -> bool:
129 """
130 Handles sending out a reminder after/before a specified time
131 :param matchday: The matchday that acts as an anchor
132 :param delta: The time delta relative to the matchday
133 :param message_file: The HTML template containing the message
134 :param before: Whether or not the email should be sent before the event
135 :return: True if the message was sent, False if it was not yet due
136 """
137 kickoff_times = [
138 x.kickoff_datetime
139 for x in Match.query.filter_by(
140 matchday=matchday,
141 season=Config.season(),
142 league=Config.OPENLIGADB_LEAGUE
143 ).all()
144 ]
146 if len(kickoff_times) == 0: 146 ↛ 147line 146 didn't jump to line 147, because the condition on line 146 was never true
147 return False
148 elif before:
149 kickoff = min(kickoff_times)
150 else:
151 kickoff = max(kickoff_times)
153 now = datetime.utcnow()
155 if before and kickoff - delta > now:
156 return False
157 elif not before and kickoff + delta > now:
158 return False
159 else:
160 for user in User.query.filter_by(confirmed=True).all():
162 message = render_template(
163 message_file,
164 user=user
165 )
167 send_email(
168 user.email,
169 f"Fußball Tippspiel Saison {Config.season_string()}",
170 message,
171 Config.SMTP_HOST,
172 Config.SMTP_ADDRESS,
173 Config.SMTP_PASSWORD,
174 Config.SMTP_PORT
175 )
177 telegram = user.telegram_chat_id
178 if telegram is not None: 178 ↛ 179line 178 didn't jump to line 179, because the condition on line 178 was never true
179 message = BeautifulSoup(message, "html.parser").text
180 message = "\n".join([x.strip() for x in message.split("\n")])
181 telegram.send_message(message)
182 return True