Skip to content

Commit ed510e4

Browse files
committed
Merge branch 'develop' into bootstrap-5
2 parents 1c1554a + b2561c5 commit ed510e4

26 files changed

Lines changed: 470 additions & 146 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ var/
2323
.installed.cfg
2424
*.egg
2525

26+
.env
27+
2628
# PyInstaller
2729
# Usually these files are written by a python script from a template
2830
# before PyInstaller builds the exe, so as to inject date/other infos into it.
@@ -69,6 +71,9 @@ target/
6971
# Cloud9 IDE
7072
/.c9
7173

74+
# VS Codepy
75+
.vscode/
76+
7277
# Sensitive project files
7378
/config.json
7479
/config.py

README.md

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,23 @@ A comprehensive membership evaluations solution for Computer Science House.
88
Development
99
-----------
1010

11-
### Config
11+
## Running (containerized)
1212

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.
14-
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-
}
25-
```
26-
27-
#### Add S3 Config
28-
An S3 bucket is used to store files that users upload (currently just for major project submissions). In order to have this work properly, you need to provide some credentials to the app.
13+
It is likely easier to use containers like `podman` or `docker` or the corresponding compose file
2914

30-
There are 2 ways that you can get the needed credentials.
31-
1. Reach out to an RTP for creds to the dev bucket
32-
2. Create your own bucket using [DEaDASS](https://deadass.csh.rit.edu/), and the site will give you the credentials you need.
15+
With podman, I have been using
3316

34-
```py
35-
S3_URI = env.get("S3_URI", "https://s3.csh.rit.edu")
36-
S3_BUCKET_ID = env.get("S3_BUCKET_ID", "major-project-media")
37-
AWS_ACCESS_KEY_ID = env.get("AWS_ACCESS_KEY_ID", "")
38-
AWS_SECRET_ACCESS_KEY = env.get("AWS_SECRET_ACCESS_KEY", "")
17+
```sh
18+
podman compose up --watch
3919
```
4020

41-
#### Database
42-
You can either develop using the dev database, or use the local database provided in the docker compose file
43-
44-
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
45-
46-
#### Forcing evals/rtp or anything else
47-
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
21+
If you want, you can run without auto rebuild using
22+
```sh
23+
podman compose up --force-recreate --build
24+
```
25+
Which can be restarted every time changes are made.
4826

49-
### Run (Without Docker)
27+
## Run (Without Docker)
5028

5129
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:
5230

@@ -90,30 +68,53 @@ or
9068
python -m gunicorn
9169
```
9270

93-
### Run (containerized)
71+
## Config
9472

95-
It is likely easier to use containers like `podman` or `docker` or the corresponding compose file
96-
97-
With podman, I have been using
73+
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.
9874

99-
```sh
100-
podman compose up --watch
75+
### Add OIDC Config
76+
Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth
77+
```py
78+
# OIDC Config
79+
OIDC_ISSUER = "https://sso.csh.rit.edu/auth/realms/csh"
80+
OIDC_CLIENT_CONFIG = {
81+
'client_id': '',
82+
'client_secret': '',
83+
'post_logout_redirect_uris': ['http://0.0.0.0:6969/logout']
84+
}
10185
```
10286

