Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2a752dd
Adding 500 and 404 pages
devinmatte Mar 9, 2020
d8244e3
Fixing linting issues
devinmatte Mar 9, 2020
96fbe3e
Merge Custom Error Pages (#200)
mxmeinhold Mar 9, 2020
44076e3
Add pylint_quotes and pylint/routes to readme
mxmeinhold May 17, 2020
2b92c8e
Update pylint task in gulp
mxmeinhold May 17, 2020
31a6ad1
Add docker build to travis
mxmeinhold May 17, 2020
0fa696a
Move pylint to grunt lint task
mxmeinhold May 17, 2020
6e1265e
Update pylint command, ci for docker build (#202)
mxmeinhold May 25, 2020
ce7d037
Move stats methods to stats.py
mxmeinhold Jun 27, 2020
68e4dd3
Create packet aggregate signatures graph
mxmeinhold Jun 27, 2020
7db9966
Add links from packet to graphs and back
mxmeinhold Aug 16, 2020
176d31a
Initial Admin UI
devinmatte Aug 18, 2020
c94a2d5
Adding sync command that works, and functioning admin pages
devinmatte Aug 19, 2020
b9c3a6d
Merge pull request #205 from mxmeinhold/stats
mxmeinhold Aug 19, 2020
6c94112
Fixing for frosh
devinmatte Aug 19, 2020
e32a904
Fixing pylint
devinmatte Aug 19, 2020
e1ab5a3
Merge remote-tracking branch 'upstream/develop' into admin_ui
devinmatte Aug 19, 2020
4a900d9
Fixing pylint error
devinmatte Aug 20, 2020
b562c5e
Merge pull request #206 from devinmatte/admin_ui
devinmatte Aug 20, 2020
84fa5d5
Fixing api, adding sync
devinmatte Aug 20, 2020
da53e55
Merge pull request #207 from devinmatte/admin_ui_fixes
mxmeinhold Aug 20, 2020
b29e178
Bump lodash from 4.17.15 to 4.17.19 (#204)
dependabot[bot] Aug 20, 2020
79afcbd
Fixing UI
devinmatte Aug 20, 2020
c38c8e9
Bump to 3.5.0
mxmeinhold Aug 21, 2020
e95ad53
Yarn upgrade
devinmatte Aug 21, 2020
77c248c
Admin for evals only
devinmatte Aug 21, 2020
3a4d8e2
Remove unused ldap_is_rtp import from utils
mxmeinhold Aug 21, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ ENV/
# JetBrains
.idea/

# vscode
.vscode

# Configurations
config.py

Expand All @@ -128,3 +131,5 @@ packet/static/safari-pinned-tab.svg
packet/static/site.webmanifest
faviconData.json

# csvs
*.csv
10 changes: 9 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
language: python
python:
- "3.7"
services:
- "docker"

install:
- "pip install -r requirements.txt"
- "curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash"
- "nvm install"
- "nvm use"
- "npm install -g gulp"
- "npm install"
script:
- "pylint --load-plugins pylint_quotes packet/routes packet"
- "gulp lint"
- "docker build -t packet ."
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ fail blocking you from merging. To make your life easier just run it before maki

To run pylint use this command:
```bash
pylint packet
pylint --load-plugins pylint_quotes packet/routes packet
```

All python files should have a top-level docstring explaining the contents of the file and complex functions should
Expand Down
3 changes: 3 additions & 0 deletions frontend/scss/components/code.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pre{
white-space: pre-wrap;
}
1 change: 1 addition & 0 deletions frontend/scss/packet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ $csh-pink: #b0197e;
@import "components/buttons";
@import "components/signatures";
@import "components/badges";
@import "components/code";
6 changes: 5 additions & 1 deletion frontend/scss/partials/_global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,8 @@ tr {

.eval-user-img {
border-radius: 50%;
}
}

select.form-control:not([size]):not([multiple]) {
height: calc(3rem + 2px);
}
3 changes: 2 additions & 1 deletion gulpfile.js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ requireDir('./tasks', {recurse: true});

// Default task
gulp.task('default', gulp.parallel('css', 'js'));
gulp.task('production', gulp.parallel('css', 'js', 'generate-favicon', 'pylint'));
gulp.task('production', gulp.parallel('css', 'js', 'generate-favicon'));
gulp.task('lint', gulp.parallel('pylint'));
2 changes: 1 addition & 1 deletion gulpfile.js/tasks/pylint.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const gulp = require('gulp');
const exec = require('child_process').exec;

let pylintTask = (cb) => {
exec('pylint packet', function (err, stdout, stderr) {
exec('pylint --load-plugins pylint_quotes packet/routes packet', function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
cb(err);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "CSH Packet",
"name": "csh-packet",
"version": "3.4.0",
"version": "3.5.0",
"description": "A web app implementation of the CSH introductory packet.",
"bugs": {
"url": "https://github.com/ComputerScienceHouse/packet/issues",
Expand All @@ -20,10 +20,10 @@
"devDependencies": {
"gulp": "^4.0.2",
"gulp-clean-css": "^4.2.0",
"gulp-minify": "^3.1.0",
"gulp-real-favicon": "^0.3.2",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.2",
"gulp-minify": "^3.1.0",
"require-dir": "^1.2.0"
}
}
1 change: 1 addition & 0 deletions packet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

if app.config['REALM'] == 'csh':
from .routes import upperclassmen
from .routes import admin
else:
from .routes import freshmen

Expand Down
140 changes: 6 additions & 134 deletions packet/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@
import sys

from secrets import token_hex
from datetime import datetime, time, timedelta
from datetime import datetime, time
import csv
import click

from packet.mail import send_start_packet_mail
from packet.notifications import packet_starting_notification, packets_starting_notification
from . import app, db
from .models import Freshman, Packet, FreshSignature, UpperSignature, MiscSignature
from .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
from .models import Packet, FreshSignature, UpperSignature, MiscSignature
from .utils import sync_freshman, create_new_packets, sync_with_ldap


@app.cli.command('create-secret')
Expand Down Expand Up @@ -66,40 +62,7 @@ def sync_freshmen(freshmen_csv):
freshmen_in_csv = parse_csv(freshmen_csv)

print('Syncing contents with the DB...')
freshmen_in_db = {freshman.rit_username: freshman for freshman in Freshman.query.all()}

for csv_freshman in freshmen_in_csv.values():
if csv_freshman.rit_username not in freshmen_in_db:
# This is a new freshman so add them to the DB
freshmen_in_db[csv_freshman.rit_username] = Freshman(rit_username=csv_freshman.rit_username,
name=csv_freshman.name, onfloor=csv_freshman.onfloor)
db.session.add(freshmen_in_db[csv_freshman.rit_username])
else:
# This freshman is already in the DB so just update them
freshmen_in_db[csv_freshman.rit_username].onfloor = csv_freshman.onfloor
freshmen_in_db[csv_freshman.rit_username].name = csv_freshman.name

# Update all freshmen entries that represent people who are no longer freshmen
for freshman in filter(lambda freshman: freshman.rit_username not in freshmen_in_csv, freshmen_in_db.values()):
freshman.onfloor = False

# Update the freshmen signatures of each open or future packet
for packet in Packet.query.filter(Packet.end > datetime.now()).all():
# Handle the freshmen that are no longer onfloor
for fresh_sig in filter(lambda fresh_sig: not fresh_sig.freshman.onfloor, packet.fresh_signatures):
FreshSignature.query.filter_by(packet_id=fresh_sig.packet_id,
freshman_username=fresh_sig.freshman_username).delete()

# Add any new onfloor freshmen
# pylint: disable=cell-var-from-loop
current_fresh_sigs = set(map(lambda fresh_sig: fresh_sig.freshman_username, packet.fresh_signatures))
for csv_freshman in filter(lambda csv_freshman: csv_freshman.rit_username not in current_fresh_sigs and
csv_freshman.onfloor and
csv_freshman.rit_username != packet.freshman_username,
freshmen_in_csv.values()):
db.session.add(FreshSignature(packet=packet, freshman=freshmen_in_db[csv_freshman.rit_username]))

db.session.commit()
sync_freshman(freshmen_in_csv)
print('Done!')


Expand All @@ -115,46 +78,8 @@ def create_packets(freshmen_csv):

# Collect the necessary data
base_date = input_date('Input the first day of packet season')
start = datetime.combine(base_date, packet_start_time)
end = datetime.combine(base_date, packet_end_time) + timedelta(days=14)

print('Fetching data from LDAP...')
all_upper = list(filter(
lambda member: not ldap_is_intromember(member) and not ldap_is_on_coop(member), ldap_get_active_members()))

rtp = ldap_get_active_rtps()
three_da = ldap_get_3das()
webmaster = ldap_get_webmasters()
c_m = ldap_get_constitutional_maintainers()
drink = ldap_get_drink_admins()

# Packet starting notifications
packets_starting_notification(start)

# Create the new packets and the signatures for each freshman in the given CSV
freshmen_in_csv = parse_csv(freshmen_csv)
print('Creating DB entries and sending emails...')
for freshman in Freshman.query.filter(Freshman.rit_username.in_(freshmen_in_csv)).all():
packet = Packet(freshman=freshman, start=start, end=end)
db.session.add(packet)
send_start_packet_mail(packet)
packet_starting_notification(packet)

for member in all_upper:
sig = UpperSignature(packet=packet, member=member.uid)
sig.eboard = ldap_get_eboard_role(member)
sig.active_rtp = member.uid in rtp
sig.three_da = member.uid in three_da
sig.webmaster = member.uid in webmaster
sig.c_m = member.uid in c_m
sig.drink_admin = member.uid in drink
db.session.add(sig)

for onfloor_freshman in Freshman.query.filter_by(onfloor=True).filter(Freshman.rit_username !=
freshman.rit_username).all():
db.session.add(FreshSignature(packet=packet, freshman=onfloor_freshman))

db.session.commit()
create_new_packets(base_date, freshmen_in_csv)
print('Done!')


Expand All @@ -163,60 +88,7 @@ def ldap_sync():
"""
Updates the upper and misc sigs in the DB to match ldap.
"""
print('Fetching data from LDAP...')
all_upper = {member.uid: member for member in filter(
lambda member: not ldap_is_intromember(member) and not ldap_is_on_coop(member), ldap_get_active_members())}

rtp = ldap_get_active_rtps()
three_da = ldap_get_3das()
webmaster = ldap_get_webmasters()
c_m = ldap_get_constitutional_maintainers()
drink = ldap_get_drink_admins()

print('Applying updates to the DB...')
for packet in Packet.query.filter(Packet.end > datetime.now()).all():
# Update the role state of all UpperSignatures
for sig in filter(lambda sig: sig.member in all_upper, packet.upper_signatures):
sig.eboard = ldap_get_eboard_role(all_upper[sig.member])
sig.active_rtp = sig.member in rtp
sig.three_da = sig.member in three_da
sig.webmaster = sig.member in webmaster
sig.c_m = sig.member in c_m
sig.drink_admin = sig.member in drink

# Migrate UpperSignatures that are from accounts that are not active anymore
for sig in filter(lambda sig: sig.member not in all_upper, packet.upper_signatures):
UpperSignature.query.filter_by(packet_id=packet.id, member=sig.member).delete()
if sig.signed:
sig = MiscSignature(packet=packet, member=sig.member)
db.session.add(sig)

# Migrate MiscSignatures that are from accounts that are now active members
for sig in filter(lambda sig: sig.member in all_upper, packet.misc_signatures):
MiscSignature.query.filter_by(packet_id=packet.id, member=sig.member).delete()
sig = UpperSignature(packet=packet, member=sig.member, signed=True)
sig.eboard = ldap_get_eboard_role(all_upper[sig.member])
sig.active_rtp = sig.member in rtp
sig.three_da = sig.member in three_da
sig.webmaster = sig.member in webmaster
sig.c_m = sig.member in c_m
sig.drink_admin = sig.member in drink
db.session.add(sig)

# Create UpperSignatures for any new active members
# pylint: disable=cell-var-from-loop
upper_sigs = set(map(lambda sig: sig.member, packet.upper_signatures))
for member in filter(lambda member: member not in upper_sigs, all_upper):
sig = UpperSignature(packet=packet, member=member)
sig.eboard = ldap_get_eboard_role(all_upper[sig.member])
sig.active_rtp = sig.member in rtp
sig.three_da = sig.member in three_da
sig.webmaster = sig.member in webmaster
sig.c_m = sig.member in c_m
sig.drink_admin = sig.member in drink
db.session.add(sig)

db.session.commit()
sync_with_ldap()
print('Done!')


Expand Down
4 changes: 2 additions & 2 deletions packet/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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']
Expand Down
10 changes: 9 additions & 1 deletion packet/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,23 @@ def ldap_get_eboard_role(member):

return return_val

# Status checkers

# Status checkers
def ldap_is_eboard(member):
"""
:param member: A CSHMember instance
"""
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
Expand Down
7 changes: 7 additions & 0 deletions packet/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Comment on lines +52 to +57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the couple of characters saved by this helper method is worth the extra level of abstraction/indirection. Not a big deal either way though.



class Packet(db.Model):
__tablename__ = 'packet'
Expand Down
43 changes: 43 additions & 0 deletions packet/routes/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from flask import render_template

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/packets')
@log_cache
@packet_auth
@admin_auth
@before_request
@log_time
Comment on lines +10 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting to be a lot of decorators. I'd consider refactoring the auth and logging ones to be function-style decorators so you can make things more compact and readable. Maybe something like this:

@app.route('/admin/packets')
@with_logs(TIME, CACHE)
@require_auth(ADMIN, PACKET)
@before_request

def admin_packets(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)

return render_template('admin_packets.html',
open_packets=open_packets,
info=info)


@app.route('/admin/freshmen')
@log_cache
@packet_auth
@admin_auth
@before_request
@log_time
def admin_freshmen(info=None):
all_freshmen = Freshman.get_all()

return render_template('admin_freshmen.html',
all_freshmen=all_freshmen,
info=info)
Loading