From 275e35b0a2969e6af045accd261cd5c8d4d5edf0 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Fri, 7 Sep 2018 12:31:40 -0400 Subject: [PATCH 01/11] Cleanup and Refactor --- packet/ldap.py | 1 - packet/member.py | 11 ------- packet/packet.py | 75 +++++++++++++++++++++++++++++++++++++----------- packet/utils.py | 9 +++--- 4 files changed, 63 insertions(+), 33 deletions(-) delete mode 100644 packet/member.py diff --git a/packet/ldap.py b/packet/ldap.py index 1e4f153a..25551044 100644 --- a/packet/ldap.py +++ b/packet/ldap.py @@ -8,7 +8,6 @@ def _ldap_get_group_members(group): return _ldap.get_group(group).get_members() -@lru_cache(maxsize=2048) def _ldap_is_member_of_group(member, group): group_list = member.get("memberOf") for group_dn in group_list: diff --git a/packet/member.py b/packet/member.py deleted file mode 100644 index 9e4a75b6..00000000 --- a/packet/member.py +++ /dev/null @@ -1,11 +0,0 @@ -from .models import Freshman, FreshSignature, UpperSignature, MiscSignature - - -def signed_packets(member): - # Checks whether or not member is a freshman - if Freshman.query.filter_by(rit_username=member).first() is not None: - return FreshSignature.query.filter_by(freshman_username=member, signed=True).all() - # Checks whether or not member is an upperclassman - if UpperSignature.query.filter_by(member=member).first() is not None: - return UpperSignature.query.filter_by(member=member, signed=True).all() - return MiscSignature.query.filter_by(member=member).all() diff --git a/packet/packet.py b/packet/packet.py index a0827237..ea138108 100644 --- a/packet/packet.py +++ b/packet/packet.py @@ -6,16 +6,10 @@ def sign(signer_username, freshman_username): - if signer_username == freshman_username: - return False - - freshman_signed = Freshman.query.filter_by(rit_username=freshman_username).first() - if freshman_signed is None: - return False - packet = freshman_signed.current_packet() - if packet is None or not packet.is_open(): + if not valid_signature(signer_username, freshman_username): return False + packet = get_freshman(freshman_username).current_packet() upper_signature = UpperSignature.query.filter(UpperSignature.member == signer_username, UpperSignature.packet == packet).first() fresh_signature = FreshSignature.query.filter(FreshSignature.freshman_username == signer_username, @@ -27,7 +21,7 @@ def sign(signer_username, freshman_username): upper_signature.signed = True elif fresh_signature: # Make sure only on floor freshmen can sign packets - freshman_signer = Freshman.query.filter_by(rit_username=signer_username).first() + freshman_signer = get_freshman(signer_username) if freshman_signer and not freshman_signer.onfloor: return False fresh_signature.signed = True @@ -35,17 +29,14 @@ def sign(signer_username, freshman_username): db.session.add(MiscSignature(packet=packet, member=signer_username)) db.session.commit() - # Clear functions that read signatures cache - get_number_signed.cache_clear() - get_signatures.cache_clear() - get_upperclassmen_percent.cache_clear() + clear_cache() return True @lru_cache(maxsize=2048) def get_signatures(freshman_username): - packet = Freshman.query.filter_by(rit_username=freshman_username).first().current_packet() + packet = get_current_packet(freshman_username) eboard = UpperSignature.query.filter_by(packet_id=packet.id, eboard=True).order_by(UpperSignature.signed.desc()) upper_signatures = UpperSignature.query.filter_by(packet_id=packet.id, eboard=False).order_by( UpperSignature.signed.desc()) @@ -57,14 +48,40 @@ def get_signatures(freshman_username): 'misc': misc_signatures} +@lru_cache(maxsize=2048) +def valid_signature(signer_username, freshman_username): + if signer_username == freshman_username: + return False + + freshman_signed = get_freshman(freshman_username) + if freshman_signed is None: + return False + + packet = freshman_signed.current_packet() + if packet is None or not packet.is_open(): + return False + + return True + + +@lru_cache(maxsize=512) +def get_freshman(freshman_username): + return Freshman.query.filter_by(rit_username=freshman_username).first() + + +@lru_cache(maxsize=512) +def get_current_packet(freshman_username): + return get_freshman(freshman_username).current_packet() + + @lru_cache(maxsize=2048) def get_number_signed(freshman_username): - return Freshman.query.filter_by(rit_username=freshman_username).first().current_packet().signatures_received() + return get_current_packet(freshman_username).signatures_received() -@lru_cache(maxsize=4096) +@lru_cache(maxsize=2048) def get_number_required(freshman_username): - return Freshman.query.filter_by(rit_username=freshman_username).first().current_packet().signatures_required() + return get_current_packet(freshman_username).signatures_required() @lru_cache(maxsize=2048) @@ -78,3 +95,27 @@ def get_upperclassmen_percent(uid): upperclassmen_signature = sum(upperclassmen_signature.values()) return upperclassmen_signature / upperclassmen_required * 100 + + +@lru_cache(maxsize=512) +def signed_packets(member): + # Checks whether or not member is a freshman + if get_freshman(member) is not None: + return FreshSignature.query.filter_by(freshman_username=member, signed=True).all() + # Checks whether or not member is an upperclassman + if UpperSignature.query.filter_by(member=member).first() is not None: + return UpperSignature.query.filter_by(member=member, signed=True).all() + return MiscSignature.query.filter_by(member=member).all() + + +def clear_cache(): + """ + Clear cache of all frequently changing data + """ + get_number_signed.cache_clear() + get_signatures.cache_clear() + get_number_required.cache_clear() + get_upperclassmen_percent.cache_clear() + get_freshman.cache_clear() + get_current_packet.cache_clear() + signed_packets.cache_clear() diff --git a/packet/utils.py b/packet/utils.py index 67730aec..0e6d0f40 100644 --- a/packet/utils.py +++ b/packet/utils.py @@ -11,7 +11,8 @@ ldap_is_onfloor, ldap_get_roomnumber, ldap_get_groups) -from packet.models import Freshman, FreshSignature, Packet, UpperSignature, MiscSignature +from packet.models import FreshSignature, UpperSignature, MiscSignature +from packet.packet import get_current_packet, get_freshman INTRO_REALM = "https://sso.csh.rit.edu/auth/realms/intro" @@ -63,12 +64,12 @@ def get_member_info(uid): @lru_cache(maxsize=2048) def is_on_floor(uid): - return (Freshman.query.filter_by(rit_username=uid)).first().onfloor + return get_freshman(uid).onfloor -@lru_cache(maxsize=4096) +@lru_cache(maxsize=2048) def signed_packet(signer, freshman): - packet = Packet.query.filter_by(freshman_username=freshman).first() + packet = get_current_packet(freshman) freshman_signature = FreshSignature.query.filter_by(packet=packet, freshman_username=signer, signed=True).first() upper_signature = UpperSignature.query.filter_by(packet=packet, member=signer, signed=True).first() misc_signature = MiscSignature.query.filter_by(packet=packet, member=signer).first() From f9b736375e17808ff35670f80b20b167a701129c Mon Sep 17 00:00:00 2001 From: Joel Eager Date: Wed, 5 Sep 2018 22:59:36 -0400 Subject: [PATCH 02/11] Fixed the packet end time --- packet/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packet/commands.py b/packet/commands.py index 9f922412..26df71ed 100644 --- a/packet/commands.py +++ b/packet/commands.py @@ -99,7 +99,7 @@ def create_packets(freshmen_csv): pass start = datetime.combine(base_date, time(hour=19)) - end = datetime.combine(base_date, time(hour=23, minute=59)) + timedelta(days=14) + end = datetime.combine(base_date, time(hour=21)) + timedelta(days=14) print("Fetching data from LDAP...") eboard = set(member.uid for member in ldap_get_eboard()) From 26d09cd5ea2708b9a808c0f7cb2d0792c117a984 Mon Sep 17 00:00:00 2001 From: Joel Eager Date: Sat, 8 Sep 2018 00:21:11 -0400 Subject: [PATCH 03/11] Bug fix for something that really shouldn't be a thing --- packet/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packet/models.py b/packet/models.py index 62f07de2..33eb63c5 100644 --- a/packet/models.py +++ b/packet/models.py @@ -51,7 +51,7 @@ def is_open(self): @lru_cache(maxsize=1024) def signatures_required(self): - eboard = UpperSignature.query.filter_by(eboard=True).count() + eboard = UpperSignature.query.with_parent(self).filter_by(eboard=True).count() return {'eboard': eboard, 'upperclassmen': len(self.upper_signatures) - eboard, 'freshmen': len(self.fresh_signatures), From 4d8e6fd7ae27f59de5d225c6dd445818c22f6aa8 Mon Sep 17 00:00:00 2001 From: Joel Eager Date: Sat, 8 Sep 2018 00:21:38 -0400 Subject: [PATCH 04/11] Wrote the fetch-results command for the admin cli --- packet/commands.py | 49 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/packet/commands.py b/packet/commands.py index 26df71ed..521ad0c2 100644 --- a/packet/commands.py +++ b/packet/commands.py @@ -19,6 +19,9 @@ def create_secret(): print("Here's your random secure token:") print(token_hex()) +packet_start_time = time(hour=19) +packet_end_time = time(hour=21) + class CSVFreshman: def __init__(self, row): self.name = row[0] @@ -98,8 +101,8 @@ def create_packets(freshmen_csv): except ValueError: pass - start = datetime.combine(base_date, time(hour=19)) - end = datetime.combine(base_date, time(hour=21)) + timedelta(days=14) + start = datetime.combine(base_date, packet_start_time) + end = datetime.combine(base_date, packet_end_time) + timedelta(days=14) print("Fetching data from LDAP...") eboard = set(member.uid for member in ldap_get_eboard()) @@ -158,3 +161,45 @@ def ldap_sync(): db.session.commit() print("Done!") + +@app.cli.command("fetch-results") +def fetch_results(): + """ + Fetches and prints the results from a given packet season. + """ + end_date = None + while end_date is None: + try: + date_str = input("Enter the last day of the packet season you'd like to retrieve results from " + + "(format: MM/DD/YYYY): ") + end_date = datetime.strptime(date_str, "%m/%d/%Y").date() + except ValueError: + pass + + end_date = datetime.combine(end_date, packet_end_time) + + for packet in Packet.query.filter_by(end=end_date).all(): + print() + + print("{} ({}):".format(packet.freshman.name, packet.freshman.rit_username)) + + received = packet.signatures_received() + required = packet.signatures_required() + + upper_ratio = sum((received["eboard"], received["upperclassmen"], received["miscellaneous"])) / \ + sum((required["eboard"], required["upperclassmen"], required["misc"])) + print("\tUpperclassmen score: {}%".format(round(upper_ratio * 100, 2))) + + total_ratio = sum(received.values()) / sum(required.values()) + print("\tTotal score: {}%".format(round(total_ratio * 100, 2))) + + print() + + print("\tEboard: {}/{}".format(received["eboard"], required["eboard"])) + print("\tUpperclassmen: {}/{}".format(received["upperclassmen"], required["upperclassmen"])) + print("\tFreshmen: {}/{}".format(received["freshmen"], required["freshmen"])) + print("\tMisc: {}/{}".format(len(packet.misc_signatures), required["misc"])) + + print() + + print("\tTotal missed:", sum(required.values()) - sum(received.values())) \ No newline at end of file From 9f7bcd5da0c48dbc3a80c8244abca7b50dfc2f5a Mon Sep 17 00:00:00 2001 From: Joel Eager Date: Sat, 8 Sep 2018 00:31:15 -0400 Subject: [PATCH 05/11] Pylint fix --- packet/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packet/commands.py b/packet/commands.py index 521ad0c2..ae3fb8c5 100644 --- a/packet/commands.py +++ b/packet/commands.py @@ -202,4 +202,4 @@ def fetch_results(): print() - print("\tTotal missed:", sum(required.values()) - sum(received.values())) \ No newline at end of file + print("\tTotal missed:", sum(required.values()) - sum(received.values())) From a0c3fae252901ea179ec933dc2b701cf858a811b Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Sat, 8 Sep 2018 17:23:19 -0400 Subject: [PATCH 06/11] Adding a README --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c61b838a..26c7ae17 100644 --- a/README.md +++ b/README.md @@ -1 +1,47 @@ -*Coming soon to a repo near you* \ No newline at end of file +CSH Web Packet +============== + +[![Python 3.6](https://img.shields.io/badge/python-3.6-blue.svg)](https://www.python.org/downloads/release/python-360/) +[![Build Status](https://travis-ci.org/ComputerScienceHouse/packet.svg?branch=develop)](https://travis-ci.org/ComputerScienceHouse/packet) + +Web Packet is used by CSH to facilitate the evaluations of our members and keep track of packet signatures on the web + +Authorization +------------- + +Authentication happens via pyOIDC with CSH SSO, authenticating as the user who is viewing the page. +We have two different realms, and the site changes depending which realm is in use. + +The server uses heavy caching via lru_cache to speed up the results as much as possible + +Setup +------ + +For local development setup follow these steps: + +1. ```pip install -r requirements.txt``` +2. `Create config.py` or set environment variables + - Several of these variables require keys and information, please reach out to an RTP for testing information +3. Run `wsgi.py` + + +Commands +-------- + +The flask CLI provides all the methods needed to setup a packet and a packet season + +``` + create-packets Creates a new packet season for each of the freshmen in the given CSV. + create-secret Generates a securely random token. + db Perform database migrations. + ldap-sync Updates the upper and misc sigs in the DB to match ldap. + sync-freshmen Updates the freshmen entries in the DB to match the given CSV. +``` + +Code Standards +------------ + +Use Pylint to ensure your code follows standards. Commits will be pylinted by Travis CI, if your +build fails you must fix whatever it tells you is wrong before it will be merged. + +To check locally, run ```pylint packet``` From b359f268745578131ae4e1047a45408ce7838198 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Sun, 9 Sep 2018 22:19:16 -0400 Subject: [PATCH 07/11] Version Bump to 3.0.3 --- package.json | 2 +- packet/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d7910e6c..d17a09dd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "CSH Packet", "name": "csh-packet", - "version": "3.0.2", + "version": "3.0.3", "description": "A webpacket for CSH", "bugs": { "url": "https://github.com/ComputerScienceHouse/packet/issues", diff --git a/packet/_version.py b/packet/_version.py index 131942e7..8d1c8625 100644 --- a/packet/_version.py +++ b/packet/_version.py @@ -1 +1 @@ -__version__ = "3.0.2" +__version__ = "3.0.3" From 7d3effede5c4ab705c3f561db663d8bef7d2178d Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Mon, 10 Sep 2018 11:19:56 -0400 Subject: [PATCH 08/11] Fixing searching and sorting for freshmen --- packet/static/js/tables.js | 2 +- packet/templates/active_packets.html | 99 +++++++++++++++------------- packet/templates/packet.html | 12 ++-- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/packet/static/js/tables.js b/packet/static/js/tables.js index f6c2be10..1f156d6c 100644 --- a/packet/static/js/tables.js +++ b/packet/static/js/tables.js @@ -2,7 +2,7 @@ $(document).ready(function () { $('#active_packets_table').DataTable({ "searching": true, - "order": [[2, 'desc']], + "order": [], "paging": false, "info": false, "columnDefs": [ diff --git a/packet/templates/active_packets.html b/packet/templates/active_packets.html index 1832f2f5..a49cca61 100644 --- a/packet/templates/active_packets.html +++ b/packet/templates/active_packets.html @@ -1,5 +1,7 @@ {% extends "extend/base.html" %} +{% set can_sign = (info.onfloor and info.uid != packet.freshman.rit_username) or info.realm == "csh" %} + {% block body %}
@@ -12,57 +14,60 @@

