Skip to content

Commit

Permalink
feat(eighth): check for users in sticky activities during group signups
Browse files Browse the repository at this point in the history
  • Loading branch information
alanzhu0 committed Sep 20, 2023
1 parent b7b2541 commit 83b5ce0
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 18 deletions.
7 changes: 4 additions & 3 deletions intranet/apps/eighth/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def eighth_admin_assign_hybrid_sticky_blocks(fmtdate: str) -> None:


@shared_task
def eighth_admin_signup_group_task(*, user_id: int, group_id: int, schact_id: int) -> None:
def eighth_admin_signup_group_task(*, user_id: int, group_id: int, schact_id: int, skip_users: set) -> None:
"""Sign all users in a specific group up for a specific scheduled activity
(in the background), sending an email to the user who requested the operation when
it is done.
Expand All @@ -194,7 +194,8 @@ def eighth_admin_signup_group_task(*, user_id: int, group_id: int, schact_id: in
user_id: The ID of the user who requested that this operation be performed.
group_id: The ID of the group to sign up for the activity.
schact_id:.The ID of the EighthScheduledActivity to add the group members to.
skip_users: A list of users that should not be signed up for the activity,
usually because they are stickied into another activity.
"""
# Circular dependency
from .views.admin.groups import eighth_admin_perform_group_signup # pylint: disable=import-outside-toplevel
Expand All @@ -208,7 +209,7 @@ def eighth_admin_signup_group_task(*, user_id: int, group_id: int, schact_id: in
}

