Skip to content

Commit 33addbc

Browse files
Merge pull request #488 from ComputerScienceHouse/develop
Dev
2 parents 3f3563f + b4fe087 commit 33addbc

31 files changed

+764
-215
lines changed

.github/workflows/sonarqube.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ on:
44
push:
55
branches:
66
- develop
7+
- master
8+
pull_request:
79

810

911
jobs:

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ COPY --from=build-frontend /opt/conditional/conditional/static /opt/conditional/
4040

4141
RUN ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime
4242

43-
CMD ["sh", "-c", "gunicorn conditional:app --bind=0.0.0.0:${PORT} --access-logfile=- --timeout=256"]
43+
CMD ["sh", "-c", "gunicorn"]
44+

README.md

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,85 +8,126 @@ 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
```
14-
virtualenv .conditionalenv -p `which python3`
15-
source .conditionalenv/bin/activate
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+
#### Forcing evals/rtp or anything else
33+
All of the role checking is done in `conditional/utils/user_dict.py`, and you can change the various functions to `return True` for debugging
34+
35+
### Run (Without Docker)
36+
37+
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:
38+
39+
```sh
40+
virtualenv .venv
41+
source .venv/bin/activate
1642
pip install -r requirements.txt
17-
export FLASK_APP=app.py
1843
```
1944

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):
45+
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):
2146

22-
```
47+
```sh
2348
nvm install
2449
nvm use
25-
npm install -g gulp
50+
npm install -g webpack
2651
```
2752

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

30-
```
55+
```sh
3156
npm install
3257
```
3358

34-
### Config
59+
Once you have all of the dependencies installed, run
3560

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.
61+
```sh
62+
npm webpack
63+
```
3764

38-
#### Add OIDC Config
39-
Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth
65+
This will build the frontend assets and put them in the correct place for use with flask
66+
67+
Finally, start the flask app with `gunicorn`
68+
69+
```sh
70+
gunicorn
4071
```
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-
}
72+
73+
or
74+
75+
```sh
76+
python -m gunicorn
4877
```
4978

50-
### Run
79+
### Run (containerized)
5180

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

54-
```
55-
npm start
83+
With podman, I have been using
84+
85+
```sh
86+
podman compose up --force-recreate --build
5687
```
5788

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.
89+
Which can be restarted every time changes are made
5990

6091
### Dependencies
6192

6293
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.
6394

64-
### Local database
95+
### Database Stuff
6596

66-
You can run the database locally using the docker compose, make sure to upgrade it as explained below
97+
#### Local database
98+
99+
You can run the database locally using the docker compose
67100

68101
To populate it with dev data for example, you can use the command
69102

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
103+
```sh
104+
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
72105
```
73106

74107
This can be helpful for changing the database schema
75108

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
109+
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
110+
```sh
111+
podman exec conditional flask db upgrade
112+
```
77113

78-
### Database Migrations
114+
#### Database Migrations
79115

80116
If the database schema is changed after initializing the database, you must migrate it to the new schema by running:
81117

82-
```
118+
```sh
83119
flask db upgrade
120+
# or, to run it inside the container for use with local databases (DO THIS
121+
podman exec conditional flask db upgrade
84122
```
85123

124+
86125
At the same time, if you change the database schema, you must generate a new migration by running:
87126

88-
```
127+
```sh
89128
flask db migrate
129+
# or, to run it inside the container for use with local databases (DO THIS
130+
podman exec conditional flask db migrate
90131
```
91132

92133
The new migration script in `migrations/versions` should be verified before being committed, as Alembic may not detect every change you make to the models.
@@ -97,7 +138,7 @@ For more information, refer to the [Flask-Migrate](https://flask-migrate.readthe
97138

98139
Conditional includes a utility to facilitate data migrations from the old Evals DB. This isn't necessary to run Conditional. To perform this migration, run the following commands before starting the application:
99140

100-
```
141+
```sh
101142
pip install pymysql
102143
flask zoo
103144
```

conditional/__init__.py

Lines changed: 6 additions & 4 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
@@ -116,6 +118,7 @@ def database_processor(logger, log_method, event_dict): # pylint: disable=unuse
116118
from .blueprints.intro_evals_form import intro_evals_form_bp
117119
from .blueprints.housing import housing_bp
118120
from .blueprints.spring_evals import spring_evals_bp
121+
from .blueprints.gatekeep import gatekeep_bp
119122
from .blueprints.conditional import conditionals_bp
120123
from .blueprints.member_management import member_management_bp
121124
from .blueprints.slideshow import slideshow_bp
@@ -130,15 +133,14 @@ def database_processor(logger, log_method, event_dict): # pylint: disable=unuse
130133
app.register_blueprint(intro_evals_form_bp)
131134
app.register_blueprint(housing_bp)
132135
app.register_blueprint(spring_evals_bp)
136+
app.register_blueprint(gatekeep_bp)
133137
app.register_blueprint(conditionals_bp)
134138
app.register_blueprint(member_management_bp)
135139
app.register_blueprint(slideshow_bp)
136140
app.register_blueprint(cache_bp)
137141
app.register_blueprint(co_op_bp)
138142
app.register_blueprint(log_bp)
139143

140-
from .util.ldap import ldap_get_member
141-
142144

143145
@app.route('/<path:path>')
144146
def static_proxy(path):
@@ -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)

0 commit comments

Comments
 (0)