diff --git a/intranet/apps/eighth/views/signup.py b/intranet/apps/eighth/views/signup.py index 90f7815cccd..6856a31921d 100644 --- a/intranet/apps/eighth/views/signup.py +++ b/intranet/apps/eighth/views/signup.py @@ -1,3 +1,4 @@ +import datetime import logging import time @@ -9,6 +10,7 @@ from django.contrib.auth.decorators import login_required from django.db import transaction from django.shortcuts import get_object_or_404, redirect, render +from django.utils import timezone from django.views.decorators.http import require_POST from ....utils.helpers import is_entirely_digit @@ -130,6 +132,8 @@ def eighth_signup_view(request, block_id=None): waitlists = EighthWaitlist.objects.filter(user=user).select_related("scheduled_activity__block", "scheduled_activity__activity") block_waitlist_map = {w.scheduled_activity.block.id: w.scheduled_activity for w in waitlists} + today = timezone.localdate() + for b in surrounding_blocks: info = { "id": b.id, @@ -139,6 +143,7 @@ def eighth_signup_view(request, block_id=None): "current_signup": getattr(block_signup_map.get(b.id, {}), "activity", None), "current_signup_cancelled": getattr(block_signup_map.get(b.id, {}), "cancelled", False), "current_waitlist": getattr(block_waitlist_map.get(b.id, {}), "activity", None), + "within_few_days": b.date >= today and b.date <= today + datetime.timedelta(days=2), "locked": b.locked, } diff --git a/intranet/apps/users/models.py b/intranet/apps/users/models.py index 3725576bef5..f5abf0501ca 100644 --- a/intranet/apps/users/models.py +++ b/intranet/apps/users/models.py @@ -1,7 +1,7 @@ # pylint: disable=too-many-lines; Allow more than 1000 lines import logging from base64 import b64encode -from datetime import datetime +from datetime import datetime, timedelta from typing import Collection, Optional, Union from dateutil.relativedelta import relativedelta @@ -809,6 +809,32 @@ def signed_up_today(self): return not EighthBlock.objects.get_blocks_today().exclude(eighthscheduledactivity__eighthsignup_set__user=self).exists() + def signed_up_next_few_days(self, *, num_days: int = 3) -> bool: + """If the user is a student, returns whether they are signed up for an activity during + all eighth period blocks in the next ``num_days`` days. Otherwise, returns ``True``. + + Today is counted as a day, so ``signed_up_few_next_day(num_days=1)`` is equivalent to + ``signed_up_today()``. + + Args: + num_days: The number of days (including today) on which to search for blocks during + which the user is signed up. + + Returns: + If the user is a student, returns whether they are signed up for an activity during + all eighth period blocks in the next ``num_days`` days. Otherwise, returns ``True``. + + """ + if not self.is_student: + return True + + today = timezone.localdate() + end_date = today + timedelta(days=num_days - 1) + + return ( + not EighthBlock.objects.filter(date__gte=today, date__lte=end_date).exclude(eighthscheduledactivity__eighthsignup_set__user=self).exists() + ) + def absence_count(self): """Return the user's absence count. diff --git a/intranet/apps/users/tests.py b/intranet/apps/users/tests.py index 335d555b8aa..be22d89a41d 100644 --- a/intranet/apps/users/tests.py +++ b/intranet/apps/users/tests.py @@ -385,6 +385,78 @@ def test_signed_up_today(self): block.delete() act.delete() + def test_signed_up_next_few_days(self): + user = self.login() + user.user_type = "student" + user.save() + + EighthBlock.objects.all().delete() + + act = EighthActivity.objects.create(name="Test activity 1") + + today = timezone.localdate() + + self.assertTrue(user.signed_up_next_few_days()) + self.assertTrue(user.signed_up_next_few_days(num_days=3)) + self.assertTrue(user.signed_up_next_few_days(num_days=2)) + self.assertTrue(user.signed_up_next_few_days(num_days=1)) + + for d in [3, -1]: + EighthScheduledActivity.objects.create( + activity=act, block=EighthBlock.objects.create(date=today + datetime.timedelta(days=d), block_letter="A") + ) + + self.assertTrue(user.signed_up_next_few_days()) + self.assertTrue(user.signed_up_next_few_days(num_days=3)) + self.assertTrue(user.signed_up_next_few_days(num_days=2)) + self.assertTrue(user.signed_up_next_few_days(num_days=1)) + + for d in [2, 1, 0]: + schact_a = EighthScheduledActivity.objects.create( + activity=act, block=EighthBlock.objects.create(date=today + datetime.timedelta(days=d), block_letter="A") + ) + + self.assertFalse(user.signed_up_next_few_days()) + for j in range(d + 1, 5): + self.assertFalse(user.signed_up_next_few_days(num_days=j)) + for j in range(1, d + 1): + self.assertTrue(user.signed_up_next_few_days(num_days=j)) + + EighthSignup.objects.create(user=user, scheduled_activity=schact_a) + + self.assertTrue(user.signed_up_next_few_days()) + self.assertTrue(user.signed_up_next_few_days(num_days=3)) + self.assertTrue(user.signed_up_next_few_days(num_days=2)) + self.assertTrue(user.signed_up_next_few_days(num_days=1)) + + schact_b = EighthScheduledActivity.objects.create( + activity=act, block=EighthBlock.objects.create(date=today + datetime.timedelta(days=d), block_letter="B") + ) + + self.assertFalse(user.signed_up_next_few_days()) + for j in range(d + 1, 5): + self.assertFalse(user.signed_up_next_few_days(num_days=j)) + for j in range(1, d + 1): + self.assertTrue(user.signed_up_next_few_days(num_days=j)) + + EighthSignup.objects.create(user=user, scheduled_activity=schact_b) + + self.assertTrue(user.signed_up_next_few_days()) + self.assertTrue(user.signed_up_next_few_days(num_days=3)) + self.assertTrue(user.signed_up_next_few_days(num_days=2)) + self.assertTrue(user.signed_up_next_few_days(num_days=1)) + + EighthBlock.objects.all().delete() + + # Teachers are never "not signed up" + user.user_type = "teacher" + user.save() + EighthScheduledActivity.objects.create(activity=act, block=EighthBlock.objects.create(date=today, block_letter="A")) + self.assertTrue(user.signed_up_next_few_days()) + self.assertTrue(user.signed_up_next_few_days(num_days=3)) + self.assertTrue(user.signed_up_next_few_days(num_days=2)) + self.assertTrue(user.signed_up_next_few_days(num_days=1)) + def test_tj_email_non_tj_email(self): user = self.login() user.primary_email = None diff --git a/intranet/static/js/eighth/signup.js b/intranet/static/js/eighth/signup.js index f65aa92dc48..6576e06e252 100644 --- a/intranet/static/js/eighth/signup.js +++ b/intranet/static/js/eighth/signup.js @@ -225,6 +225,25 @@ $(function() { var activity = activityModels.get(aid); if (response.indexOf("added to waitlist") === -1) { + // If: + // - The signup succeeded + // - The user is signing themselves up + // - They have less then 40 minutes until signups close + // - They are not going to be redirected when they finish signing up + // - They are not already signed up for an activity ('.block.active-block .selected-activity .no-activity-selected' exists) + // Then ask them to sign up sooner. + if(response.toLowerCase().indexOf("successfully signed up") != -1 + && window.isSelfSignup + && window.signupTime && window.signupTime - new Date() < 40 * 60 * 1000 + && !window.next_url + && $(".block.active-block .selected-activity .no-activity-selected").length) { + Messenger().info({ + message: 'In the future, please sign up for eighth period activities sooner.', + hideAfter: 5, + showCloseButton: false + }); + } + if (!activity.attributes.both_blocks) { $(".current-day .both-blocks .selected-activity").html("\nNo activity selected").attr("title", ""); $(".current-day .both-blocks").removeClass("both-blocks"); @@ -263,6 +282,21 @@ $(function() { $(".active-block.cancelled").removeClass("cancelled"); + if (!activity.attributes.both_blocks) { + $(".current-day .blocks a[data-bid='" + bid + "'] .fa-exclamation-circle").css("display", "none"); + $(".current-day .blocks a[data-bid!='" + bid + "'] .fa-exclamation-circle").each(function() { + if($(this).closest(".block").find(".selected-activity .no-activity-selected").length) { + $(this).css("display", ""); + } + else { + $(this).css("display", "none"); + } + }); + } else { + $(".current-day .block-letter .fa-exclamation-circle").css("display", "none"); + } + + var selectedActivity = activityModels.filter(function(a) { return a.attributes.selected === true }); diff --git a/intranet/templates/eighth/multi_signup.html b/intranet/templates/eighth/multi_signup.html index 6cc1653a8fd..c1e570216b3 100644 --- a/intranet/templates/eighth/multi_signup.html +++ b/intranet/templates/eighth/multi_signup.html @@ -58,6 +58,8 @@ window.isEighthAdmin = {% if request.user.is_eighth_admin %}true{% else %}false{% endif %}; window.waitlistEnabled = {% if waitlist_enabled %}true{% else %}false{% endif %}; window.blockIsToday = {% if active_block.is_today %}true{% else %}false{% endif %}; + window.signupTime = new Date({{ active_block.date|date:'Y,m-1,j' }},{{ active_block.signup_time|time:'G,i' }}); + window.isSelfSignup = {% if request.user == user %}true{% else %}false{% endif %}; var pn = location.pathname.substring(7); window.isDefaultPage = (pn == "" || pn == "/" || pn == "/signup" || pn == "/signup/"); diff --git a/intranet/templates/eighth/profile_signup.html b/intranet/templates/eighth/profile_signup.html index 16a14544aa7..f8d7fc766b5 100644 --- a/intranet/templates/eighth/profile_signup.html +++ b/intranet/templates/eighth/profile_signup.html @@ -58,6 +58,8 @@ window.isEighthAdmin = {% if request.user.is_eighth_admin %}true{% else %}false{% endif %}; window.waitlistEnabled = {% if waitlist_enabled %}true{% else %}false{% endif %}; window.blockIsToday = {% if active_block.is_today %}true{% else %}false{% endif %}; + window.signupTime = new Date({{ active_block.date|date:'Y,m-1,j' }},{{ active_block.signup_time|time:'G,i' }}); + window.isSelfSignup = {% if request.user == user %}true{% else %}false{% endif %}; var pn = location.pathname.substring(7); window.isDefaultPage = (pn == "" || pn == "/" || pn == "/signup" || pn == "/signup/"); diff --git a/intranet/templates/eighth/signup.html b/intranet/templates/eighth/signup.html index be4c3f0a1a0..6b8a383136c 100644 --- a/intranet/templates/eighth/signup.html +++ b/intranet/templates/eighth/signup.html @@ -3,6 +3,7 @@ {% load math %} {% load strings %} {% load pipeline %} +{% load tz %} {% comment %} @@ -67,6 +68,8 @@ window.isEighthAdmin = {% if request.user.is_eighth_admin %}true{% else %}false{% endif %}; window.waitlistEnabled = {% if waitlist_enabled %}true{% else %}false{% endif %}; window.blockIsToday = {% if active_block.is_today %}true{% else %}false{% endif %}; + window.signupTime = new Date({{ active_block.date|date:'Y,m-1,j' }},{{ active_block.signup_time|time:'G,i' }}); + window.isSelfSignup = {% if request.user == user %}true{% else %}false{% endif %}; window.currentWaitlist = false; var pn = location.pathname.substring(7); window.isDefaultPage = (pn == "" || pn == "/" || pn == "/signup" || pn == "/signup/"); @@ -378,8 +381,9 @@

{% for b in day.blocks %}
- + {{ b.block_letter }} + {% if b.locked %} diff --git a/intranet/templates/nav.html b/intranet/templates/nav.html index b11834f686a..bee9418112c 100644 --- a/intranet/templates/nav.html +++ b/intranet/templates/nav.html @@ -13,7 +13,7 @@ @@ -31,7 +31,7 @@