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.
master
Tanner Collin 2 years ago
parent 2d76aaf87d
commit 4f121d0541
  1. 2
      apiserver/apiserver/api/emails/interest.html
  2. 2
      apiserver/apiserver/api/emails/interest.txt
  3. 4
      apiserver/apiserver/api/utils_email.py
  4. 9
      apiserver/apiserver/api/utils_stats.py
  5. 14
      apiserver/apiserver/api/views.py
  6. 24
      webclient/src/InstructorClasses.js

@ -12,7 +12,7 @@
<div>You can find the class on its course page here:<br></div>
<div><a href="[link]">[link]</a><br></div>
<div><br></div>
<div>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.<br></div>
<div>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.<br></div>
<div><br></div>
<div>Spaceport<br></div>
</body>

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

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

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

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

@ -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) {
<InstructorClassEditor input={input} setInput={setInput} error={error} token={token} />
<p>
{progress.map(x => <>{x}<br /></>)}
</p>
<Form.Button loading={loading} error={error.non_field_errors}>
Submit
</Form.Button>

Loading…
Cancel
Save