103-
If you want, you can run without compose support using
104-
```sh
105-
podman compose up --force-recreate --build
87+
### Add S3 Config
88+
An S3 bucket is used to store files that users upload (currently just for major project submissions). In order to have this work properly, you need to provide some credentials to the app.
89+
90+
There are 2 ways that you can get the needed credentials.
91+
1. Reach out to an RTP for creds to the dev bucket
92+
2. Create your own bucket using [DEaDASS](https://deadass.csh.rit.edu/), and the site will give you the credentials you need.
93+
94+
```py
95+
S3_URI = env.get("S3_URI", "https://s3.csh.rit.edu")
96+
S3_BUCKET_ID = env.get("S3_BUCKET_ID", "major-project-media")
97+
AWS_ACCESS_KEY_ID = env.get("AWS_ACCESS_KEY_ID", "")
98+
AWS_SECRET_ACCESS_KEY = env.get("AWS_SECRET_ACCESS_KEY", "")
10699
```
107100

108-
Which can be restarted every time changes are made
101+
### Database
102+
You can either develop using the dev database, or use the local database provided in the docker compose file
103+
104+
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
105+
106+
### Forcing evals/rtp or anything else
107+
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
108+
109+
109110

110-
### Dependencies
111+
## Dependencies
111112

112113
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.
113114

114-
### Database Stuff
115+
## Database Stuff
115116

116-
#### Local database
117+
### Local database
117118

118119
You can run the database locally using the docker compose
119120

@@ -130,7 +131,7 @@ To run migration commands in the local database, you can run the commands inside
130131
podman exec conditional flask db upgrade
131132
```
132133

133-
#### Database Migrations
134+
### Database Migrations
134135

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

conditional/blueprints/attendance.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
from conditional.models.models import FreshmanCommitteeAttendance
1111
from conditional.models.models import FreshmanHouseMeetingAttendance
1212
from conditional.models.models import FreshmanSeminarAttendance
13+
from conditional.models.models import FreshmanSeminarHost
1314
from conditional.models.models import HouseMeeting
1415
from conditional.models.models import MemberCommitteeAttendance
1516
from conditional.models.models import MemberHouseMeetingAttendance
1617
from conditional.models.models import MemberSeminarAttendance
18+
from conditional.models.models import MemberSeminarHost
1719
from conditional.models.models import TechnicalSeminar
1820
from conditional.util.auth import get_user
1921
from conditional.util.flask import render_template
@@ -218,6 +220,8 @@ def submit_seminar_attendance(user_dict=None):
218220
seminar_name = post_data['name']
219221
m_attendees = post_data['members']
220222
f_attendees = post_data['freshmen']
223+
m_host = post_data['member_host']
224+
f_host = post_data['freshman_host']
221225
timestamp = post_data['timestamp']
222226

223227
timestamp = datetime.strptime(timestamp, "%Y-%m-%d")
@@ -228,12 +232,24 @@ def submit_seminar_attendance(user_dict=None):
228232
db.session.refresh(seminar)
229233

230234
for m in m_attendees:
235+
if m in m_host:
236+
log.info(f'Skipped giving Attendence to {m} for {seminar_name}')
237+
continue
231238
log.info(f'Gave Attendance to {m} for {seminar_name}')
232239
db.session.add(MemberSeminarAttendance(m, seminar.id))
240+
for m in m_host:
241+
log.info(f'Gave Host Credit to {m} for {seminar_name}')
242+
db.session.add(MemberSeminarHost(m, seminar.id))
233243

234244
for f in f_attendees:
245+
if f in f_host:
246+
log.info(f'Skipped giving Attendance to freshman-{f} for {seminar_name}')
247+
continue
235248
log.info(f'Gave Attendance to freshman-{f} for {seminar_name}')
236249
db.session.add(FreshmanSeminarAttendance(f, seminar.id))
250+
for f in f_host:
251+
log.info(f'Gave Host Credit to freshman-{f} for {seminar_name}')
252+
db.session.add(FreshmanSeminarHost(f, seminar.id))
237253

238254
db.session.commit()
239255
return jsonify({"success": True}), 200
@@ -379,6 +395,18 @@ def get_seminar_attendees(meeting_id):
379395
FreshmanAccount.id == freshman).first().name)
380396
return attendees
381397

398+
def get_seminar_hosts(meeting_id):
399+
hosts = [ldap_get_member(a.uid).displayName for a in
400+
MemberSeminarHost.query.filter(
401+
MemberSeminarHost.seminar_id == meeting_id).all()]
402+
403+
for freshman in [a.fid for a in
404+
FreshmanSeminarHost.query.filter(
405+
FreshmanSeminarHost.seminar_id == meeting_id).all()]:
406+
hosts.append(FreshmanAccount.query.filter(
407+
FreshmanAccount.id == freshman).first().name)
408+
return hosts
409+
382410
log = logger.new(request=request, auth_dict=user_dict)
383411

