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

70 statements  

1"""LICENSE 

2Copyright 2017 Hermann Krumrey <hermann@krumreyh.com> 

3 

4This file is part of bundesliga-tippspiel. 

5 

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. 

10 

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. 

15 

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""" 

19 

20 

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 

35 

36 

37class ReminderSettings(ModelMixin, db.Model): 

38 """ 

39 Database model that keeps track of reminder settings 

40 """ 

41 

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) 

49 

50 __tablename__ = "reminder_settings" 

51 

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) 

58 

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") 

63 

64 user: User = db.relationship( 

65 "User", 

66 backref=db.backref("reminder_settings", cascade="all, delete") 

67 ) 

68 

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) 

75 

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") 

82 

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() 

92 

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") 

104 

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) 

129 

130 return to_remind 

131 

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() 

152 

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)