Skip to content

Commit c94a2d5

Browse files
committed
Adding sync command that works, and functioning admin pages
1 parent 176d31a commit c94a2d5

File tree

12 files changed

+232
-133
lines changed

12 files changed

+232
-133
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,5 @@ packet/static/safari-pinned-tab.svg
131131
packet/static/site.webmanifest
132132
faviconData.json
133133

134+
# csvs
135+
*.csv

packet/commands.py

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .ldap import ldap_get_eboard_role, ldap_get_active_rtps, ldap_get_3das, ldap_get_webmasters, \
1717
ldap_get_drink_admins, ldap_get_constitutional_maintainers, ldap_is_intromember, ldap_get_active_members, \
1818
ldap_is_on_coop
19+
from .utils import sync_freshman
1920

2021

2122
@app.cli.command('create-secret')
@@ -66,40 +67,7 @@ def sync_freshmen(freshmen_csv):
6667
freshmen_in_csv = parse_csv(freshmen_csv)
6768

6869
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()
70+
sync_freshman(freshmen_in_csv)
10371
print('Done!')
10472

10573

packet/ldap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ 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

packet/routes/admin.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import csv
2-
import os
3-
4-
import requests
5-
from flask import render_template, redirect, request
6-
from werkzeug.utils import secure_filename
1+
from flask import render_template
72

83
from packet import app
94
from packet.models import Packet, Freshman
@@ -12,13 +7,13 @@
127
from packet.log_utils import log_cache, log_time
138

149

15-
@app.route('/admin/')
10+
@app.route('/admin/packets')
1611
@log_cache
1712
@packet_auth
1813
@admin_auth
1914
@before_request
2015
@log_time
21-
def admin(info=None):
16+
def admin_packets(info=None):
2217
open_packets = Packet.open_packets()
2318

2419
# Pre-calculate and store the return values of did_sign(), signatures_received(), and signatures_required()
@@ -29,9 +24,20 @@ def admin(info=None):
2924

3025
open_packets.sort(key=packet_sort_key, reverse=True)
3126

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):
3239
all_freshmen = Freshman.get_all()
3340

34-
return render_template('admin.html',
35-
open_packets=open_packets,
41+
return render_template('admin_freshmen.html',
3642
all_freshmen=all_freshmen,
3743
info=info)

packet/routes/api.py

Lines changed: 54 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@
88

99
from packet import app, db
1010
from packet.context_processors import get_rit_name
11-
from packet.commands import packet_start_time, packet_end_time
11+
from packet.commands import packet_start_time, packet_end_time, CSVFreshman
1212
from packet.ldap import ldap_get_eboard_role, ldap_get_active_rtps, ldap_get_3das, ldap_get_webmasters, \
1313
ldap_get_drink_admins, ldap_get_constitutional_maintainers, ldap_is_intromember, ldap_get_active_members, \
1414
ldap_is_on_coop, _ldap_is_member_of_group, ldap_get_member
1515
from packet.log_utils import log_time
1616
from packet.mail import send_report_mail, send_start_packet_mail
17-
from packet.utils import before_request, packet_auth, notify_slack
17+
from packet.utils import before_request, packet_auth, notify_slack, sync_freshman as sync_freshman_list
1818
from packet.models import Packet, MiscSignature, NotificationSubscription, Freshman, FreshSignature, UpperSignature
1919
from packet.notifications import packet_signed_notification, packet_100_percent_notification, \
20-
packet_starting_notification, packets_starting_notification
20+
packet_starting_notification, packets_starting_notification
21+
22+
23+
class POSTFreshman:
24+
def __init__(self, freshman):
25+
self.name = freshman['name'].strip()
26+
self.rit_username = freshman['rit_username'].strip()
27+
self.onfloor = freshman['onfloor'].strip() == 'TRUE'
2128

2229

2330
@app.route('/api/v1/freshmen', methods=['POST'])
@@ -40,43 +47,9 @@ def sync_freshman():
4047
if not _ldap_is_member_of_group(ldap_get_member(username), 'eboard-evaluations'):
4148
return 'Forbidden: not Evaluations Director', 403
4249

43-
freshmen = request.json
44-
results = list()
45-
46-
packets = Packet.query.filter(Packet.end > datetime.now()).all()
47-
48-
for freshman in freshmen:
49-
rit_username = freshman['rit_username']
50-
name = freshman['name']
51-
onfloor = freshman['onfloor']
52-
53-
frosh = Freshman.query.filter_by(rit_username=rit_username).first()
54-
if frosh:
55-
if onfloor and not frosh.onfloor:
56-
# Add new onfloor signature
57-
for packet in packets:
58-
db.session.add(FreshSignature(packet=packet, freshman=frosh))
59-
elif not onfloor and frosh.onfloor:
60-
# Remove outdated onfloor signature
61-
for packet in packets:
62-
FreshSignature.query.filter_by(packet_id=packet.id, freshman_username=frosh.rit_username).delete()
63-
64-
frosh.name = name
65-
frosh.onfloor = onfloor
66-
67-
results.append(f"'{name} ({rit_username})' updated")
68-
else:
69-
frosh = Freshman(rit_username=rit_username, name=name, onfloor=onfloor)
70-
db.session.add(frosh)
71-
if onfloor:
72-
# Add onfloor signature
73-
for packet in packets:
74-
db.session.add(FreshSignature(packet=packet, freshman=frosh))
75-
76-
results.append(f"Freshman '{name} ({rit_username})' created")
77-
78-
db.session.commit()
79-
return dumps(results), 200
50+
freshmen_in_post = {freshman.rit_username: freshman for freshman in map(POSTFreshman, request.json)}
51+
sync_freshman_list(freshmen_in_post)
52+
return dumps('Done'), 200
8053

8154

8255
@app.route('/api/v1/packets', methods=['POST'])
@@ -164,7 +137,7 @@ def get_packets_by_user(username: str) -> dict:
164137
return {packet.id: {
165138
'start': packet.start,
166139
'end': packet.end,
167-
} for packet in frosh.packets}
140+
} for packet in frosh.packets}
168141