Active Packets

-
-
- - - - - +
+
NameSignatures
+ + + + + {% if can_sign %} - - - - {% for packet in packets %} - {% if packet.is_open() %} - - - + + + {% for packet in packets %} + {% if packet.is_open() %} + + + + {% if can_sign %} + - {% if (info.onfloor and info.uid != packet.freshman.rit_username) or info.realm == "csh" %} - - {% endif %} - - {% endif %} - {% endfor %} - -
NameSignaturesSign
- - {{ packet.freshman.name }} {{ packet.freshman.name }} ({{ packet.freshman.rit_username }}) - - - {% if packet.total_signatures == packet.required_signatures %} - 💯 {# 100% emoji #} + {% endif %} +
+ + {{ packet.freshman.name }} {{ packet.freshman.name }} + ({{ packet.freshman.rit_username }}) + + + {% if packet.total_signatures == packet.required_signatures %} + 💯 {# 100% emoji #} + {% else %} + {{ packet.total_signatures }}/ + {{ packet.required_signatures }} + {% endif %} + + {% if not packet.did_sign %} + {% else %} - {{ packet.total_signatures }}/{{ packet.required_signatures }} + {% endif %} - {% if not packet.did_sign %} - - {% else %} - - {% endif %} -
-
+ {% endif %} + + {% endif %} + {% endfor %} + +
diff --git a/packet/templates/packet.html b/packet/templates/packet.html index 1638834e..1a4fb608 100644 --- a/packet/templates/packet.html +++ b/packet/templates/packet.html @@ -1,5 +1,7 @@ {% extends "extend/base.html" %} +{% set packet_end = freshman.current_packet().end.strftime('%m/%d/%Y') %} + {% block body %}
@@ -26,26 +28,26 @@

{{ freshman.name }}

Signatures: {{ signed }}/{{ required }}
+
+
Packet Ends: {{ packet_end }}
+
- {{ '%0.2f' % (signed/required * 100) }}% -
Total Score
+
Total Score - {{ '%0.2f' % (signed/required * 100) }}%
- {{ '%0.2f' % upperclassmen_percent }}% -
Upperclassmen Score
+
Upperclassmen Score - {{ '%0.2f' % upperclassmen_percent }}%
-
From 1bf3a090302a34f939551ee0138e1e1b907f71b9 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Mon, 10 Sep 2018 11:20:34 -0400 Subject: [PATCH 09/11] Cleaning up Essays page --- frontend/scss/partials/_base.scss | 4 ++++ packet/templates/essays.html | 32 ++++++++++++++++++------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/frontend/scss/partials/_base.scss b/frontend/scss/partials/_base.scss index c580cd36..6ba84a3b 100644 --- a/frontend/scss/partials/_base.scss +++ b/frontend/scss/partials/_base.scss @@ -12,3 +12,7 @@ body { display: flex; justify-content: space-between; } + +.right-align { + float: right; +} diff --git a/packet/templates/essays.html b/packet/templates/essays.html index 96aa0378..f9b4ccf1 100644 --- a/packet/templates/essays.html +++ b/packet/templates/essays.html @@ -10,23 +10,29 @@

Essays

-
-
-
- - + + +
+
+ +
-
- - +
+ +
-
- - +
+ +
+
+ +
+
From abdafe3ae4ae50a91985ce17329e968131253570 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Mon, 10 Sep 2018 11:27:07 -0400 Subject: [PATCH 10/11] Using methods on essays --- packet/routes/freshmen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packet/routes/freshmen.py b/packet/routes/freshmen.py index f12234a4..4dc41556 100644 --- a/packet/routes/freshmen.py +++ b/packet/routes/freshmen.py @@ -1,7 +1,7 @@ from flask import redirect, render_template, request from packet import auth, app, db -from packet.models import Packet +from packet.packet import get_current_packet from packet.utils import before_request @@ -16,7 +16,7 @@ def index(info=None): @auth.oidc_auth @before_request def essays(info=None): - packet = Packet.query.filter_by(freshman_username=info['uid']).first() + packet = get_current_packet(info['uid']) return render_template("essays.html", info=info, packet=packet) @@ -25,7 +25,7 @@ def essays(info=None): @before_request def submit_essay(info=None): formdata = request.form - packet = Packet.query.filter_by(freshman_username=info['uid']).first() + packet = get_current_packet(info['uid']) packet.info_eboard = formdata['info_eboard'] packet.info_events = formdata['info_events'] From 89de12c88010fcadc19b763a7f06ace6a722aa01 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Mon, 10 Sep 2018 12:08:54 -0400 Subject: [PATCH 11/11] Adding fetch-results --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 26c7ae17..5c07e710 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ The flask CLI provides all the methods needed to setup a packet and a packet sea db Perform database migrations. ldap-sync Updates the upper and misc sigs in the DB to match ldap. sync-freshmen Updates the freshmen entries in the DB to match the given CSV. + fetch-results Fetches and prints the results from a given packet season. ``` Code Standards