384412
if not user_dict_is_eboard(user_dict):
@@ -402,6 +430,7 @@ def get_seminar_attendees(meeting_id):
402430
"name": m.name,
403431
"dt_obj": m.timestamp,
404432
"date": m.timestamp.strftime("%a %m/%d/%Y"),
433+
"hosts": get_seminar_hosts(m.id),
405434
"attendees": get_seminar_attendees(m.id),
406435
"type": "ts"
407436
} for m in TechnicalSeminar.query.filter(
@@ -419,6 +448,7 @@ def get_seminar_attendees(meeting_id):
419448
"name": m.name,
420449
"dt_obj": m.timestamp,
421450
"date": m.timestamp.strftime("%a %m/%d/%Y"),
451+
"hosts": get_seminar_hosts(m.id),
422452
"attendees": get_seminar_attendees(m.id)
423453
} for m in TechnicalSeminar.query.filter(
424454
TechnicalSeminar.timestamp > start_of_year(),
@@ -433,6 +463,7 @@ def get_seminar_attendees(meeting_id):
433463
history=all_meetings,
434464
pending_cm=pend_cm,
435465
pending_ts=pend_ts,
466+
all_ts=all_ts,
436467
num_pages=total_pages,
437468
current_page=int(page))
438469

@@ -483,19 +514,37 @@ def alter_seminar_attendance(sid, user_dict=None):
483514
meeting_id = sid
484515
m_attendees = post_data['members']
485516
f_attendees = post_data['freshmen']
517+
m_host = post_data['member_host']
518+
f_host = post_data['freshman_host']
486519

487520
FreshmanSeminarAttendance.query.filter(
488521
FreshmanSeminarAttendance.seminar_id == meeting_id).delete()
489522

523+
FreshmanSeminarHost.query.filter(
524+
FreshmanSeminarHost.seminar_id == meeting_id).delete()
525+
490526
MemberSeminarAttendance.query.filter(
491527
MemberSeminarAttendance.seminar_id == meeting_id).delete()
492528

529+
MemberSeminarHost.query.filter(
530+
MemberSeminarHost.seminar_id == meeting_id).delete()
531+
493532
for m in m_attendees:
533+
if m in m_host:
534+
continue
494535
db.session.add(MemberSeminarAttendance(m, meeting_id))
495536

537+
for m in m_host:
538+
db.session.add(MemberSeminarHost(m, meeting_id))
539+
496540
for f in f_attendees:
541+
if f in f_host:
542+
continue
497543
db.session.add(FreshmanSeminarAttendance(f, meeting_id))
498544

545+
for f in f_host:
546+
db.session.add(FreshmanSeminarHost(f, meeting_id))
547+
499548
db.session.flush()
500549
db.session.commit()
501550
return jsonify({"success": True}), 200
@@ -512,12 +561,25 @@ def get_cm_attendees(sid, user_dict=None):
512561
MemberSeminarAttendance.query.filter(
513562
MemberSeminarAttendance.seminar_id == sid).all()]
514563

564+
host = [{"value": a.uid,
565+
"display": ldap_get_member(a.uid).displayName
566+
} for a in
567+
MemberSeminarHost.query.filter(
568+
MemberSeminarHost.seminar_id == sid).all()]
569+
515570
for freshman in [{"value": a.fid,
516571
"display": FreshmanAccount.query.filter(FreshmanAccount.id == a.fid).first().name
517572
} for a in FreshmanSeminarAttendance.query.filter(
518573
FreshmanSeminarAttendance.seminar_id == sid).all()]:
519574
attendees.append(freshman)
520-
return jsonify({"attendees": attendees}), 200
575+
576+
for freshman in [{"value": a.fid,
577+
"display": FreshmanAccount.query.filter(FreshmanAccount.id == a.fid).first().name
578+
} for a in FreshmanSeminarHost.query.filter(
579+
FreshmanSeminarHost.seminar_id == sid).all()]:
580+
host.append(freshman)
581+
582+
return jsonify({"attendees": attendees, "host": host}), 200
521583

