Skip to content

Commit

Permalink
Merge pull request #77 from ComputerScienceHouse/develop
Browse files Browse the repository at this point in the history
Version 3.0.3
  • Loading branch information
devinmatte authored Sep 10, 2018
2 parents 482aaff + 89de12c commit 93bb364
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 108 deletions.
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,48 @@
*Coming soon to a repo near you*
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.
fetch-results Fetches and prints the results from a given packet season.
```

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```
4 changes: 4 additions & 0 deletions frontend/scss/partials/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ body {
display: flex;
justify-content: space-between;
}

.right-align {
float: right;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packet/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.0.2"
__version__ = "3.0.3"
49 changes: 47 additions & 2 deletions packet/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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=23, minute=59)) + 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())
Expand Down Expand Up @@ -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()))
1 change: 0 additions & 1 deletion packet/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 0 additions & 11 deletions packet/member.py

This file was deleted.

2 changes: 1 addition & 1 deletion packet/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
75 changes: 58 additions & 17 deletions packet/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -27,25 +21,22 @@ 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
else:
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())
Expand All @@ -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)
Expand All @@ -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()
6 changes: 3 additions & 3 deletions packet/routes/freshmen.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -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)


Expand All @@ -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']
Expand Down
2 changes: 1 addition & 1 deletion packet/static/js/tables.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ $(document).ready(function () {

$('#active_packets_table').DataTable({
"searching": true,
"order": [[2, 'desc']],
"order": [],
"paging": false,
"info": false,
"columnDefs": [
Expand Down
Loading

0 comments on commit 93bb364

Please sign in to comment.