Auto suggest new class times

This commit is contained in:
Tanner Collin 2022-07-15 03:21:25 +00:00
parent ab1a04d800
commit 41f6969a4a
2 changed files with 80 additions and 1 deletions

View File

@ -11,7 +11,7 @@ from rest_auth.registration.serializers import RegisterSerializer
from rest_auth.serializers import PasswordChangeSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer, LoginSerializer from rest_auth.serializers import PasswordChangeSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer, LoginSerializer
from rest_auth.serializers import UserDetailsSerializer from rest_auth.serializers import UserDetailsSerializer
import re import re
import datetime, time import datetime, time, calendar
from . import models, fields, utils, utils_ldap, utils_auth, utils_stats from . import models, fields, utils, utils_ldap, utils_auth, utils_stats
from .. import settings, secrets from .. import settings, secrets
@ -528,10 +528,80 @@ class CourseDetailSerializer(serializers.ModelSerializer):
sessions = SessionListSerializer(many=True, read_only=True) sessions = SessionListSerializer(many=True, read_only=True)
name = serializers.CharField(max_length=100) name = serializers.CharField(max_length=100)
description = fields.HTMLField(max_length=6000) description = fields.HTMLField(max_length=6000)
suggestion = serializers.SerializerMethodField()
class Meta: class Meta:
model = models.Course model = models.Course
fields = '__all__' fields = '__all__'
def get_suggestion(self, obj):
def iter_dates():
start_of_month = utils.today_alberta_tz().replace(day=1)
for i in range(90):
yield start_of_month + datetime.timedelta(days=i)
def iter_matching_dates(weekday, week_num=False):
week_num_counts = [0] * 13
for date in iter_dates():
if date.weekday() == weekday:
week_num_counts[date.month] += 1
if week_num and week_num_counts[date.month] != week_num:
continue
yield date
def next_date(weekday, week_num=False):
for date in iter_matching_dates(weekday, week_num):
if date > utils.today_alberta_tz():
return date
raise
def course_is_usually_monthly(course):
two_months_ago = utils.today_alberta_tz() - datetime.timedelta(days=61)
recent_sessions = obj.sessions.filter(datetime__gte=two_months_ago)
if recent_sessions.count() < 3:
return True
else:
return False
prev_session = obj.sessions.last()
if obj.id == 273: # monthly clean 10:00 AM 3rd Saturday of each month
date = next_date(calendar.SATURDAY, week_num=3)
time = datetime.time(10, 0)
dt = datetime.datetime.combine(date, time)
dt = utils.TIMEZONE_CALGARY.localize(dt)
cost = 0
max_students = None
elif obj.id == 317: # members' meeting 7:00 PM 3rd Thursday of odd months, Wednesday of even months
if utils.today_alberta_tz().month % 2 == 0:
date = next_date(calendar.WEDNESDAY, week_num=3)
else:
date = next_date(calendar.THURSDAY, week_num=3)
time = datetime.time(19, 0)
dt = datetime.datetime.combine(date, time)
dt = utils.TIMEZONE_CALGARY.localize(dt)
cost = 0
max_students = None
elif prev_session:
prev_session = obj.sessions.last()
dt = prev_session.datetime
if course_is_usually_monthly(obj):
offset_weeks = 4
else:
offset_weeks = 1
dt = dt + datetime.timedelta(weeks=offset_weeks)
five_days_from_now = utils.now_alberta_tz() + datetime.timedelta(days=5)
while dt < five_days_from_now:
dt = dt + datetime.timedelta(weeks=offset_weeks)
cost = prev_session.cost
max_students = prev_session.max_students
else:
return None
return dict(datetime=dt, cost=str(cost), max_students=max_students)
class UserTrainingSerializer(serializers.ModelSerializer): class UserTrainingSerializer(serializers.ModelSerializer):
session = SessionListSerializer() session = SessionListSerializer()

View File

@ -420,6 +420,11 @@ export function InstructorClassList(props) {
).sort((a, b) => a.datetime > b.datetime ? 1 : -1)); ).sort((a, b) => a.datetime > b.datetime ? 1 : -1));
}, [input.datetime]); }, [input.datetime]);
const fillSuggestion = (e) => {
e.preventDefault();
setInput({ ...input, ...course.suggestion });
};
return ( return (
<div> <div>
<Header size='medium'>Instructor Panel</Header> <Header size='medium'>Instructor Panel</Header>
@ -434,6 +439,10 @@ export function InstructorClassList(props) {
<p>Documentation: <a href='https://wiki.protospace.ca/Be_a_Course_Instructor' target='_blank' rel='noopener noreferrer'>https://wiki.protospace.ca/Be_a_Course_Instructor</a></p> <p>Documentation: <a href='https://wiki.protospace.ca/Be_a_Course_Instructor' target='_blank' rel='noopener noreferrer'>https://wiki.protospace.ca/Be_a_Course_Instructor</a></p>
{course.suggestion &&
<p><Button onClick={fillSuggestion}>Suggest</Button> based off previous classes.</p>
}
<InstructorClassEditor input={input} setInput={setInput} error={error} token={token} /> <InstructorClassEditor input={input} setInput={setInput} error={error} token={token} />
<Form.Button loading={loading} error={error.non_field_errors}> <Form.Button loading={loading} error={error.non_field_errors}>