try:
eighth_admin_perform_group_signup(group_id=group_id, schact_id=schact_id, request=None)
eighth_admin_perform_group_signup(group_id=group_id, schact_id=schact_id, request=None, skip_users=skip_users)
except Exception:
email_send(
"eighth/emails/group_signup_error.txt",
Expand Down
71 changes: 62 additions & 9 deletions intranet/apps/eighth/views/admin/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse

Expand All @@ -21,7 +22,7 @@
from ...forms.admin.activities import ActivitySelectionForm, ScheduledActivityMultiSelectForm
from ...forms.admin.blocks import BlockSelectionForm
from ...forms.admin.groups import GroupForm, QuickGroupForm, UploadGroupForm
from ...models import EighthActivity, EighthBlock, EighthScheduledActivity
from ...models import EighthActivity, EighthBlock, EighthScheduledActivity, EighthSignup
from ...tasks import eighth_admin_signup_group_task
from ...utils import get_start_date

Expand Down Expand Up @@ -441,28 +442,52 @@ def eighth_admin_signup_group_action(request, group_id, schact_id):

users = group.user_set.all()

# Sticky check
sticky_users_and_activities = {}
for user in users:
sticky_activity_signup = EighthSignup.objects.filter(user=user, scheduled_activity__block=scheduled_activity.block).filter(
Q(scheduled_activity__activity__sticky=True) | Q(scheduled_activity__sticky=True)
)
if sticky_activity_signup.exists():
sticky_users_and_activities[user] = sticky_activity_signup[0].scheduled_activity
sticky_users_and_activities = dict(sorted(sticky_users_and_activities.items(), key=lambda x: x[0].last_name))

if not users.exists():
messages.info(request, "The group you have selected has no members.")
return redirect("eighth_admin_dashboard")

if "confirm" in request.POST:
skip_users = set(sticky_users_and_activities.keys())
for user in request.POST.getlist("remove_from_sticky"):
try:
user = User.objects.get(username=user)
skip_users.remove(user)
except (User.DoesNotExist, ValueError):
messages.warning(request, f"Error signing up user {user} for activity.")

if request.POST.get("run_in_background"):
eighth_admin_signup_group_task.delay(user_id=request.user.id, group_id=group_id, schact_id=schact_id)
eighth_admin_signup_group_task.delay(user_id=request.user.id, group_id=group_id, schact_id=schact_id, skip_users=skip_users)
messages.success(request, "Group members are being signed up in the background.")
return redirect("eighth_admin_dashboard")
else:
eighth_admin_perform_group_signup(group_id=group_id, schact_id=schact_id, request=request)
eighth_admin_perform_group_signup(group_id=group_id, schact_id=schact_id, request=request, skip_users=skip_users)
messages.success(request, "Successfully signed up group for activity.")
return redirect("eighth_admin_dashboard")

return render(
request,
"eighth/admin/sign_up_group.html",
{"admin_page_title": "Confirm Group Signup", "scheduled_activity": scheduled_activity, "group": group, "users_num": users.count()},
{
"admin_page_title": "Confirm Group Signup",
"scheduled_activity": scheduled_activity,
"group": group,
"users_num": users.count(),
"sticky_users_and_activities": sticky_users_and_activities,
},
)


def eighth_admin_perform_group_signup(*, group_id: int, schact_id: int, request: Optional[http.HttpRequest]):
def eighth_admin_perform_group_signup(*, group_id: int, schact_id: int, request: Optional[http.HttpRequest], skip_users: set):
"""Performs sign up of all users in a specific group up for a
specific scheduled activity.
Expand All @@ -471,15 +496,17 @@ def eighth_admin_perform_group_signup(*, group_id: int, schact_id: int, request:
schact_id: The ID of the EighthScheduledActivity all the users in the group
should be signed up for.
request: If possible, the request object associated with the operation.
skip_users: A list of users that should not be signed up for the activity,
usually because they are stickied into another activity.
"""

# We assume these exist
scheduled_activity = EighthScheduledActivity.objects.get(id=schact_id)
group = Group.objects.get(id=group_id)

for user in group.user_set.all():
scheduled_activity.add_user(user, request=request, force=True, no_after_deadline=True)
if user not in skip_users:
scheduled_activity.add_user(user, request=request, force=True, no_after_deadline=True)


class EighthAdminDistributeGroupWizard(SessionWizardView):
Expand Down Expand Up @@ -592,11 +619,26 @@ def eighth_admin_distribute_action(request):
userids = request.POST.getlist(item)
activity_user_map[schact] = userids

sticky_users_and_activities = {}
for schact, userids in activity_user_map.items():
for uid in userids:
user = get_user_model().objects.get(id=uid)
sticky_activity_signup = EighthSignup.objects.filter(user=user, scheduled_activity__block=schact.block).filter(
Q(scheduled_activity__activity__sticky=True) | Q(scheduled_activity__sticky=True)
)
if sticky_activity_signup.exists():
sticky_users_and_activities[uid] = sticky_activity_signup[0].scheduled_activity

skip_users = set(sticky_users_and_activities.keys())
for uid in request.POST.getlist("remove_from_sticky"):
skip_users.remove(uid)

changes = 0
for schact, userids in activity_user_map.items():
for uid in userids:
changes += 1
schact.add_user(get_user_model().objects.get(id=int(uid)), request=request, force=True, no_after_deadline=True)
if uid not in skip_users:
changes += 1
schact.add_user(get_user_model().objects.get(id=int(uid)), request=request, force=True, no_after_deadline=True)

messages.success(request, "Successfully completed {} activity signups.".format(changes))

Expand Down Expand Up @@ -639,6 +681,16 @@ def eighth_admin_distribute_action(request):
# Sort by last name
users = sorted(list(users), key=lambda x: x.last_name)

# Sticky check
sticky_users_and_activities = {}
for user in users:
sticky_activity_signup = EighthSignup.objects.filter(
user=user, scheduled_activity__block=block if users_type == "unsigned" else schacts[0].block
).filter(Q(scheduled_activity__activity__sticky=True) | Q(scheduled_activity__sticky=True))
if sticky_activity_signup.exists():
sticky_users_and_activities[user] = sticky_activity_signup[0].scheduled_activity
sticky_users_and_activities = dict(sorted(sticky_users_and_activities.items(), key=lambda x: x[0].last_name))

context = {
"admin_page_title": "Distribute Group Members Across Activities",
"users_type": users_type,
Expand All @@ -647,6 +699,7 @@ def eighth_admin_distribute_action(request):
"schacts": schacts,
"users": users,
"show_selection": True,
"sticky_users_and_activities": sticky_users_and_activities,
}

return render(request, "eighth/admin/distribute_group.html", context)
Expand Down
10 changes: 10 additions & 0 deletions intranet/static/css/eighth.admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ table.choose-fields td {
}
}

.sticky-check-table {
th {
padding: 0 20px 0 0;
}

td {
padding: 0 30px 0 0 !important;
}
}

button,
a.button,
input[type="button"],
Expand Down
55 changes: 52 additions & 3 deletions intranet/templates/eighth/admin/distribute_group.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,68 @@
{% block admin_main %}
{% if show_selection %}
<form action="{% url 'eighth_admin_distribute_action' %}" method="post">

{% csrf_token %}
{% if users_type == "unsigned" %}
<p>{{ users|length }} user{{ users|length|pluralize }} have not signed up for any activities on {{ eighthblock }}.</p>
{% elif users_type == "group" %}
<b>Group:</b> {{ group }} - {{ users|length }} member{{ users|length|pluralize }}<br>
<a href="{% url 'eighth_admin_edit_group' group.id %}" class="button">Modify Group</a> &nbsp;
{% endif %}
<button onclick="distribute(); return false">Evenly Distribute</button>
<br>

