Skip to content

Commit 35fb475

Browse files
authored
Merge Version 3.5.0 (#208)
2 parents 1ff8d94 + 3a4d8e2 commit 35fb475

34 files changed

+1391
-750
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ ENV/
103103
# JetBrains
104104
.idea/
105105

106+
# vscode
107+
.vscode
108+
106109
# Configurations
107110
config.py
108111

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

134+
# csvs
135+
*.csv

.travis.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
language: python
22
python:
33
- "3.7"
4+
services:
5+
- "docker"
46

57
install:
68
- "pip install -r requirements.txt"
9+
- "curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash"
10+
- "nvm install"
11+
- "nvm use"
12+
- "npm install -g gulp"
13+
- "npm install"
714
script:
8-
- "pylint --load-plugins pylint_quotes packet/routes packet"
15+
- "gulp lint"
16+
- "docker build -t packet ."

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fail blocking you from merging. To make your life easier just run it before maki
106106

107107
To run pylint use this command:
108108
```bash
109-
pylint packet
109+
pylint --load-plugins pylint_quotes packet/routes packet
110110
```
111111

112112
All python files should have a top-level docstring explaining the contents of the file and complex functions should

frontend/scss/components/code.scss

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pre{
2+
white-space: pre-wrap;
3+
}

frontend/scss/packet.scss

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ $csh-pink: #b0197e;
77
@import "components/buttons";
88
@import "components/signatures";
99
@import "components/badges";
10+
@import "components/code";

frontend/scss/partials/_global.scss

+5-1
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,8 @@ tr {
103103

104104
.eval-user-img {
105105
border-radius: 50%;
106-
}
106+
}
107+
108+
select.form-control:not([size]):not([multiple]) {
109+
height: calc(3rem + 2px);
110+
}

gulpfile.js/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ requireDir('./tasks', {recurse: true});
1919

2020
// Default task
2121
gulp.task('default', gulp.parallel('css', 'js'));
22-
gulp.task('production', gulp.parallel('css', 'js', 'generate-favicon', 'pylint'));
22+
gulp.task('production', gulp.parallel('css', 'js', 'generate-favicon'));
23+
gulp.task('lint', gulp.parallel('pylint'));

gulpfile.js/tasks/pylint.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const gulp = require('gulp');
22
const exec = require('child_process').exec;
33

44
let pylintTask = (cb) => {
5-
exec('pylint packet', function (err, stdout, stderr) {
5+
exec('pylint --load-plugins pylint_quotes packet/routes packet', function (err, stdout, stderr) {
66
console.log(stdout);
77
console.log(stderr);
88
cb(err);

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"title": "CSH Packet",
33
"name": "csh-packet",
4-
"version": "3.4.0",
4+
"version": "3.5.0",
55
"description": "A web app implementation of the CSH introductory packet.",
66
"bugs": {
77
"url": "https://github.com/ComputerScienceHouse/packet/issues",
@@ -20,10 +20,10 @@
2020
"devDependencies": {
2121
"gulp": "^4.0.2",
2222
"gulp-clean-css": "^4.2.0",
23+
"gulp-minify": "^3.1.0",
2324
"gulp-real-favicon": "^0.3.2",
2425
"gulp-rename": "^1.4.0",
2526
"gulp-sass": "^4.0.2",
26-
"gulp-minify": "^3.1.0",
2727
"require-dir": "^1.2.0"
2828
}
2929
}

packet/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

8181
if app.config['REALM'] == 'csh':
8282
from .routes import upperclassmen
83+
from .routes import admin
8384
else:
8485
from .routes import freshmen
8586

packet/commands.py

+6-134
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
import sys
66

77
from secrets import token_hex
8-
from datetime import datetime, time, timedelta
8+
from datetime import datetime, time
99
import csv
1010
import click
1111

12-
from packet.mail import send_start_packet_mail
13-
from packet.notifications import packet_starting_notification, packets_starting_notification
1412
from . import app, db
15-
from .models import Freshman, Packet, FreshSignature, UpperSignature, MiscSignature
16-
from .ldap import ldap_get_eboard_role, ldap_get_active_rtps, ldap_get_3das, ldap_get_webmasters, \
17-
ldap_get_drink_admins, ldap_get_constitutional_maintainers, ldap_is_intromember, ldap_get_active_members, \
18-
ldap_is_on_coop
13+
from .models import Packet, FreshSignature, UpperSignature, MiscSignature
14+
from .utils import sync_freshman, create_new_packets, sync_with_ldap
1915

2016

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

6864
print('Syncing contents with the DB...')
69-
freshmen_in_db = {freshman.rit_username: freshman for freshman in Freshman.query.all()}
70-
71-
for csv_freshman in freshmen_in_csv.values():
72-
if csv_freshman.rit_username not in freshmen_in_db:
73-
# This is a new freshman so add them to the DB
74-
freshmen_in_db[csv_freshman.rit_username] = Freshman(rit_username=csv_freshman.rit_username,
75-
name=csv_freshman.name, onfloor=csv_freshman.onfloor)
76-
db.session.add(freshmen_in_db[csv_freshman.rit_username])
77-
else:
78-
# This freshman is already in the DB so just update them
79-
freshmen_in_db[csv_freshman.rit_username].onfloor = csv_freshman.onfloor
80-
freshmen_in_db[csv_freshman.rit_username].name = csv_freshman.name
81-
82-
# Update all freshmen entries that represent people who are no longer freshmen
83-
for freshman in filter(lambda freshman: freshman.rit_username not in freshmen_in_csv, freshmen_in_db.values()):
84-
freshman.onfloor = False
85-
86-
# Update the freshmen signatures of each open or future packet
87-
for packet in Packet.query.filter(Packet.end > datetime.now()).all():
88-
# Handle the freshmen that are no longer onfloor
89-
for fresh_sig in filter(lambda fresh_sig: not fresh_sig.freshman.onfloor, packet.fresh_signatures):
90-
FreshSignature.query.filter_by(packet_id=fresh_sig.packet_id,
91-
freshman_username=fresh_sig.freshman_username).delete()
92-
93-
# Add any new onfloor freshmen
94-
# pylint: disable=cell-var-from-loop
95-
current_fresh_sigs = set(map(lambda fresh_sig: fresh_sig.freshman_username, packet.fresh_signatures))
96-
for csv_freshman in filter(lambda csv_freshman: csv_freshman.rit_username not in current_fresh_sigs and
97-
csv_freshman.onfloor and
98-
csv_freshman.rit_username != packet.freshman_username,
99-
freshmen_in_csv.values()):
100-
db.session.add(FreshSignature(packet=packet, freshman=freshmen_in_db[csv_freshman.rit_username]))
101-
102-
db.session.commit()
65+
sync_freshman(freshmen_in_csv)
10366
print('Done!')
10467

10568

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

11679
# Collect the necessary data
11780
base_date = input_date('Input the first day of packet season')
118-
start = datetime.combine(base_date, packet_start_time)
119-
end = datetime.combine(base_date, packet_end_time) + timedelta(days=14)
120-
121-
print('Fetching data from LDAP...')
122-
all_upper = list(filter(
123-
lambda member: not ldap_is_intromember(member) and not ldap_is_on_coop(member), ldap_get_active_members()))
124-
125-
rtp = ldap_get_active_rtps()
126-
three_da = ldap_get_3das()
127-
webmaster = ldap_get_webmasters()
128-
c_m = ldap_get_constitutional_maintainers()
129-
drink = ldap_get_drink_admins()
130-
131-
# Packet starting notifications
132-
packets_starting_notification(start)
133-
134-
# Create the new packets and the signatures for each freshman in the given CSV
13581
freshmen_in_csv = parse_csv(freshmen_csv)
136-
print('Creating DB entries and sending emails...')
137-
for freshman in Freshman.query.filter(Freshman.rit_username.in_(freshmen_in_csv)).all():
138-
packet = Packet(freshman=freshman, start=start, end=end)
139-
db.session.add(packet)
140-
send_start_packet_mail(packet)
141-
packet_starting_notification(packet)
142-
143-
for member in all_upper:
144-
sig = UpperSignature(packet=packet, member=member.uid)
145-
sig.eboard = ldap_get_eboard_role(member)
146-
sig.active_rtp = member.uid in rtp
147-
sig.three_da = member.uid in three_da
148-
sig.webmaster = member.uid in webmaster
149-
sig.c_m = member.uid in c_m
150-
sig.drink_admin = member.uid in drink
151-
db.session.add(sig)
152-
153-
for onfloor_freshman in Freshman.query.filter_by(onfloor=True).filter(Freshman.rit_username !=
154-
freshman.rit_username).all():
155-
db.session.add(FreshSignature(packet=packet, freshman=onfloor_freshman))
156-
157-
db.session.commit()
82+
create_new_packets(base_date, freshmen_in_csv)
15883
print('Done!')
15984

16085

@@ -163,60 +88,7 @@ def ldap_sync():
16388
"""
16489
Updates the upper and misc sigs in the DB to match ldap.
16590
"""
166-
print('Fetching data from LDAP...')
167-
all_upper = {member.uid: member for member in filter(
168-
lambda member: not ldap_is_intromember(member) and not ldap_is_on_coop(member), ldap_get_active_members())}
169-
170-
rtp = ldap_get_active_rtps()
171-
three_da = ldap_get_3das()
172-
webmaster = ldap_get_webmasters()
173-
c_m = ldap_get_constitutional_maintainers()
174-
drink = ldap_get_drink_admins()
175-
176-
print('Applying updates to the DB...')
177-
for packet in Packet.query.filter(Packet.end > datetime.now()).all():
178-
# Update the role state of all UpperSignatures
179-
for sig in filter(lambda sig: sig.member in all_upper, packet.upper_signatures):
180-
sig.eboard = ldap_get_eboard_role(all_upper[sig.member])
181-
sig.active_rtp = sig.member in rtp
182-
sig.three_da = sig.member in three_da
183-
sig.webmaster = sig.member in webmaster
184-
sig.c_m = sig.member in c_m
185-
sig.drink_admin = sig.member in drink
186-
187-
# Migrate UpperSignatures that are from accounts that are not active anymore
188-
for sig in filter(lambda sig: sig.member not in all_upper, packet.upper_signatures):
189-
UpperSignature.query.filter_by(packet_id=packet.id, member=sig.member).delete()
190-
if sig.signed:
191-
sig = MiscSignature(packet=packet, member=sig.member)
192-
db.session.add(sig)
193-
194-
# Migrate MiscSignatures that are from accounts that are now active members
195-
for sig in filter(lambda sig: sig.member in all_upper, packet.misc_signatures):
196-
MiscSignature.query.filter_by(packet_id=packet.id, member=sig.member).delete()
197-
sig = UpperSignature(packet=packet, member=sig.member, signed=True)
198-
sig.eboard = ldap_get_eboard_role(all_upper[sig.member])
199-
sig.active_rtp = sig.member in rtp
200-
sig.three_da = sig.member in three_da
201-
sig.webmaster = sig.member in webmaster
202-
sig.c_m = sig.member in c_m
203-
sig.drink_admin = sig.member in drink
204-
db.session.add(sig)
205-
206-
# Create UpperSignatures for any new active members
207-
# pylint: disable=cell-var-from-loop
208-
upper_sigs = set(map(lambda sig: sig.member, packet.upper_signatures))
209-
for member in filter(lambda member: member not in upper_sigs, all_upper):
210-
sig = UpperSignature(packet=packet, member=member)
211-
sig.eboard = ldap_get_eboard_role(all_upper[sig.member])
212-
sig.active_rtp = sig.member in rtp
213-
sig.three_da = sig.member in three_da
214-
sig.webmaster = sig.member in webmaster
215-
sig.c_m = sig.member in c_m
216-
sig.drink_admin = sig.member in drink
217-
db.session.add(sig)
218-
219-
db.session.commit()
91+
sync_with_ldap()
22092
print('Done!')
22193

22294

packet/context_processors.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def get_roles(sig):
4343

4444

4545
# pylint: disable=bare-except
46-
@lru_cache(maxsize=128)
46+
@lru_cache(maxsize=256)
4747
def get_rit_name(username):
4848
try:
4949
freshman = Freshman.query.filter_by(rit_username=username).first()
@@ -53,7 +53,7 @@ def get_rit_name(username):
5353

5454

5555
# pylint: disable=bare-except
56-
@lru_cache(maxsize=128)
56+
@lru_cache(maxsize=256)
5757
def get_rit_image(username):
5858
if username:
5959
addresses = [username + '@rit.edu', username + '@g.rit.edu']

packet/ldap.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,23 @@ def ldap_get_eboard_role(member):
149149

150150
return return_val
151151

152-
# Status checkers
153152

153+
# Status checkers
154154
def ldap_is_eboard(member):
155155
"""
156156
:param member: A CSHMember instance
157157
"""
158158
return _ldap_is_member_of_group(member, 'eboard')
159159

160160

161+
def ldap_is_evals(member):
162+
return _ldap_is_member_of_group(member, 'eboard-evaluations')
163+
164+
165+
def ldap_is_rtp(member):
166+
return _ldap_is_member_of_group(member, 'rtp')
167+
168+
161169
def ldap_is_intromember(member):
162170
"""
163171
:param member: A CSHMember instance

packet/models.py

+7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ def by_username(cls, username: str):
4949
"""
5050
return cls.query.filter_by(rit_username=username).first()
5151

52+
@classmethod
53+
def get_all(cls):
54+
"""
55+
Helper method to get all freshmen easily
56+
"""
57+
return cls.query.all()
58+
5259

5360
class Packet(db.Model):
5461
__tablename__ = 'packet'

packet/routes/admin.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from flask import render_template
2+
3+
from packet import app
4+
from packet.models import Packet, Freshman
5+
from packet.routes.shared import packet_sort_key
6+
from packet.utils import before_request, packet_auth, admin_auth
7+
from packet.log_utils import log_cache, log_time
8+
9+
10+
@app.route('/admin/packets')
11+
@log_cache
12+
@packet_auth
13+
@admin_auth
14+
@before_request
15+
@log_time
16+
def admin_packets(info=None):
17+
open_packets = Packet.open_packets()
18+
19+
# Pre-calculate and store the return values of did_sign(), signatures_received(), and signatures_required()
20+
for packet in open_packets:
21+
packet.did_sign_result = packet.did_sign(info['uid'], app.config['REALM'] == 'csh')
22+
packet.signatures_received_result = packet.signatures_received()
23+
packet.signatures_required_result = packet.signatures_required()
24+
25+
open_packets.sort(key=packet_sort_key, reverse=True)
26+
27+
return render_template('admin_packets.html',
28+
open_packets=open_packets,
29+
info=info)
30+
31+
32+
@app.route('/admin/freshmen')
33+
@log_cache
34+
@packet_auth
35+
@admin_auth
36+
@before_request
37+
@log_time
38+
def admin_freshmen(info=None):
39+
all_freshmen = Freshman.get_all()
40+
41+
return render_template('admin_freshmen.html',
42+
all_freshmen=all_freshmen,
43+
info=info)

0 commit comments

Comments
 (0)