From 176d31a48c9daa22ddf9ca0bc987da2f1ce970b9 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Tue, 18 Aug 2020 17:42:28 -0400 Subject: [PATCH 1/5] Initial Admin UI --- .gitignore | 3 + packet/__init__.py | 1 + packet/context_processors.py | 4 +- packet/ldap.py | 8 ++ packet/models.py | 7 ++ packet/routes/admin.py | 37 +++++++++ packet/routes/api.py | 2 + packet/routes/shared.py | 2 +- packet/static/js/admin.js | 79 +++++++++++++++++++ packet/templates/admin.html | 37 +++++++++ .../templates/include/admin/all_freshmen.html | 33 ++++++++ .../templates/include/admin/new_packets.html | 23 ++++++ .../templates/include/admin/open_packets.html | 39 +++++++++ packet/templates/include/nav.html | 3 + packet/utils.py | 21 ++++- 15 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 packet/routes/admin.py create mode 100644 packet/static/js/admin.js create mode 100644 packet/templates/admin.html create mode 100644 packet/templates/include/admin/all_freshmen.html create mode 100644 packet/templates/include/admin/new_packets.html create mode 100644 packet/templates/include/admin/open_packets.html diff --git a/.gitignore b/.gitignore index 2dde3669..fda07a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,9 @@ ENV/ # JetBrains .idea/ +# vscode +.vscode + # Configurations config.py diff --git a/packet/__init__.py b/packet/__init__.py index c369efdc..32e9fe74 100644 --- a/packet/__init__.py +++ b/packet/__init__.py @@ -80,6 +80,7 @@ if app.config['REALM'] == 'csh': from .routes import upperclassmen + from .routes import admin else: from .routes import freshmen diff --git a/packet/context_processors.py b/packet/context_processors.py index 604d931b..935106b6 100644 --- a/packet/context_processors.py +++ b/packet/context_processors.py @@ -43,7 +43,7 @@ def get_roles(sig): # pylint: disable=bare-except -@lru_cache(maxsize=128) +@lru_cache(maxsize=256) def get_rit_name(username): try: freshman = Freshman.query.filter_by(rit_username=username).first() @@ -53,7 +53,7 @@ def get_rit_name(username): # pylint: disable=bare-except -@lru_cache(maxsize=128) +@lru_cache(maxsize=256) def get_rit_image(username): if username: addresses = [username + '@rit.edu', username + '@g.rit.edu'] diff --git a/packet/ldap.py b/packet/ldap.py index 30b2f285..1b7de82c 100644 --- a/packet/ldap.py +++ b/packet/ldap.py @@ -158,6 +158,14 @@ def ldap_is_eboard(member): return _ldap_is_member_of_group(member, 'eboard') +def ldap_is_evals(member): + return _ldap_is_member_of_group(member, 'eboard-evaluations') + + +def ldap_is_rtp(member): + return _ldap_is_member_of_group(member, 'rtp') + + def ldap_is_intromember(member): """ :param member: A CSHMember instance diff --git a/packet/models.py b/packet/models.py index 6f8cbb6d..355a9976 100644 --- a/packet/models.py +++ b/packet/models.py @@ -49,6 +49,13 @@ def by_username(cls, username: str): """ return cls.query.filter_by(rit_username=username).first() + @classmethod + def get_all(cls): + """ + Helper method to get all freshmen easily + """ + return cls.query.all() + class Packet(db.Model): __tablename__ = 'packet' diff --git a/packet/routes/admin.py b/packet/routes/admin.py new file mode 100644 index 00000000..5db63b36 --- /dev/null +++ b/packet/routes/admin.py @@ -0,0 +1,37 @@ +import csv +import os + +import requests +from flask import render_template, redirect, request +from werkzeug.utils import secure_filename + +from packet import app +from packet.models import Packet, Freshman +from packet.routes.shared import packet_sort_key +from packet.utils import before_request, packet_auth, admin_auth +from packet.log_utils import log_cache, log_time + + +@app.route('/admin/') +@log_cache +@packet_auth +@admin_auth +@before_request +@log_time +def admin(info=None): + open_packets = Packet.open_packets() + + # Pre-calculate and store the return values of did_sign(), signatures_received(), and signatures_required() + for packet in open_packets: + packet.did_sign_result = packet.did_sign(info['uid'], app.config['REALM'] == 'csh') + packet.signatures_received_result = packet.signatures_received() + packet.signatures_required_result = packet.signatures_required() + + open_packets.sort(key=packet_sort_key, reverse=True) + + all_freshmen = Freshman.get_all() + + return render_template('admin.html', + open_packets=open_packets, + all_freshmen=all_freshmen, + info=info) diff --git a/packet/routes/api.py b/packet/routes/api.py index f3d2f0f0..c369835a 100644 --- a/packet/routes/api.py +++ b/packet/routes/api.py @@ -12,6 +12,7 @@ from packet.ldap import ldap_get_eboard_role, ldap_get_active_rtps, ldap_get_3das, ldap_get_webmasters, \ ldap_get_drink_admins, ldap_get_constitutional_maintainers, ldap_is_intromember, ldap_get_active_members, \ ldap_is_on_coop, _ldap_is_member_of_group, ldap_get_member +from packet.log_utils import log_time from packet.mail import send_report_mail, send_start_packet_mail from packet.utils import before_request, packet_auth, notify_slack from packet.models import Packet, MiscSignature, NotificationSubscription, Freshman, FreshSignature, UpperSignature @@ -80,6 +81,7 @@ def sync_freshman(): @app.route('/api/v1/packets', methods=['POST']) @packet_auth +@log_time def create_packet(): """ Create a new packet. diff --git a/packet/routes/shared.py b/packet/routes/shared.py index 97241815..5b872786 100644 --- a/packet/routes/shared.py +++ b/packet/routes/shared.py @@ -13,7 +13,7 @@ @app.route('/logout/') @auth.oidc_logout def logout(): - return redirect('http://csh.rit.edu') + return redirect('https://csh.rit.edu') @app.route('/packet//') diff --git a/packet/static/js/admin.js b/packet/static/js/admin.js new file mode 100644 index 00000000..f279f0c7 --- /dev/null +++ b/packet/static/js/admin.js @@ -0,0 +1,79 @@ +$(document).ready(function () { + + let openPacketsTable = $('#open_packets_table'); + openPacketsTable.DataTable({ + "searching": true, + "order": [], + "scrollX": false, + "paging": true, + "info": false, + "columnDefs": [ + { + "targets": 0, + "max-width": "50%", + }, + { + "type": "num-fmt", + "targets": 1, + "visible": true, + "max-width": "15%", + } + ] + }); + + let allFreshmenTable = $('#all_freshmen_table'); + allFreshmenTable.DataTable({ + "searching": true, + "order": [], + "scrollX": false, + "paging": true, + "info": false, + }); + + $("#create-packets").click(() => { + makePackets(); + }); + +}); + +let makePackets = () => { + let freshmen = []; + let fileUpload = document.getElementById("newPacketsFile"); + let regex = /^([a-zA-Z0-9\s_\\.\-:])+(.csv|.txt)$/; + if (regex.test(fileUpload.value.toLowerCase())) { + if (typeof (FileReader) != "undefined") { + let reader = new FileReader(); + reader.onload = (e) => { + let rows = e.target.result.split("\n"); + for (let i = 0; i < rows.length; i++) { + let cells = rows[i].split(","); + if (cells.length > 1) { + freshmen.push(cells[3]); + } + } + const payload = {start_date: $('#packet-start-date').val(), freshmen: freshmen} + if (freshmen.length >= 1) { + $("#create-packets").append(" "); + $("#create-packets").attr('disabled', true); + fetch('/api/v1/packets', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + } + ).then(response => { + if (response.status < 300) { + $('#new-packets-modal').modal('hide'); + location.reload(); + } else { + alert("There was an error creating packets") + } + }) + } + } + reader.readAsText(fileUpload.files[0]); + } + } +} diff --git a/packet/templates/admin.html b/packet/templates/admin.html new file mode 100644 index 00000000..d9c81abd --- /dev/null +++ b/packet/templates/admin.html @@ -0,0 +1,37 @@ +{% extends "extend/base.html" %} + +{% block body %} +
+
+
+
+

