From 4f121d054186cf743bae79af09bb9053396559b5 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Sep 2022 22:01:46 +0000 Subject: [PATCH] Add progress to class creation, fix interest emails If there's 20 people interested in a course, class creation could take quite a while so show progress. Only send emails to active members. Change "Interest +" wording to "interested" in emails. --- apiserver/apiserver/api/emails/interest.html | 2 +- apiserver/apiserver/api/emails/interest.txt | 2 +- apiserver/apiserver/api/utils_email.py | 4 ++++ apiserver/apiserver/api/utils_stats.py | 9 ++++++-- apiserver/apiserver/api/views.py | 14 +++++++++--- webclient/src/InstructorClasses.js | 24 ++++++++++++++++++-- 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/apiserver/apiserver/api/emails/interest.html b/apiserver/apiserver/api/emails/interest.html index 63190e3..6a0af34 100644 --- a/apiserver/apiserver/api/emails/interest.html +++ b/apiserver/apiserver/api/emails/interest.html @@ -12,7 +12,7 @@
You can find the class on its course page here:
[link]

-
Your "interest" in this course is now removed and you won't receive any more notifications about its classes until you press the "Interest +" button again.
+
Your "interest" in this course is now removed and you won't receive any more notifications about its classes until you press the "interested" button again.

Spaceport
diff --git a/apiserver/apiserver/api/emails/interest.txt b/apiserver/apiserver/api/emails/interest.txt index ea54041..ea47ffe 100644 --- a/apiserver/apiserver/api/emails/interest.txt +++ b/apiserver/apiserver/api/emails/interest.txt @@ -6,6 +6,6 @@ You can find the class on its course page here: [link] Your "interest" in this course is now removed and you won't receive any more -notifications about its classes until you press the "Interest +" button again. +notifications about its classes until you press the "interested" button again. Spaceport diff --git a/apiserver/apiserver/api/utils_email.py b/apiserver/apiserver/api/utils_email.py index c9faf99..6110099 100644 --- a/apiserver/apiserver/api/utils_email.py +++ b/apiserver/apiserver/api/utils_email.py @@ -3,6 +3,7 @@ logger = logging.getLogger(__name__) import os import smtplib +import time from datetime import datetime, timedelta from django.core.mail import send_mail, EmailMultiAlternatives @@ -92,4 +93,7 @@ def send_interest_email(interest): html_message=email_html, ) + if not settings.EMAIL_HOST: + time.sleep(0.5) # simulate slowly sending emails when logging to console + logger.info('Sent interest email:\n' + email_text) diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py index 0b66dba..3adf999 100644 --- a/apiserver/apiserver/api/utils_stats.py +++ b/apiserver/apiserver/api/utils_stats.py @@ -195,8 +195,13 @@ def calc_card_scans(): def get_progress(request_id): return cache.get('request-progress-' + request_id, []) -def set_progress(request_id, data): +def set_progress(request_id, data, replace=False): logger.info('Progress - ID: %s | Status: %s', request_id, data) progress = get_progress(request_id) - progress.append(data) + + if replace and len(progress): + progress[-1] = data + else: + progress.append(data) + cache.set('request-progress-' + request_id, progress) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 23d5a58..607dee6 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -279,6 +279,7 @@ class SessionViewSet(Base, List, Retrieve, Create, Update): return serializers.SessionSerializer def perform_create(self, serializer): + data = self.request.data session = serializer.save(instructor=self.request.user) # ensure session datetime is at least 1 day in the future @@ -287,15 +288,22 @@ class SessionViewSet(Base, List, Retrieve, Create, Update): logging.info('Session is in the past or too soon, not sending interest emails.') return - interests = models.Interest.objects.filter(course=session.course, satisfied_by__isnull=True) + interests = models.Interest.objects.filter( + course=session.course, + satisfied_by__isnull=True, + user__member__paused_date__isnull=True + ) + + for num, interest in enumerate(interests): + msg = 'Sending email {} / {}...'.format(num+1, len(interests)) + if data['request_id']: utils_stats.set_progress(data['request_id'], msg, replace=True) - for interest in interests: try: utils_email.send_interest_email(interest) except BaseException as e: msg = 'Problem sending interest email: ' + str(e) logger.exception(msg) - alert_tanner(msg) + utils.alert_tanner(msg) num_satisfied = interests.update(satisfied_by=session) logging.info('Satisfied %s interests.', num_satisfied) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index aed1b2b..84a1bcd 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -6,7 +6,7 @@ import 'react-datetime/css/react-datetime.css'; import moment from 'moment-timezone'; import './light.css'; import { Button, Checkbox, Form, Grid, Header, Icon, Label, Message, Table } from 'semantic-ui-react'; -import { requester } from './utils.js'; +import { requester, randomString } from './utils.js'; import { MembersDropdown } from './Members.js'; class AttendanceSheet extends React.Component { @@ -378,6 +378,7 @@ export function InstructorClassList(props) { const [open, setOpen] = useState(false); const [input, setInput] = useState({ max_students: null }); const [error, setError] = useState(false); + const [progress, setProgress] = useState([]); const [loading, setLoading] = useState(false); const [success, setSuccess] = useState(false); const [classes, setClasses] = useState([]); @@ -387,9 +388,23 @@ export function InstructorClassList(props) { if (loading) return; setLoading(true); setSuccess(false); - const data = { ...input, course: course.id }; + + const request_id = randomString(); + const getStatus = () => { + requester('/stats/progress/?request_id='+request_id, 'GET') + .then(res => { + setProgress(res); + }) + .catch(err => { + console.log(err); + }); + }; + const interval = setInterval(getStatus, 500); + + const data = { ...input, course: course.id, request_id: request_id }; requester('/sessions/', 'POST', token, data) .then(res => { + clearInterval(interval); setSuccess(res.id); setInput({ max_students: null }); setLoading(false); @@ -398,6 +413,7 @@ export function InstructorClassList(props) { setCourse({ ...course, sessions: [ res, ...course.sessions ] }); }) .catch(err => { + clearInterval(interval); setLoading(false); console.log(err); setError(err.data); @@ -445,6 +461,10 @@ export function InstructorClassList(props) { +

+ {progress.map(x => <>{x}
)} +

+ Submit