Coverage for bundesliga_tippspiel/db/settings/ReminderSettings.py: 84%
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"""
21from flask import render_template
22from typing import List
23from bs4 import BeautifulSoup
24from datetime import timedelta, datetime
25from smtplib import SMTPAuthenticationError
26from jerrycan.base import app, db
27from jerrycan.db.ModelMixin import ModelMixin
28from jerrycan.db.TelegramChatId import TelegramChatId
29from puffotter.smtp import send_email
30from jerrycan.db.User import User
31from bundesliga_tippspiel.Config import Config
32from bundesliga_tippspiel.enums import ReminderType
33from bundesliga_tippspiel.db.user_generated.Bet import Bet
34from bundesliga_tippspiel.db.match_data.Match import Match
37class ReminderSettings(ModelMixin, db.Model):
38 """
39 Database model that keeps track of reminder settings
40 """
42 def __init__(self, *args, **kwargs):
43 """
44 Initializes the Model
45 :param args: The constructor arguments
46 :param kwargs: The constructor keyword arguments
47 """
48 super().__init__(*args, **kwargs)
50 __tablename__ = "reminder_settings"
52 user_id: int = db.Column(
53 db.Integer,
54 db.ForeignKey("users.id"),
55 primary_key=True
56 )
57 reminder_type = db.Column(db.Enum(ReminderType), primary_key=True)
59 active = db.Column(db.Boolean, nullable=False, default=True)
60 reminder_time: int = db.Column(db.Integer, nullable=False, default=86400)
61 last_reminder: str = db.Column(db.String(19), nullable=False,
62 default="1970-01-01:01-01-01")
64 user: User = db.relationship(
65 "User",
66 backref=db.backref("reminder_settings", cascade="all, delete")
67 )
69 @property
70 def reminder_time_delta(self) -> timedelta:
71 """
72 :return: The 'reminder_time' parameter as a datetime timedelta
73 """
74 return timedelta(seconds=self.reminder_time)
76 @property
77 def last_reminder_datetime(self) -> datetime:
78 """
79 :return: The 'last_reminder' parameter as a datetime object
80 """
81 return datetime.strptime(self.last_reminder, "%Y-%m-%d:%H-%M-%S")
83 def set_reminder_time(self, reminder_time: int):
84 """
85 Sets the reminder time and resets the time stored as the last reminder
86 :param reminder_time: the new reminder time
87 :return: None
88 """
89 self.reminder_time = reminder_time
90 self.last_reminder = "1970-01-01:01-01-01"
91 db.session.commit()
93 def get_due_matches(self) -> List[Match]:
94 """
95 Checks if the reminder is due and returns a list of matches that the
96 user still needs to bet on.
97 :return: The matches for which the reminder is due
98 """
99 now = datetime.utcnow()
100 start = max(now, self.last_reminder_datetime)
101 start_str = start.strftime("%Y-%m-%d:%H-%M-%S")
102 then = now + self.reminder_time_delta
103 then_str = then.strftime("%Y-%m-%d:%H-%M-%S")
105 due_matches: List[Match] = [
106 x for x in Match.query.filter_by(
107 season=Config.season(),
108 league=Config.OPENLIGADB_LEAGUE
109 ).all()
110 if start_str < x.kickoff < then_str
111 ]
112 user_bet_matches = [
113 (bet.match.home_team_abbreviation,
114 bet.match.away_team_abbreviation,
115 bet.match.season)
116 for bet in Bet.query.filter_by(
117 user_id=self.user_id,
118 season=Config.season(),
119 league=Config.OPENLIGADB_LEAGUE
120 ).options(db.joinedload(Bet.match)).all()
121 ]
122 to_remind = []
123 for match in due_matches:
124 identifier = (match.home_team_abbreviation,
125 match.away_team_abbreviation,
126 match.season)
127 if identifier not in user_bet_matches:
128 to_remind.append(match)
130 return to_remind
132 def send_reminder(self):
133 """
134 Sends a reminder message if it's due
135 :return: None
136 """
137 due = self.get_due_matches()
138 if len(due) < 1:
139 return
140 else:
141 app.logger.debug("Sending reminder to {}.".format(self.user.email))
142 message = render_template(
143 "email/reminder.html",
144 user=self.user,
145 matches=due,
146 hours=int(self.reminder_time / 3600)
147 )
148 self.send_reminder_message(message)
149 last_match = max(due, key=lambda x: x.kickoff)
150 self.last_reminder = last_match.kickoff
151 db.session.commit()
153 def send_reminder_message(self, message: str):
154 """
155 Sends a reminder message using the appropriate method of delivery
156 :param message: The message to send
157 :return: None
158 """
159 if self.reminder_type == ReminderType.EMAIL: 159 ↛ 172line 159 didn't jump to line 172, because the condition on line 159 was never false
160 try:
161 send_email(
162 self.user.email,
163 "Tippspiel Erinnerung",
164 message,
165 Config.SMTP_HOST,
166 Config.SMTP_ADDRESS,
167 Config.SMTP_PASSWORD,
168 Config.SMTP_PORT
169 )
170 except SMTPAuthenticationError:
171 app.logger.error("Invalid SMTP settings, failed to send email")
172 elif self.reminder_type == ReminderType.TELEGRAM:
173 telegram = TelegramChatId.query.filter_by(user=self.user).first()
174 if telegram is not None:
175 message = BeautifulSoup(message, "html.parser").text
176 message = "\n".join([x.strip() for x in message.split("\n")])
177 telegram.send_message(message)