Skip to content

Commit 9660664

Browse files
Merge pull request #18 from FSU-ACM/release
Codebase cleanup + new contest admin tools
2 parents a2c4bab + e7ac4a7 commit 9660664

File tree

13 files changed

+320
-140
lines changed

13 files changed

+320
-140
lines changed

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
FROM python:3.10-slim
22
LABEL maintainer="ACM at FSU <contact@fsu.acm.org>"
33

4-
ENV PYTHONUNBUFFERED 1
5-
ENV PYTHONDONTWRITEBYTECODE 1
4+
ENV PYTHONUNBUFFERED=1
5+
ENV PYTHONDONTWRITEBYTECODE=1
66

77
ARG REQUIREMENTS=requirements.txt
88

@@ -30,6 +30,7 @@ RUN pip install --no-cache-dir -r /tmp/requirements.txt \
3030
&& install -d -m 0755 -o app_user -g app_user /app/media \
3131
&& install -d -m 0755 -o app_user -g app_user /app/media/contest_files \
3232
&& install -d -m 0755 -o app_user -g app_user /app/media/ec_files \
33+
&& install -d -m 0755 -o app_user -g app_user /app/media/team_files \
3334
&& install -d -m 0755 -o app_user -g app_user /app/media/uploads
3435

3536
# Code and User Setup

src/contestadmin/forms.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,30 @@ class Meta:
4343
model = Profile
4444
fields = ["role"]
4545

46+
47+
class AccountStatusForm(forms.Form):
48+
STATUS = (
49+
(0, 'Activate'),
50+
(1, 'Deactivate')
51+
)
4652

47-
class ActivateAccountForm(forms.Form):
4853
username = forms.CharField(
4954
max_length=150,
5055
label='Username',
5156
help_text="Person's account username.")
57+
status = forms.ChoiceField(choices=STATUS)
5258

5359

5460
class DesignateFacultyTeamForm(forms.Form):
5561
teamname = forms.CharField(
5662
max_length=30,
5763
label='Team name',
5864
help_text="Name of faculty team.")
65+
66+
67+
class UpdatePasswordForm(forms.Form):
68+
username = forms.CharField(
69+
max_length=150,
70+
label='Username',
71+
help_text="Person's account username.")
72+
password = forms.CharField()

src/contestadmin/tasks.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,38 @@ def generate_ec_reports():
266266
f'Processed extra credit files for {num_courses} courses')
267267

268268