Active Packets

+
+
+ + {% include 'include/admin/new_packets.html' %} +
+
+
+
+ {% include 'include/admin/open_packets.html' %} +
+{#
#} +{#
#} +{#
#} +{#

All Freshmen

#} +{#
#} +{#
#} +{#
#} +{#
#} +{# {% include 'include/admin/all_freshmen.html' %}#} +{#
#} +
+{% endblock %} + +{% block scripts %} + {{ super() }} + +{% endblock %} diff --git a/packet/templates/include/admin/all_freshmen.html b/packet/templates/include/admin/all_freshmen.html new file mode 100644 index 00000000..a3e79e3b --- /dev/null +++ b/packet/templates/include/admin/all_freshmen.html @@ -0,0 +1,33 @@ +
+
+
+
+ + + + + + + + + {% for freshman in all_freshmen %} + {% set freshman_name = freshman.name + ' (' + freshman.rit_username + ')' %} + + + + + {% endfor %} + +
NameOn-Floor
+ {{ freshman_name }} {{ freshman_name }} + + {{ freshman.onfloor }} +
+
+
+
+
diff --git a/packet/templates/include/admin/new_packets.html b/packet/templates/include/admin/new_packets.html new file mode 100644 index 00000000..ba0667a2 --- /dev/null +++ b/packet/templates/include/admin/new_packets.html @@ -0,0 +1,23 @@ + diff --git a/packet/templates/include/admin/open_packets.html b/packet/templates/include/admin/open_packets.html new file mode 100644 index 00000000..275b622c --- /dev/null +++ b/packet/templates/include/admin/open_packets.html @@ -0,0 +1,39 @@ +
+
+
+
+ + + + + + + + + {% for packet in open_packets %} + + + + + {% endfor %} + +
NameSignatures
+ + {{ get_rit_name(packet.freshman_username) }} {{ get_rit_name(packet.freshman_username) }} + + + {% if packet.signatures_received_result.total == packet.signatures_required_result.total %} + 💯 {# 100% emoji #} + {% else %} + {{ packet.signatures_received_result.total }} / + {{ packet.signatures_required_result.total }} + {% endif %} +
+
+
+
+
diff --git a/packet/templates/include/nav.html b/packet/templates/include/nav.html index fb8f2a62..02a15e4c 100644 --- a/packet/templates/include/nav.html +++ b/packet/templates/include/nav.html @@ -18,6 +18,9 @@ + {% else %} - + {% if info.admin %} + + {% endif %} {% else %}