Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ package-lock.json
# Virtual environments
venv/
.venv/

4 changes: 3 additions & 1 deletion docs/source/developing/usernames.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ There are more usernames generated by default than the ones in this table, but t
| 2028student1 | student | 2028 | FALSE |
| 2028student | student | 2028 | FALSE |

The usernames in the table above depend on when they are generated - if the current school year has started (in August), then 1 year will be appended to the current year for graduation year calculation.

The usernames in the table above depend on when they are generated - if the current school year has started (in August), then 1 year will be appended to the current year for graduation year calculation.

12 changes: 12 additions & 0 deletions intranet/apps/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ def gen_schedule(user, num_blocks: int = 6, surrounding_blocks: Iterable[EighthB
# don't duplicate this info; already caught
current_signup = current_signup.replace(" (Cancelled)", "")

# check if attendance open, if so, will display attendance button
attendance_open = False
if current_sched_act:
from ..eighth.views.attendance import check_attendance_open # noqa: PLC0415

attendance_open = check_attendance_open(current_sched_act) is None
sch_act_id = None
if attendance_open:
sch_act_id = current_sched_act.id

info = {
"id": b.id,
"block": b,
Expand All @@ -108,6 +118,8 @@ def gen_schedule(user, num_blocks: int = 6, surrounding_blocks: Iterable[EighthB
"signup_time": b.signup_time,
"signup_time_future": b.signup_time_future(),
"rooms": rooms,
"attendance_open": attendance_open,
"sch_act_id": sch_act_id,
}
schedule.append(info)

Expand Down
57 changes: 57 additions & 0 deletions intranet/apps/eighth/migrations/0073_eighth_attendance_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Generated by Django 3.2.25 on 2025-04-29 15:28

from django.db import migrations, models
import intranet.apps.eighth.models

def generate_attendance_codes(apps, schema_editor):
EighthScheduledActivity = apps.get_model('eighth', 'EighthScheduledActivity')
HistoricalEighthScheduledActivity = apps.get_model('eighth', 'HistoricalEighthScheduledActivity')

for activity in EighthScheduledActivity.objects.all():
activity.attendance_code = intranet.apps.eighth.models.random_code()
activity.save(update_fields=['attendance_code'])

for historical_activity in HistoricalEighthScheduledActivity.objects.all():
historical_activity.attendance_code = intranet.apps.eighth.models.random_code()
historical_activity.save(update_fields=['attendance_code'])


class Migration(migrations.Migration):

dependencies = [
('eighth', '0072_alter_eighthscheduledactivity_waitlist'),
]

operations = [
migrations.AddField(
model_name='eighthscheduledactivity',
name='attendance_code',
field=models.CharField(default=intranet.apps.eighth.models.random_code, max_length=6),
),
migrations.AddField(
model_name='historicaleighthscheduledactivity',
name='attendance_code',
field=models.CharField(default=intranet.apps.eighth.models.random_code, max_length=6),
),
migrations.RunPython(generate_attendance_codes, reverse_code=migrations.RunPython.noop),
migrations.AddField(
model_name='eighthscheduledactivity',
name='code_mode',
field=models.IntegerField(choices=[(0, 'Auto'), (1, 'Open'), (2, 'Closed')], default=0),
),
migrations.AddField(
model_name='historicaleighthscheduledactivity',
name='code_mode',
field=models.IntegerField(choices=[(0, 'Auto'), (1, 'Open'), (2, 'Closed')], default=0),
),
migrations.AddField(
model_name='eighthsignup',
name='attendance_marked',
field=models.BooleanField(blank=True, default=False),
),
migrations.AddField(
model_name='historicaleighthsignup',
name='attendance_marked',
field=models.BooleanField(blank=True, default=False),
),
]
31 changes: 30 additions & 1 deletion intranet/apps/eighth/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# pylint: disable=too-many-lines; Allow more than 1000 lines
import datetime
import logging
import secrets
import string
from collections.abc import Collection, Iterable, Sequence
from typing import Optional
Expand Down Expand Up @@ -794,6 +795,10 @@ def for_sponsor(self, sponsor: EighthSponsor, include_cancelled: bool = False) -
return sched_acts


def random_code():
return "".join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(6))


class EighthScheduledActivity(AbstractBaseEighthModel):
r"""Represents the relationship between an activity and a block in which it has been scheduled.
Attributes:
Expand Down Expand Up @@ -822,6 +827,14 @@ class EighthScheduledActivity(AbstractBaseEighthModel):
attendance_taken
Whether the :class:`EighthSponsor` for the scheduled
:class:`EighthActivity` has taken attendance yet
attendance_code
Random 6-character code of digits and uppercase letters to check attendance
Distinct for each 8th activity-block
code_mode
Whether the activity is automatically enabling/disabling the attendance code based on the daily schedule
Is an integer:
0 = Automatic; 1 = Open; 2 = Closed
default = 0
special
Whether this scheduled instance of the activity is special. If
not set, falls back on the EighthActivity's special setting.
Expand All @@ -842,6 +855,14 @@ class EighthScheduledActivity(AbstractBaseEighthModel):
blank=True,
)

attendance_code = models.CharField(max_length=6, default=random_code)
mode_choices = [
(0, "Auto"),
(1, "Open"),
(2, "Closed"),
]
code_mode = models.IntegerField(choices=mode_choices, default=0)

admin_comments = models.CharField(max_length=1000, blank=True)
title = models.CharField(max_length=1000, blank=True)
comments = models.CharField(max_length=1000, blank=True)
Expand Down Expand Up @@ -1195,6 +1216,10 @@ def set_sticky_students(self, users: "Sequence[AbstractBaseUser]") -> None:
bcc=True,
)

def set_code_mode(self, mode):
self.code_mode = mode
self.save(update_fields=["code_mode"])

@transaction.atomic # This MUST be run in a transaction. Do NOT remove this decorator.
def add_user(
self,
Expand Down Expand Up @@ -1703,6 +1728,8 @@ class EighthSignup(AbstractBaseEighthModel):
Whether the pass was accepted
was_absent
Whether the student was absent.
attendance_marked
Whether the student has been marked / filled out attendance.
absence_acknowledged
Whether the student has dismissed the absence notification.
absence_emailed
Expand All @@ -1725,6 +1752,7 @@ class EighthSignup(AbstractBaseEighthModel):

pass_accepted = models.BooleanField(default=False, blank=True)
was_absent = models.BooleanField(default=False, blank=True)
attendance_marked = models.BooleanField(default=False, blank=True)
absence_acknowledged = models.BooleanField(default=False, blank=True)
absence_emailed = models.BooleanField(default=False, blank=True)

Expand Down Expand Up @@ -1814,7 +1842,8 @@ def accept_pass(self):
"""Accepts an eighth period pass for the EighthSignup object."""
self.was_absent = False
self.pass_accepted = True
self.save(update_fields=["was_absent", "pass_accepted"])
self.attendance_marked = True
self.save(update_fields=["was_absent", "pass_accepted", "attendance_marked"])

def reject_pass(self):
"""Rejects an eighth period pass for the EighthSignup object."""
Expand Down
3 changes: 3 additions & 0 deletions intranet/apps/eighth/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from rest_framework.reverse import reverse

from .models import EighthActivity, EighthBlock, EighthScheduledActivity, EighthSignup, EighthSponsor
from .views.attendance import check_attendance_open

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -139,6 +140,8 @@ def process_scheduled_activity(
"name_with_flags_for_user": name_with_flags_for_user,
"description": activity.description,
"cancelled": scheduled_activity.cancelled,
"attendance_open": check_attendance_open(scheduled_activity) is None,
"attendance_url": reverse("student_attendance", args=[scheduled_activity.id]),
"favorited": activity.id in favorited_activities,
"subscribed_to": activity.id in subscribed_activities,
"subscriptions_enabled": subscriptions_enabled,
Expand Down
1 change: 1 addition & 0 deletions intranet/apps/eighth/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
path("/absences", attendance.eighth_absences_view, name="eighth_absences"),
path("/absences/<int:user_id>", attendance.eighth_absences_view, name="eighth_absences"),
path("/glance", signup.eighth_location, name="eighth_location"),
re_path(r"^/student_attendance/(?P<sch_act_id>\d+)(?:/(?P<code>[A-Za-z0-9]+))?$", attendance.student_attendance_view, name="student_attendance"),
# Teachers
path("/attendance", attendance.teacher_choose_scheduled_activity_view, name="eighth_attendance_choose_scheduled_activity"),
path("/attendance/<int:scheduled_activity_id>", attendance.take_attendance_view, name="eighth_take_attendance"),
Expand Down
Loading
Loading