Skip to content

Commit ea77e7d

Browse files
Speedup pt 2 (#469)
* Added uids and fids as indicies in the database to speed up queries * speedup member management * use user dict instead of ldap where possible * fix linting :) * fix eboard group names SIGH * moved migrations out of dockerfile and updated documentation
1 parent d3f014c commit ea77e7d

File tree

17 files changed

+265
-152
lines changed

17 files changed

+265
-152
lines changed

README.md

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,103 @@ A comprehensive membership evaluations solution for Computer Science House.
88
Development
99
-----------
1010

11-
To run the application, you must have the latest version of [Python 3](https://www.python.org/downloads/) and [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) installed. Once you have those installed, create a new virtualenv and install the Python dependencies:
11+
### Config
12+
13+
You must create `config.py` in the top-level directory with the appropriate credentials for the application to run. See `config.env.py` for an example.
1214

15+
#### Add OIDC Config
16+
Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth
17+
```py
18+
# OIDC Config
19+
OIDC_ISSUER = "https://sso.csh.rit.edu/auth/realms/csh"
20+
OIDC_CLIENT_CONFIG = {
21+
'client_id': '',
22+
'client_secret': '',
23+
'post_logout_redirect_uris': ['http://0.0.0.0:6969/logout']
24+
}
1325
```
26+
27+
#### Database
28+
You can either develop using the dev database, or use the local database provided in the docker compose file
29+
30+
Using the local database is detailed below, but both options will require the dev database password, so you will have to ask an RTP for this too
31+
32+
### Run (Without Docker)
33+
34+
To run the application without using containers, you must have the latest version of [Python 3](https://www.python.org/downloads/) and [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) installed. Once you have those installed, create a new virtualenv and install the Python dependencies:
35+
36+
```sh
1437
virtualenv .conditionalenv -p `which python3`
1538
source .conditionalenv/bin/activate
1639
pip install -r requirements.txt
17-
export FLASK_APP=app.py
1840
```
1941

20-
In addition, you must have Node, NPM, and Gulp CLI installed to properly execute the asset pipeline. If you don't have Node installed, we recommending installing with [NVM](https://github.com/creationix/nvm):
42+
In addition, you must have Node, NPM, and Weback CLI installed to properly execute the asset pipeline. If you don't have Node installed, we recommending installing with [NVM](https://github.com/creationix/nvm):
2143

22-
```
44+
```sh
2345
nvm install
2446
nvm use
25-
npm install -g gulp
47+
npm install -g webpack
2648
```
2749

28-
Then, install the pipeline and frontend dependencies:
50+
Then, install the pipeline and frontend dependencies: (do this in the `frontend` directory)
2951

30-
```
52+
```sh
3153
npm install
3254
```
3355

34-
### Config
56+
Once you have all of the dependencies installed, run
3557

36-
You must create `config.py` in the top-level directory with the appropriate credentials for the application to run. See `config.sample.py` for an example.
58+
```sh
59+
npm webpack
60+
```
3761

38-
#### Add OIDC Config
39-
Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth
62+
This will build the frontend assets and put them in the correct place for use with flask
63+
64+
Finally, start the flask app with `gunicorn`
65+
66+
```sh
67+
gunicorn
4068
```
41-
# OIDC Config
42-
OIDC_ISSUER = "https://sso.csh.rit.edu/auth/realms/csh"
43-
OIDC_CLIENT_CONFIG = {
44-
'client_id': '',
45-
'client_secret': '',
46-
'post_logout_redirect_uris': ['http://0.0.0.0:6969/logout']
47-
}
69+
70+
or
71+
72+
```sh
73+
python -m gunicorn
4874
```
4975

50-
### Run
76+
### Run (containerized)
5177

52-
Once you have all of the dependencies installed, simply run:
78+
It is likely easier to use containers like `podman` or `docker` or the corresponding compose file
5379

54-
```
55-
npm start
80+
With podman, I have been using
81+
82+
```sh
83+
podman compose up --force-recreate --build
5684
```
5785

58-
This will run the asset pipeline, start the Python server, and start BrowserSync. Your default web browser will open automatically. If it doesn't, navigate to `http://127.0.0.1:3000`. Any changes made to the frontend files in `frontend` or the Jinja templates in `conditional/templates` will cause the browser to reload automatically.
86+
Which can be restarted every time changes are made
5987

6088
### Dependencies
6189

6290
To add new dependencies, add them to `requirements.in` and then run `pip-compile requirements.in` to produce a new locked `requirements.txt`. Do not edit `requirements.txt` directly as it will be overwritten by future PRs.
6391

6492
### Local database
6593

66-
You can run the database locally using the docker compose, make sure to upgrade it as explained below
94+
You can run the database locally using the docker compose
6795

6896
To populate it with dev data for example, you can use the command
6997

70-
```
71-
PGPASSWORD='[DB PASSWORD]' pg_dump -h postgres.csh.rit.edu -p 5432 -U conditional-dev conditional-dev | PGPASSWORD='fancypantspassword' psql -h localhost -p 5432 -U conditional conditional
98+
```sh
99+
PGPASSWORD='[DB PASSWORD]' pg_dump -h postgres.csh.rit.edu -p 5432 -U conditionaldev conditionaldev | PGPASSWORD='fancypantspassword' psql -h localhost -p 5432 -U conditional conditional
72100
```
73101

74102
This can be helpful for changing the database schema
75103

76-
NOTE: to use flask db commands with a database running in the compose file, you will have to update your url to point to localhost, not conditional-postgres
104+
To run migration commands in the local database, you can run the commands inside the docker container. Any migrations created will also be in the local repository since migrations are mounted in the docker compose
105+
```sh
106+
podman exec conditional flask db upgrade
107+
```
77108

78109
### Database Migrations
79110

conditional/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from datetime import datetime
33

4+
from pstats import SortKey
45
import structlog
56
from csh_ldap import CSHLDAP
67
from flask import Flask, redirect, render_template, request, g
@@ -33,7 +34,8 @@
3334
if app.config['PROFILING']:
3435
app.wsgi_app = ProfilerMiddleware(
3536
app.wsgi_app,
36-
restrictions=[30]
37+
sort_by=('cumulative',),
38+
restrictions=[80]
3739
)
3840

3941
# Sentry setup
@@ -195,7 +197,7 @@ def route_errors(error, user_dict=None):
195197

196198
# Handle the case where the header isn't present
197199
if user_dict['username'] is not None:
198-
data['username'] = user_dict['account'].uid
200+
data['username'] = user_dict['username']
199201
data['name'] = user_dict['account'].cn
200202
else:
201203
data['username'] = "unknown"

conditional/blueprints/attendance.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from conditional.util.ldap import ldap_get_current_students
2222
from conditional.util.ldap import ldap_get_member
2323
from conditional.util.ldap import ldap_is_eboard
24-
from conditional.util.ldap import ldap_is_eval_director
24+
from conditional.util.user_dict import user_dict_is_eboard, user_dict_is_eval_director
2525

2626
logger = structlog.get_logger()
2727

@@ -160,7 +160,7 @@ def display_attendance_hm(user_dict=None):
160160
log = logger.new(request=request, auth_dict=user_dict)
161161
log.info('Display House Meeting Attendance Page')
162162

163-
if not ldap_is_eval_director(user_dict['account']):
163+
if not user_dict_is_eval_director(user_dict):
164164
return redirect("/dashboard")
165165

166166
return render_template('attendance_hm.html',
@@ -175,7 +175,7 @@ def display_attendance_hm(user_dict=None):
175175
def submit_committee_attendance(user_dict=None):
176176
log = logger.new(request=request, auth_dict=user_dict)
177177

178-
approved = ldap_is_eboard(user_dict['account'])
178+
approved = user_dict_is_eval_director(user_dict)
179179
post_data = request.get_json()
180180

181181
committee = post_data['committee']
@@ -211,7 +211,7 @@ def submit_seminar_attendance(user_dict=None):
211211
log = logger.new(request=request, auth_dict=user_dict)
212212
log.info('Submit Technical Seminar Attendance')
213213

214-
approved = ldap_is_eboard(user_dict['account'])
214+
approved = user_dict_is_eboard(user_dict)
215215

216216
post_data = request.get_json()
217217

@@ -248,7 +248,7 @@ def submit_house_attendance(user_dict=None):
248248

249249
# status: Attended | Excused | Absent
250250

251-
if not ldap_is_eval_director(user_dict['account']):
251+
if not user_dict_is_eval_director(user_dict):
252252
return "must be evals", 403
253253

254254
post_data = request.get_json()
@@ -289,7 +289,7 @@ def submit_house_attendance(user_dict=None):
289289
def alter_house_attendance(uid, hid, user_dict=None):
290290
log = logger.new(request=request, auth_dict=user_dict)
291291

292-
if not ldap_is_eval_director(user_dict['account']):
292+
if not user_dict_is_eval_director(user_dict):
293293
return "must be evals", 403
294294

295295
if not uid.isdigit():
@@ -319,7 +319,7 @@ def alter_house_attendance(uid, hid, user_dict=None):
319319
def alter_house_excuse(uid, hid, user_dict=None):
320320
log = logger.new(request=request, auth_dict=user_dict)
321321

322-
if not ldap_is_eval_director(user_dict['account']):
322+
if not user_dict_is_eval_director(user_dict):
323323
return "must be eval director", 403
324324

325325
post_data = request.get_json()
@@ -381,7 +381,7 @@ def get_seminar_attendees(meeting_id):
381381

382382
log = logger.new(request=request, auth_dict=user_dict)
383383

384-
if not ldap_is_eboard(user_dict['account']):
384+
if not user_dict_is_eboard(user_dict):
385385
return jsonify({"success": False, "error": "Not EBoard"}), 403
386386

387387

@@ -444,7 +444,7 @@ def alter_committee_attendance(cid, user_dict=None):
444444
log = logger.new(request=request, auth_dict=user_dict)
445445
log.info('Edit Committee Meeting Attendance')
446446

447-
if not ldap_is_eboard(user_dict['account']):
447+
if not user_dict_is_eboard(user_dict):
448448
return jsonify({"success": False, "error": "Not EBoard"}), 403
449449

450450
post_data = request.get_json()
@@ -476,7 +476,7 @@ def alter_seminar_attendance(sid, user_dict=None):
476476
log = logger.new(request=request, auth_dict=user_dict)
477477
log.info('Edit Technical Seminar Attendance')
478478

479-
if not ldap_is_eboard(user_dict['account']):
479+
if not user_dict_is_eboard(user_dict):
480480
return jsonify({"success": False, "error": "Not EBoard"}), 403
481481

482482
post_data = request.get_json()
@@ -559,7 +559,7 @@ def get_ts_attendees(cid, user_dict=None):
559559
log = logger.new(request=request, auth_dict=user_dict)
560560
log.info(f'Delete Committee Meeting {cid}')
561561

562-
if not ldap_is_eboard(user_dict['account']):
562+
if not user_dict_is_eboard(user_dict):
563563
return jsonify({"success": False, "error": "Not EBoard"}), 403
564564

565565
FreshmanCommitteeAttendance.query.filter(
@@ -582,7 +582,7 @@ def approve_cm(cid, user_dict=None):
582582
log = logger.new(request=request, auth_dict=user_dict)
583583
log.info(f'Approve Committee Meeting {cid} Attendance')
584584

585-
if not ldap_is_eboard(user_dict['account']):
585+
if not user_dict_is_eboard(user_dict):
586586
return jsonify({"success": False, "error": "Not EBoard"}), 403
587587

588588
CommitteeMeeting.query.filter(
@@ -600,7 +600,7 @@ def approve_ts(sid, user_dict=None):
600600
log = logger.new(request=request, auth_dict=user_dict)
601601
log.info(f'Approve Technical Seminar {sid} Attendance')
602602

603-
if not ldap_is_eboard(user_dict['account']):
603+
if not user_dict_is_eboard(user_dict):
604604
return jsonify({"success": False, "error": "Not EBoard"}), 403
605605

606606
TechnicalSeminar.query.filter(

conditional/blueprints/cache_management.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
from conditional.util.ldap import ldap_get_intro_members
1414
from conditional.util.ldap import ldap_get_member
1515
from conditional.util.ldap import ldap_get_onfloor_members
16-
from conditional.util.ldap import ldap_is_eval_director
17-
from conditional.util.ldap import ldap_is_rtp
16+
from conditional.util.user_dict import user_dict_is_eval_director, user_dict_is_rtp
1817

1918
logger = structlog.get_logger()
2019
cache_bp = Blueprint('cache_bp', __name__)
@@ -24,7 +23,7 @@
2423
@auth.oidc_auth("default")
2524
@get_user
2625
def restart_app(user_dict=None):
27-
if not ldap_is_rtp(user_dict['account']):
26+
if not user_dict_is_rtp(user_dict):
2827
return redirect("/dashboard")
2928

3029
log = logger.new(request=request, auth_dict=user_dict)
@@ -37,7 +36,7 @@ def restart_app(user_dict=None):
3736
@auth.oidc_auth("default")
3837
@get_user
3938
def clear_cache(user_dict=None):
40-
if not ldap_is_eval_director(user_dict['account']) and not ldap_is_rtp(user_dict['account']):
39+
if not user_dict_is_eval_director(user_dict) and not user_dict_is_rtp(user_dict):
4140
return redirect("/dashboard")
4241

4342
log = logger.new(request=request, auth_dict=user_dict)

conditional/blueprints/co_op.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
from conditional.util.member import req_cm
77
from conditional.util.auth import get_user
88
from conditional.util.flask import render_template
9-
from conditional.util.ldap import ldap_is_eval_director, ldap_is_current_student
109
from conditional.util.ldap import _ldap_add_member_to_group as ldap_add_member_to_group
1110
from conditional.util.ldap import _ldap_remove_member_from_group as ldap_remove_member_from_group
12-
from conditional.util.ldap import _ldap_is_member_of_group as ldap_is_member_of_group
11+
from conditional.util.user_dict import user_dict_is_current_student, user_dict_is_eval_director, user_dict_is_in_group
1312

1413
co_op_bp = Blueprint('co_op_bp', __name__)
1514

@@ -43,7 +42,7 @@ def submit_co_op_form(user_dict=None):
4342
semester = post_data['semester']
4443
if post_data['semester'] not in valid_semesters:
4544
return "Invalid semester submitted", 400
46-
if not ldap_is_current_student(user_dict['account']):
45+
if not user_dict_is_current_student(user_dict):
4746
return "Must be current student", 403
4847

4948
log.info(f'Submit {semester} Co-Op')
@@ -70,15 +69,15 @@ def submit_co_op_form(user_dict=None):
7069
def delete_co_op(uid, user_dict=None):
7170
log = logger.new(request=request, auth_dict=user_dict)
7271

73-
if not ldap_is_eval_director(user_dict['account']):
72+
if not user_dict_is_eval_director(user_dict):
7473
return "must be eval director", 403
7574

7675
log.info(f'Delete {uid}\'s Co-Op')
7776

7877
# Remove from corresponding co-op ldap group
79-
if ldap_is_member_of_group(user_dict['account'], 'fall_coop'):
78+
if user_dict_is_in_group(user_dict, 'fall_coop'):
8079
ldap_remove_member_from_group(user_dict['account'], 'fall_coop')
81-
if ldap_is_member_of_group(user_dict['account'], 'spring_coop'):
80+
if user_dict_is_in_group(user_dict, 'spring_coop'):
8281
ldap_remove_member_from_group(user_dict['account'], 'spring_coop')
8382

8483
CurrentCoops.query.filter(CurrentCoops.uid == uid, CurrentCoops.date_created > start_of_year()).delete()
@@ -97,7 +96,7 @@ def display_co_op_management(user_dict=None):
9796
log = logger.new(request=request, auth_dict=user_dict)
9897
log.info('Display Co-Op Management')
9998

100-
if not ldap_is_eval_director(user_dict['account']):
99+
if not user_dict_is_eval_director(user_dict):
101100
return "must be eval director", 403
102101

103102
co_op_list = [(member.semester, member.uid)

conditional/blueprints/conditional.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from conditional.models.models import Conditional, SpringEval, FreshmanEvalData
88
from conditional.util.auth import get_user
99
from conditional.util.flask import render_template
10-
from conditional.util.ldap import ldap_is_eval_director
10+
from conditional.util.user_dict import user_dict_is_eval_director
1111

1212
conditionals_bp = Blueprint('conditionals_bp', __name__)
1313

@@ -44,7 +44,7 @@ def display_conditionals(user_dict=None):
4444
def create_conditional(user_dict=None):
4545
log = logger.new(request=request, auth_dict=user_dict)
4646

47-
if not ldap_is_eval_director(user_dict['account']):
47+
if not user_dict_is_eval_director(user_dict):
4848
return "must be eval director", 403
4949

5050
post_data = request.get_json()
@@ -81,7 +81,7 @@ def create_conditional(user_dict=None):
8181
def conditional_review(user_dict=None):
8282
log = logger.new(request=request, auth_dict=user_dict)
8383

84-
if not ldap_is_eval_director(user_dict['account']):
84+
if not user_dict_is_eval_director(user_dict):
8585
return redirect("/dashboard", code=302)
8686

8787
post_data = request.get_json()
@@ -119,7 +119,7 @@ def conditional_delete(cid, user_dict=None):
119119
log = logger.new(request=request, auth_dict=user_dict)
120120
log.info(f'Delete conditional-{cid}')
121121

122-
if ldap_is_eval_director(user_dict['account']):
122+
if user_dict_is_eval_director(user_dict):
123123
Conditional.query.filter(
124124
Conditional.id == cid
125125
).delete()

0 commit comments

Comments
 (0)