522584
log = logger.new(request=request, auth_dict=user_dict)
523585
log.info(f'Delete Technical Seminar {sid}')
@@ -527,8 +589,12 @@ def get_cm_attendees(sid, user_dict=None):
527589

528590
FreshmanSeminarAttendance.query.filter(
529591
FreshmanSeminarAttendance.seminar_id == sid).delete()
592+
FreshmanSeminarHost.query.filter(
593+
FreshmanSeminarHost.seminar_id == sid).delete()
530594
MemberSeminarAttendance.query.filter(
531595
MemberSeminarAttendance.seminar_id == sid).delete()
596+
MemberSeminarHost.query.filter(
597+
MemberSeminarHost.seminar_id == sid).delete()
532598
TechnicalSeminar.query.filter(
533599
TechnicalSeminar.id == sid).delete()
534600

conditional/blueprints/conditional.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +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_get_member, ldap_set_housingpoints
1011
from conditional.util.user_dict import user_dict_is_eval_director
1112

1213
conditionals_bp = Blueprint('conditionals_bp', __name__)
@@ -91,6 +92,7 @@ def conditional_review(user_dict=None):
9192
log.info(f'Updated conditional-{cid} to {status}')
9293
conditional = Conditional.query.filter(Conditional.id == cid)
9394
cond_obj = conditional.first()
95+
uid = cond_obj.uid
9496

9597
conditional.update(
9698
{
@@ -101,6 +103,12 @@ def conditional_review(user_dict=None):
101103
{
102104
'status': status
103105
})
106+
107+
if status == 'Passed':
108+
account = ldap_get_member(uid)
109+
hp = int(account.housingPoints)
110+
ldap_set_housingpoints(account, str(hp + 2))
111+
104112
elif cond_obj.i_evaluation:
105113
FreshmanEvalData.query.filter(FreshmanEvalData.id == cond_obj.i_evaluation).update(
106114
{

conditional/blueprints/dashboard.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from conditional.models.models import HouseMeeting
77
from conditional.models.models import MemberHouseMeetingAttendance
88
from conditional.models.models import MemberSeminarAttendance
9+
from conditional.models.models import MemberSeminarHost
910
from conditional.models.models import TechnicalSeminar
1011
from conditional.models.models import SpringEval
1112
from conditional.util.auth import get_user
@@ -106,11 +107,21 @@ def display_dashboard(user_dict=None):
106107
MemberSeminarAttendance.uid == uid,
107108
) if is_seminar_attendance_valid(s)]
108109
data['ts_total'] = len(t_seminars)
110+
# technical seminars hosted
111+
t_seminars_hosted = [s.seminar_id for s in
112+
MemberSeminarHost.query.filter(
113+
MemberSeminarHost.uid == uid,
114+
) if is_seminar_attendance_valid(s)]
115+
data['ts_hosted_total'] = len(t_seminars_hosted)
109116
attendance = [m.name for m in TechnicalSeminar.query.filter(
110117
TechnicalSeminar.id.in_(t_seminars)
111118
)]
119+
hosted = [m.name for m in TechnicalSeminar.query.filter(
120+
TechnicalSeminar.id.in_(t_seminars_hosted)
121+
)]
112122

113123
data['ts_list'] = attendance
124+
data['ts_hosted'] = hosted
114125

115126
spring['mp_status'] = "Failed"
116127
for mp in data['major_projects']:
@@ -165,6 +176,7 @@ def display_dashboard(user_dict=None):
165176
'status': gatekeep_result,
166177
'committee_meetings': gatekeep_info['c_meetings'],
167178
'technical_seminars': gatekeep_info['t_seminars'],
179+
'technical_seminars_hosted': gatekeep_info['t_seminars_hosted'],
168180
'hm_missed': gatekeep_info['h_meetings_missed']
169181
}
170182

0 commit comments

Comments
 (0)