|
1 | | -import asyncio |
2 | | -import smtplib |
3 | | -import traceback |
4 | 1 | from aiohttp import ClientSession |
5 | 2 | from contextlib import asynccontextmanager |
6 | | -from datetime import date, datetime |
7 | | -from datetime import time as dt_time |
8 | | -from datetime import timedelta |
9 | | -from email.message import EmailMessage |
10 | | -from email.mime.text import MIMEText |
11 | 3 | from functools import wraps |
12 | 4 |
|
13 | 5 | import asyncpg |
@@ -96,7 +88,6 @@ async def render_template(template: str, request: Request, **context) -> HTTPRes |
96 | 88 | user = await conn.fetchrow('SELECT * FROM users WHERE id = $1', request.ctx.session['id']) |
97 | 89 | context['avatar'] = user['avatar'] |
98 | 90 | context['username'] = user['name'] |
99 | | - context['discrim'] = user['discrim'] |
100 | 91 | context['messages'] = request.ctx.session['messages'] |
101 | 92 |
|
102 | 93 | html_content = template.render(**context) |
@@ -132,167 +123,3 @@ async def decorated_function(request, *args, **kwargs): |
132 | 123 | return response.json({'error': True, 'message': 'Unauthorized'}, status=401) |
133 | 124 | return decorated_function |
134 | 125 | return decorator |
135 | | - |
136 | | - |
137 | | -def daterange(start_date: date, end_date: date) -> list: |
138 | | - """Creates a list of dates from the start date to end date, inclusive""" |
139 | | - day_count = (end_date - start_date).days + 1 |
140 | | - return [start_date + timedelta(days=i) for i in range(day_count)] |
141 | | - |
142 | | -def thisweek(today: date) -> list: |
143 | | - """Gets a list of dates for this week""" |
144 | | - if 0 <= today.weekday() <= 4: |
145 | | - # Monday to Friday |
146 | | - monday = today - timedelta(days=today.weekday()) # Last Monday |
147 | | - else: |
148 | | - monday = today - timedelta(days=today.weekday() - 7) |
149 | | - friday = monday + timedelta(days=4) |
150 | | - |
151 | | - return daterange(monday, friday) |
152 | | - |
153 | | -async def get_school_week(requested_date: date, first_day: date, week=True): |
154 | | - no_school = [ |
155 | | - date(2020, 9, 28), # Yom Kippur |
156 | | - date(2020, 10, 12), # Columbus day |
157 | | - date(2020, 11, 3), # Election day |
158 | | - date(2020, 11, 11), # Veteran's day |
159 | | - *daterange(date(2020, 11, 25), date(2020, 11, 27)), # Thanksgiving break |
160 | | - *daterange(date(2020, 12, 24), date(2021, 1, 1)), # Holiday break |
161 | | - date(2021, 1, 18), # Martin Luther King Day |
162 | | - *daterange(date(2021, 2, 15), date(2021, 2, 19)) # Winter break |
163 | | - ] |
164 | | - special_days = [ |
165 | | - date(2020, 10, 2), |
166 | | - date(2021, 2, 1), # Snow day 2 (first snow day didn't affect A/B days) |
167 | | - date(2021, 2, 9), # Idk why they messed up the days in the first place only to mess it up to "fix" it again |
168 | | - date(2021, 2, 24), # dumb schedule again |
169 | | - date(2021, 3, 12) # no more hybrid schedule, just fix the last day of the week |
170 | | - ] |
171 | | - |
172 | | - all_days = [] |
173 | | - day_map = { |
174 | | - 1: 'A', |
175 | | - 0: 'B' |
176 | | - } |
177 | | - |
178 | | - next_friday = thisweek(requested_date)[-1] |
179 | | - elapsed_dates = daterange(first_day, next_friday) |
180 | | - mondays = [d for d in elapsed_dates if d.weekday() == 0 and d not in no_school] |
181 | | - cohort_day = 'maroon' |
182 | | - |
183 | | - for d in elapsed_dates: |
184 | | - if d in no_school: |
185 | | - continue |
186 | | - dow = d.weekday() |
187 | | - if dow in (5, 6): |
188 | | - continue |
189 | | - if dow in (1, 3): |
190 | | - cohort_day = 'maroon' |
191 | | - elif dow in (2, 4): |
192 | | - cohort_day = 'gray' |
193 | | - elif dow == 0: |
194 | | - # Mondays |
195 | | - if d not in mondays: |
196 | | - continue |
197 | | - if mondays.index(d) % 2 == 0: |
198 | | - cohort_day = 'maroon' |
199 | | - else: |
200 | | - cohort_day = 'gray' |
201 | | - |
202 | | - try: |
203 | | - prev_day = [day for day in all_days if day['cohort'] == cohort_day][-1]['day'] |
204 | | - except IndexError: |
205 | | - # We are adding 1 for the first day of each cohort so we "start" with a B (0) day |
206 | | - prev_day = 0 |
207 | | - |
208 | | - if d in special_days: |
209 | | - # Skip a day |
210 | | - all_days.append({ |
211 | | - 'cohort': cohort_day, |
212 | | - 'date': d, |
213 | | - 'day': prev_day + 2}) |
214 | | - else: |
215 | | - all_days.append({ |
216 | | - 'cohort': cohort_day, |
217 | | - 'date': d, |
218 | | - 'day': prev_day + 1}) |
219 | | - |
220 | | - this_week = thisweek(requested_date) |
221 | | - |
222 | | - if week: |
223 | | - week_fmt = [] |
224 | | - for day in this_week: |
225 | | - try: |
226 | | - day_info = list(filter(lambda d: d['date'] == day, all_days))[0] |
227 | | - except IndexError: |
228 | | - week_fmt.append(f"{day.strftime('%a %m/%d')}<br>NO SCHOOL") |
229 | | - else: |
230 | | - week_fmt.append( |
231 | | - f"{day.strftime('%a %m/%d')}<br>{day_info['cohort'].title()} {day_map[day_info['day'] % 2]} day" |
232 | | - ) |
233 | | - return week_fmt |
234 | | - |
235 | | - try: |
236 | | - day_info = list(filter(lambda d: d['date'] == requested_date, all_days))[0] |
237 | | - except IndexError: |
238 | | - # No school |
239 | | - return None |
240 | | - day_info['day'] = day_map[day_info['day'] % 2] |
241 | | - return day_info |
242 | | - |
243 | | -async def handle_daily_emails(app): |
244 | | - """Send out an email at a specified time every weekday""" |
245 | | - today = date.today() |
246 | | - # Saturday/Sunday |
247 | | - if today.weekday() in (5, 6): |
248 | | - today = today - timedelta(days=today.weekday() - 7) # Not actually today, next monday |
249 | | - next_email = datetime.combine(today, dt_time(7, 0, 0)) |
250 | | - |
251 | | - # Past the time today, send "tomorrow" |
252 | | - if next_email < datetime.now(): |
253 | | - next_email = datetime.combine(today + timedelta(days=1), dt_time(7, 0, 0)) |
254 | | - |
255 | | - delta = (next_email - datetime.now()).seconds |
256 | | - await asyncio.sleep(delta) |
257 | | - |
258 | | - today = date.today() # next day after sleeping |
259 | | - if today.weekday() in (5, 6): |
260 | | - # Could be here if the func was called after the time on Friday |
261 | | - return app.add_task(handle_daily_emails) |
262 | | - |
263 | | - today_info = await get_school_week(today, date(2020, 9, 8), week=False) |
264 | | - if today_info is None: |
265 | | - # No school |
266 | | - await asyncio.sleep(1) |
267 | | - return app.add_task(handle_daily_emails) |
268 | | - |
269 | | - async with open_db_connection(app) as conn: |
270 | | - emails = await conn.fetch('SELECT * FROM mailing_list') |
271 | | - |
272 | | - messages = [] |
273 | | - for email in emails: |
274 | | - msg = EmailMessage() |
275 | | - msg['Subject'] = f"GCHS Daily Email Notification for {today.strftime('%m/%d/%Y')}" |
276 | | - msg['From'] = app.config.CUSTOM_EMAIL |
277 | | - msg['To'] = email |
278 | | - body = MIMEText( |
279 | | - f"Today, {today.strftime('%m/%d/%Y')}, is a {today_info['cohort'].title()} {today_info['day']} day.<br><br>" |
280 | | - f"Click <a href=\"http{'s' if not app.config.DEV else ''}://{app.config.DOMAIN}" |
281 | | - f"/schoolweek/unsubscribe/{msg['To']}\">here</a> to unsubscribe.", 'html') |
282 | | - |
283 | | - msg.set_content(body) |
284 | | - messages.append(msg) |
285 | | - |
286 | | - with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp: |
287 | | - try: |
288 | | - smtp.login(app.config.NOREPLY_EMAIL, app.config.EMAIL_APP_PASSWORD) |
289 | | - except smtplib.SMTPAuthenticationError: |
290 | | - # Prevent failure of email scheduling if Google's servers crash (like on 12/14/20) |
291 | | - print(traceback.format_exc()) |
292 | | - else: |
293 | | - for msg in messages: |
294 | | - smtp.send_message(msg) |
295 | | - |
296 | | - # Prevent it from sending twice |
297 | | - await asyncio.sleep(1) |
298 | | - app.add_task(handle_daily_emails) |
0 commit comments