From d10177a9ad1f0973145f737fcf26bbdd89f54757 Mon Sep 17 00:00:00 2001 From: Stefan Dworschak Date: Wed, 24 Feb 2021 17:29:20 +0000 Subject: [PATCH] #A15. As Admin, I would like to be able to view the participants of a hackathon (#153) --- .../migrations/0038_auto_20210224_1521.py | 25 +++++++ hackathon/models.py | 4 +- .../templates/hackathon/hackathon_stats.html | 74 +++++++++++++++++++ hackathon/urls.py | 2 + hackathon/views.py | 15 ++++ static/css/style.css | 5 ++ static/js/script.js | 42 +++++++++++ templates/includes/navbar.html | 1 + 8 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 hackathon/migrations/0038_auto_20210224_1521.py create mode 100644 hackathon/templates/hackathon/hackathon_stats.html diff --git a/hackathon/migrations/0038_auto_20210224_1521.py b/hackathon/migrations/0038_auto_20210224_1521.py new file mode 100644 index 00000000..8996b4c7 --- /dev/null +++ b/hackathon/migrations/0038_auto_20210224_1521.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.3 on 2021-02-24 15:21 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('hackathon', '0037_hackathon_teamsize'), + ] + + operations = [ + migrations.AlterField( + model_name='hackathon', + name='judges', + field=models.ManyToManyField(blank=True, related_name='judged_hackathons', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='hackathon', + name='participants', + field=models.ManyToManyField(blank=True, related_name='participated_hackathons', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/hackathon/models.py b/hackathon/models.py index 1d4745eb..d791bb82 100644 --- a/hackathon/models.py +++ b/hackathon/models.py @@ -39,12 +39,12 @@ class Hackathon(models.Model): # users could be the judges of more than one Hackathon: Many to Many judges = models.ManyToManyField(User, blank=True, - related_name='hackathon_judges') + related_name='judged_hackathons') # Hackathons can have multiple participants judges and # users could be participating in more than one Hackathon: Many to Many participants = models.ManyToManyField(User, blank=True, - related_name='hackathon_participants') + related_name='participated_hackathons') # Hackathons can have multiple score categories and score categories # Can belong to multiple hackahtons: Many to Many score_categories = models.ManyToManyField( diff --git a/hackathon/templates/hackathon/hackathon_stats.html b/hackathon/templates/hackathon/hackathon_stats.html new file mode 100644 index 00000000..3fa3a821 --- /dev/null +++ b/hackathon/templates/hackathon/hackathon_stats.html @@ -0,0 +1,74 @@ +{% extends 'base.html' %} + +{% block content %} + +
+
+
+
+
Hackathon Stats
+
+ +
+
+ +
+
+ User Count: {{ users|length }} +
+
+ +
+
+ + + + + + + + + + + + + {% for user in users %} + + + + + + + {% endfor %} +
Slack Display NameEmailCurrent LMS ModulePrivileges
+ {{ user.slack_display_name }} + + {{ user.email }} + + {{ user.current_lms_module }} + + {% if user.is_staff and user.is_superuser %} + Superuser + {% elif user.is_staff %} + Staff + {% else %} + Participant + {% endif %} +
+ +
+
+
+
+
+ + + +{% endblock %} diff --git a/hackathon/urls.py b/hackathon/urls.py index 992b7a68..f57205ee 100644 --- a/hackathon/urls.py +++ b/hackathon/urls.py @@ -11,6 +11,7 @@ view_hackathon, update_hackathon_status, change_awards, + hackathon_stats, ) from teams.views import change_teams @@ -30,4 +31,5 @@ path("/delete", delete_hackathon, name="delete_hackathon"), path('enroll/', enroll_toggle, name='enroll_toggle'), + path('stats/', hackathon_stats, name='hackathon_stats'), ] diff --git a/hackathon/views.py b/hackathon/views.py index 78b2ae53..c00778a4 100644 --- a/hackathon/views.py +++ b/hackathon/views.py @@ -9,6 +9,7 @@ from django.shortcuts import render, get_object_or_404, redirect, reverse from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.contrib.auth import get_user_model from django.core.paginator import Paginator from django.http import JsonResponse, HttpResponse from django.utils import timezone @@ -533,3 +534,17 @@ def change_awards(request, hack_id): "An unexpected error occurred. Please try again") return redirect(reverse('hackathon:awards', kwargs={'hack_id': hack_id})) + + +@login_required +def hackathon_stats(request): + if not request.user.is_superuser: + messages.error(request, 'You do not have access to this page.') + return reverse(reverse('hackathon:hackathon-list')) + + hackathons = Hackathon.objects.all().exclude(status='deleted') + users = get_user_model().objects.all() + return render(request, 'hackathon/hackathon_stats.html', { + 'hackathons': hackathons, + 'users': users, + }) diff --git a/static/css/style.css b/static/css/style.css index 7ff7c0cb..4ce1e96a 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -612,6 +612,11 @@ img.showcase-image-edit { max-width: none; } +/* Hackathon Stats */ +.hide-row{ + display:none; +} + /* Media Queries */ @media (max-width:453px) { diff --git a/static/js/script.js b/static/js/script.js index b1a5496f..0b9e5d74 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -1,5 +1,6 @@ $(document).ready(function(){ $('.edit-image').click(setUpoadImageType); + filterUsersByHackathon(); }); function setUpoadImageType(){ @@ -8,3 +9,44 @@ function setUpoadImageType(){ $('#image-upload-type').val(imageType); $('#image-upload-identifier').val(identifier); } + +function filterUsersByHackathon(){ + $('#hackathonFilter').change(function(){ + let userCount = 0; + let elementValue = $(this).val(); + $('#usersTable tbody tr').each(function(){ + if(elementValue == '0'){ + $(this).removeClass('hide-row'); + userCount++; + } else { + if($(this).data('hackathons').split(',').includes(elementValue)){ + $(this).removeClass('hide-row'); + userCount++; + } else { + $(this).addClass('hide-row'); + } + } + }); + $('#userCount').text(userCount); + }); + + $('#downloadUsers').click(function(){ + let csvContent = ''; + let rows = $('#usersTable tr:not(.hide-row)'); + rows.each(function(){ + let tds = $(this).children(); + let rowText = []; + tds.each(function(){ + rowText.push($(this).text().trim()); + }); + csvContent +=rowText.join(',') + '\n'; + }); + + let link = document.createElement('a') + link.id = 'download-csv' + link.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(csvContent)); + link.setAttribute('download', 'user-export.csv'); + document.body.appendChild(link); + document.querySelector('#download-csv').click(); + }); +} diff --git a/templates/includes/navbar.html b/templates/includes/navbar.html index 9c690b3c..bf0c1cab 100644 --- a/templates/includes/navbar.html +++ b/templates/includes/navbar.html @@ -66,6 +66,7 @@ My Profile {% if user.is_superuser %} Admin Panel + Hackthon Stats {% endif %} Logout {% endif %}