169142

170143
@app.route('/api/v1/packets/<username>/newest', methods=['GET'])
@@ -178,13 +151,13 @@ def get_newest_packet_by_user(username: str) -> dict:
178151
packet = frosh.packets[-1]
179152

180153
return {
181-
packet.id: {
182-
'start': packet.start,
183-
'end': packet.end,
184-
'required': vars(packet.signatures_required()),
185-
'received': vars(packet.signatures_received()),
186-
}
187-
}
154+
packet.id: {
155+
'start': packet.start,
156+
'end': packet.end,
157+
'required': vars(packet.signatures_required()),
158+
'received': vars(packet.signatures_received()),
159+
}
160+
}
188161

189162

190163
@app.route('/api/v1/packet/<packet_id>', methods=['GET'])
@@ -197,9 +170,10 @@ def get_packet_by_id(packet_id: int) -> dict:
197170
packet = Packet.by_id(packet_id)
198171

199172
return {
200-
'required': vars(packet.signatures_required()),
201-
'received': vars(packet.signatures_received()),
202-
}
173+
'required': vars(packet.signatures_required()),
174+
'received': vars(packet.signatures_received()),
175+
}
176+
203177

204178
@app.route('/api/v1/sign/<packet_id>/', methods=['POST'])
205179
@packet_auth
@@ -259,7 +233,7 @@ def report(info):
259233
def packet_stats(packet_id):
260234
packet = Packet.by_id(packet_id)
261235

262-
dates = [packet.start.date() + timedelta(days=x) for x in range(0, (packet.end-packet.start).days + 1)]
236+
dates = [packet.start.date() + timedelta(days=x) for x in range(0, (packet.end - packet.start).days + 1)]
263237

264238
print(dates)
265239

@@ -280,15 +254,15 @@ def packet_stats(packet_id):
280254
total_stats = dict()
281255
for date in dates:
282256
total_stats[date.isoformat()] = {
283-
'upper': upper_stats[date],
284-
'fresh': fresh_stats[date],
285-
'misc': misc_stats[date],
286-
}
257+
'upper': upper_stats[date],
258+
'fresh': fresh_stats[date],
259+
'misc': misc_stats[date],
260+
}
287261