{% if sticky_users_and_activities %}
<br>
<hr>
<i class="fas fa-exclamation-triangle" style="color:red"></i>
There are {{ sticky_users_and_activities|length }} users signed up for sticky activities in the selected block.
<br>
Select whether you want to remove them from their sticky activity and sign them up for this activity instead.
<br>
Students not selected here will remain in their sticky activity regardless of what is indicated in the
activity signup registration table.
<br><br>
<table class="sticky-check-table">
<thead>
<tr>
<th>Student</th>
<th>Student ID</th>
<th>Email</th>
<th>Grade</ht>
<th>Sticky Activity</th>
<th>
<input type="checkbox" id="select_all_sticky" onclick="$('.sticky-check-table input[type=checkbox]').prop('checked', this.checked);">
Remove From Sticky Activity
</th>
</tr>
</thead>
<tbody>
{% for user, sticky_activity in sticky_users_and_activities.items %}
<tr>
<td>
<a href="{% url 'user_profile' user.id %}">{{ user.last_name }}, {{ user.first_name }}</a>
</td>
<td>{{ user.student_id }}</td>
<td>
<a href="mailto:{{ user.tj_email }}">{{ user.tj_email }}</a>
</td>
<td>{{ user.grade_number }}</td>
<td>
<a href="{% url 'eighth_roster' sticky_activity.id %}">
{{ sticky_activity.activity.id }}: {{ sticky_activity.activity.name }}
</a>
</td>
<td><input type="checkbox" name="remove_from_sticky" value="{{ user.id }}"></td>
</tr>
{% endfor %}
</tbody>
</table>
<hr>
<br>
{% endif %}

<input type="hidden" name="users" value="true">
<input type="submit" value="Register Users" onclick="if(confirm('Are you sure you want to sign these users up for these activities?')){showWaitScreen();return true;}else return false;">
{% csrf_token %}
<button onclick="distribute(); return false">Evenly Distribute</button>
<br>
Click on column titles to sort.
<table data-sortable class="sortable distribute-group-grid fancy-table">
Expand Down
51 changes: 48 additions & 3 deletions intranet/templates/eighth/admin/sign_up_group.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,61 @@
{% endcomment %}
</form>
{% else %}
<p>Are you sure you want to sign up <b>{{ users_num }} users</b> in group:</p>
<p><b>{{ group }}</b></p>
for:<br><br>
<p>Are you sure you want to sign up <b>{{ users_num }} users</b> in group <b>{{ group }}</b> for:<br>
{% if hybrid %}
<p><b>{{ scheduled_activity_virtual }}</b> (Virtual) and <b>{{ scheduled_activity_person }}</b> (In-Person)</p>
{% else %}
<p><b>{{ scheduled_activity }}</b></p>
{% endif %}
<br>

<form action="" method="POST">
{% csrf_token %}

{% if sticky_users_and_activities %}
<i class="fas fa-exclamation-triangle" style="color:red"></i>
There are {{ sticky_users_and_activities|length }} users signed up for sticky activities in the selected block.
<br>
Select whether you want to remove them from their sticky activity and sign them up for this activity instead.
<br><br>
<table class="sticky-check-table">
<thead>
<tr>
<th>Student</th>
<th>Student ID</th>
<th>Email</th>
<th>Grade</ht>
<th>Sticky Activity</th>
<th>
<input type="checkbox" id="select_all_sticky" onclick="$('.sticky-check-table input[type=checkbox]').prop('checked', this.checked);">
Remove From Sticky Activity
</th>
</tr>
</thead>
<tbody>
{% for user, sticky_activity in sticky_users_and_activities.items %}
<tr>
<td>
<a href="{% url 'user_profile' user.id %}">{{ user.last_name }}, {{ user.first_name }}</a>
</td>
<td>{{ user.student_id }}</td>
<td>
<a href="mailto:{{ user.tj_email }}">{{ user.tj_email }}</a>
</td>
<td>{{ user.grade_number }}</td>
<td>
<a href="{% url 'eighth_roster' sticky_activity.id %}">
{{ sticky_activity.activity.id }}: {{ sticky_activity.activity.name }}
</a>
</td>
<td><input type="checkbox" name="remove_from_sticky" value="{{ user.username }}"></td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
{% endif %}

<input type="hidden" name="confirm" value="true">
<input type="checkbox" name="run_in_background" id="run_in_background" checked>
<label for="run_in_background">Run this process in the background and email me when it's done (make sure to check your email address under Preferences)</label><br>
Expand Down

0 comments on commit 83b5ce0

Please sign in to comment.