269+
@shared_task
270+
def generate_team_csvs():
271+
"""
272+
Celery task which creates CSV files containing team data per division.
273+
"""
274+
275+
for division in Team.DIVISION:
276+
if division[0] == 1: # Upper
277+
team_file = f"{MEDIA_ROOT}/team_files/upper.csv"
278+
else: # Lower
279+
team_file = f"{MEDIA_ROOT}/team_files/lower.csv"
280+
281+
with open(team_file, 'w', newline='') as team_csv:
282+
writer = csv.writer(
283+
team_csv, delimiter=',', quoting=csv.QUOTE_MINIMAL)
284+
285+
# File header
286+
writer.writerow(['team_division', 'team_name', 'questions_answered', 'domjudge_id', 'team_active', 'team_members'])
287+
288+
# Team data
289+
teams = Team.objects.filter(division=division[0])
290+
for team in teams:
291+
writer.writerow([
292+
team.get_division_code(),
293+
team.name,
294+
team.questions_answered,
295+
team.contest_id,
296+
'T' if team.is_active() else 'F',
297+
'_'.join(team.get_members())
298+
])
299+
300+
269301
@shared_task
270302
def email_faculty(domain):
271303
"""

src/contestadmin/templates/contestadmin/dashboard.html

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ <h1 class="text-center">Contest Dashboard</h1>
2525
<i class="fa-solid fa-clock-rotate-left fa-fw"></i> Pre-Contest
2626
</div>
2727
<div class="card-body overflow-auto">
28+
<div class="row justify-content-center">
29+
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#walkinModal"> <i class="fa-solid fa-person-walking fa-fw"></i> Create Walk-in teams</button>
30+
</div>
2831
<div class="row justify-content-center">
2932
<a class="btn btn-primary btn-sm my-1" href="{% url 'gen_dj_files' %}" onclick="return confirm('Are you certain you want to generate the DOMjudge files?');"><i class="fa-solid fa-file-circle-plus fa-fw"></i> Generate DOMjudge TSVs</a>
3033
{% if dj_files_available %}
@@ -73,7 +76,6 @@ <h1 class="text-center">Contest Dashboard</h1>
7376
</div>
7477
<div class="card-body overflow-auto">
7578
<div class="row justify-content-center">
76-
7779
{% if dj_results_processed %}
7880
<!--<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#generateEcReportsModal">Generate Reports</button>-->
7981
<a class="btn btn-primary btn-sm my-1" href="{% url 'gen_ec_reports' %}" onclick="return confirm('Are you certain you want to generate the extra credit files?');"><i class="fa-solid fa-file-circle-plus fa-fw"></i> Generate Reports</a>
@@ -101,15 +103,34 @@ <h1 class="text-center">Contest Dashboard</h1>
101103
<!-- Contest tools card -->
102104
<div class="card mt-4 border-secondary">
103105
<div class="card-header font-weight-bold bg-secondary text-white">
104-
<i class="fa-solid fa-wrench fa-fw"></i> Contest Tools
106+
<i class="fa-solid fa-wrench fa-fw"></i> Tools
105107
</div>
106108
<div class="card-body overflow-auto">
107-
<div class="row justify-content-center">
108-
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#walkinModal"> <i class="fa-solid fa-person-walking fa-fw"></i> Create Walk-in teams</button>
109+
<div class="row">
110+
<div class="col-lg-6">
111+
<div class="row justify-content-center">
112+
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#accountStatusModal"><i class="fa-solid fa-user-gear fa-fw"></i> Update Account Status</button>
113+
</div>
114+
<div class="row justify-content-center">
115+
<a class="btn btn-primary btn-sm my-1" href="{% url 'generate_team_csvs' %}" onclick="return confirm('Are you certain you want to generate the team data CSVs?');"><i class="fa-solid fa-file-circle-plus fa-fw"></i> Generate Team CSVs</a>
116+
{% if team_csvs_available %}
117+
<a class="btn text-dark" href="{% url 'download_team_csvs' %}" role="button"><i class="fa fa-download fa-md" aria-hidden="true"></i></a>
118+
{% else %}
119+
<a class="btn text-secondary disabled" href="#" role="button"><i class="fa fa-download fa-md" aria-hidden="true"></i></a>
120+
{% endif %}
121+
</div>
122+
</div>
123+
<div class="col-lg-6">
124+
<div class="row justify-content-center">
125+
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#facultyTeamModal"><i
126+
class="fa-solid fa-chalkboard-user fa-fw"></i> Designate Faculty Team</button>
127+
</div>
128+
<div class="row justify-content-center">
129+
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#checkinModal"><i class="fa-solid fa-robot fa-fw"></i> Check in/out Users</button>
130+
</div>
131+
</div>
109132
</div>
110-
<div class="row justify-content-center">
111-
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#checkinModal"><i class="fa-solid fa-robot fa-fw"></i> Check in/out Users</button>
112-
</div>
133+
113134
</div>
114135
</div>
115136
<!-- Contest tools card end -->
@@ -142,17 +163,22 @@ <h1 class="text-center">Contest Dashboard</h1>
142163
<!-- Account tools card -->
143164
<div class="card mt-4 border-secondary">
144165
<div class="card-header font-weight-bold bg-secondary text-white">
145-
<i class="fa-solid fa-wrench fa-fw"></i> Account Tools
166+
<i class="fa-solid fa-user-shield fa-fw"></i> Change User Password
146167
</div>
147-
<div class="card-body overflow-auto">
148-
<div class="row justify-content-center">
149-
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#activateAccountModal"><i class="fa-solid fa-user-check fa-fw"></i> Activate User Account</button>
168+
<form method="post" onSubmit="return confirm('Are you certain you want to update this user password?');">
169+
<div class="card-body overflow-auto">
170+
{% csrf_token %}
171+
<div class="form-group">
172+
{{ update_password_form.username | placeholder:"Username" }}
173+
</div>
174+
<div class="form-group">
175+
{{ update_password_form.password | placeholder:"New password" }}
176+
</div>
150177
</div>
151-
<div class="row justify-content-center">
152-
<button type="button" class="btn btn-primary btn-sm my-1" data-toggle="modal" data-target="#facultyTeamModal"><i
153-
class="fa-solid fa-chalkboard-user fa-fw"></i> Designate Faculty Team</button>
178+
<div class="card-footer">
179+
<button class="btn btn-primary btn-sm" type="submit">Save</button>
154180
</div>
155-
</div>
181+
</form>
156182
</div>
157183
<!-- Account tools card end -->
158184
</div>
@@ -257,7 +283,6 @@ <h1 class="text-center">Contest Dashboard</h1>
257283
</div>
258284
<!-- Volunteer Summary card end -->
259285

260-
261286
<!-- Create Walk-in Teams Modal -->
262287
<div class="modal fade" id="walkinModal" tabindex="-1" role="dialog"
263288
aria-labelledby="confirmClearModalCenterTitle" aria-hidden="true">
@@ -294,7 +319,6 @@ <h5 class="mb-0">{{ field.label_tag }}</h5>
294319
</div>
295320
<!-- End Create Walk-in Teams Modal -->
296321

297-
298322
<!-- Checkin/Checkout Users Modal -->
299323
<div class="modal fade" id="checkinModal" tabindex="-1" role="dialog" aria-labelledby="confirmClearModalCenterTitle"
300324
aria-hidden="true">
@@ -330,20 +354,22 @@ <h5 class="modal-title" id="confirmClearModalLongTitle">Check in/out Users</h5>
330354
</div>
331355
<!-- End Create Walk-in Teams Modal -->
332356

333-
334-
<!-- Activate Account Modal -->
335-
<div class="modal fade" id="activateAccountModal" tabindex="-1" role="dialog"
357+
<!-- Account Status Modal -->
358+
<div class="modal fade" id="accountStatusModal" tabindex="-1" role="dialog"
336359
aria-labelledby="confirmClearModalCenterTitle" aria-hidden="true">
337360
<div class="modal-dialog modal-dialog-centered" role="document">
338361
<div class="modal-content">
339362
<div class="modal-header">
340-
<h5 class="modal-title" id="confirmClearModalLongTitle">Activate User Account</h5>
363+
<h5 class="modal-title" id="confirmClearModalLongTitle">Update Account Status</h5>
341364
</div>
342-
<form method="post" onSubmit="return confirm('Are you certain you want to activate this account?');">
365+
<form method="post" onSubmit="return confirm('Are you certain you want to update this account?');">
343366
<div class="modal-body overflow-auto">
344367
{% csrf_token %}
345368
<div class="form-group">
346-
{{ activate_account_form.username | placeholder:"Username"}}
369+
{{ account_status_form.username | placeholder:"Username" }}
370+
</div>
371+
<div class="form-group">
372+
{{ account_status_form.status }}
347373
</div>
348374
</div>
349375
<div class="card-footer">
@@ -354,8 +380,7 @@ <h5 class="modal-title" id="confirmClearModalLongTitle">Activate User Account</h
354380
</div>
355381
</div>
356382
</div>
357-
<!-- End Activate Account Modal -->
358-
383+
<!-- End Account Status Modal -->
359384

360385
<!-- Designate Faculty Team Modal -->
361386
<div class="modal fade" id="facultyTeamModal" tabindex="-1" role="dialog"

src/contestadmin/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@
1515
path('ec_files/generate', login_required((user_passes_test(contestadmin_auth, login_url='/', redirect_field_name=None))(views.GenerateExtraCreditReports.as_view())), name='gen_ec_reports'),
1616
path('faculty/<uidb64>/', views.FacultyDashboard.as_view(), name='fac_ec_dashboard'),
1717
path('faculty/<uidb64>/download', views.FacultyDashboard.download, name='fac_ec_files_dl'),
18-
path('statistics/', views.contest_statistics, name='contest_stats')
18+
path('statistics/', views.contest_statistics, name='contest_stats'),
19+
path('team_csvs/generate', views.ExportTeamData.as_view(), name='generate_team_csvs'),
20+
path('team_csvs/download', views.ExportTeamData.download, name='download_team_csvs')
1921
]

src/contestadmin/utils.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
from django.contrib.auth.mixins import UserPassesTestMixin
2+
13
"""
24
Functions useable by @user_passes_test view decorator. Each function accepts a User object
35
as its only parameter.
46
"""
57

68
def contestadmin_auth(user):
7-
return user.profile.role == 5 or user.is_superuser
9+
return user.profile.role == 5 or user.is_superuser
10+
11+
12+
class ContestAdminAuthMixin(UserPassesTestMixin):
13+
"""
14+
Mixin which integrates the contestadmin_auth test into a UserPassesTestMixin.
15+
- Enables class based view support of the test.
16+
"""
17+
18+
def test_func(self):
19+
return contestadmin_auth(self.request.user)

0 commit comments

Comments
 (0)