288262
return {
289-
'packet_id': packet_id,
290-
'dates': total_stats,
291-
}
263+
'packet_id': packet_id,
264+
'dates': total_stats,
265+
}
292266

293267

294268
def sig2dict(sig):
@@ -298,39 +272,39 @@ def sig2dict(sig):
298272
"""
299273
packet = Packet.by_id(sig.packet_id)
300274
return {
301-
'date': sig.updated.date(),
302-
'packet': {
303-
'id': packet.id,
304-
'freshman_username': packet.freshman_username,
305-
},
306-
}
275+
'date': sig.updated.date(),
276+
'packet': {
277+
'id': packet.id,
278+
'freshman_username': packet.freshman_username,
279+
},
280+
}
307281

308282

309283
@app.route('/api/v1/stats/upperclassman/<uid>')
310284
@packet_auth
311285
def upperclassman_stats(uid):
312-
313286
sigs = UpperSignature.query.filter(
314-
UpperSignature.signed,
315-
UpperSignature.member == uid
316-
).all() + MiscSignature.query.filter(MiscSignature.member == uid).all()
287+
UpperSignature.signed,
288+
UpperSignature.member == uid
289+
).all() + MiscSignature.query.filter(MiscSignature.member == uid).all()
317290

318291
sig_dicts = list(map(sig2dict, sigs))
319292

320293
dates = set(map(lambda sd: sd['date'], sig_dicts))
321294

322295
return {
323-
'member': uid,
324-
'signatures': {
325-
date.isoformat() : list(
326-
map(lambda sd: sd['packet'],
327-
filter(lambda sig, d=date: sig['date'] == d,
328-
sig_dicts
329-
)
330-
)
331-
) for date in dates
332-
}
333-
}
296+
'member': uid,
297+
'signatures': {
298+
date.isoformat(): list(
299+
map(lambda sd: sd['packet'],
300+
filter(lambda sig, d=date: sig['date'] == d,
301+
sig_dicts
302+
)
303+
)
304+
) for date in dates
305+
}
306+
}
307+
334308

335309
def commit_sig(packet, was_100, uid):
336310
packet_signed_notification(packet, uid)

packet/static/js/admin.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,14 @@ $(document).ready(function () {
3434
makePackets();
3535
});
3636

37+
$("#sync-freshmen").click(() => {
38+
syncFreshmen();
39+
})
40+
3741
});
3842

43+
// Is this gross, yes. Do I feel like cleaning it up yet, no.
44+
3945
let makePackets = () => {
4046
let freshmen = [];
4147
let fileUpload = document.getElementById("newPacketsFile");
@@ -77,3 +83,49 @@ let makePackets = () => {
7783
}
7884
}
7985
}
86+
87+
88+
let syncFreshmen = () => {
89+
let freshmen = [];
90+
let fileUpload = document.getElementById("currentFroshFile");
91+
let regex = /^([a-zA-Z0-9\s_\\.\-:])+(.csv|.txt)$/;
92+
if (regex.test(fileUpload.value.toLowerCase())) {
93+
if (typeof (FileReader) != "undefined") {
94+
let reader = new FileReader();
95+
reader.onload = (e) => {
96+
let rows = e.target.result.split("\n");
97+
for (let i = 0; i < rows.length; i++) {
98+
let cells = rows[i].split(",");
99+
if (cells.length > 1) {
100+
freshmen.push({
101+
rit_username: cells[3],
102+
name: cells[0],
103+
onfloor: cells[1]
104+
});
105+
}
106+
}
107+
if (freshmen.length >= 1) {
108+
$("#sync-freshmen").append("&nbsp;<span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>");
109+
$("#sync-freshmen").attr('disabled', true);
110+
fetch('/api/v1/freshmen',
111+
{
112+
method: 'POST',
113+
headers: {
114+
'Content-Type': 'application/json'
115+
},
116+
body: JSON.stringify(freshmen)
117+
}
118+
).then(response => {
119+
if (response.status < 300) {
120+
$('#sync-freshmen-modal').modal('hide');
121+
location.reload();
122+
} else {
123+
alert("There was an syncing freshmen")
124+
}
125+
})
126+
}
127+
}
128+
reader.readAsText(fileUpload.files[0]);
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)