diff --git a/.gitignore b/.gitignore index 11c2a4c7..666a47c2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ var/ .installed.cfg *.egg +.env + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. @@ -69,6 +71,9 @@ target/ # Cloud9 IDE /.c9 +# VS Codepy +.vscode/ + # Sensitive project files /config.json /config.py diff --git a/README.md b/README.md index 96314d0b..6dfd4846 100644 --- a/README.md +++ b/README.md @@ -8,45 +8,23 @@ A comprehensive membership evaluations solution for Computer Science House. Development ----------- -### Config +## Running (containerized) -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. - -#### Add OIDC Config -Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth -```py -# OIDC Config -OIDC_ISSUER = "https://sso.csh.rit.edu/auth/realms/csh" -OIDC_CLIENT_CONFIG = { - 'client_id': '', - 'client_secret': '', - 'post_logout_redirect_uris': ['http://0.0.0.0:6969/logout'] -} -``` - -#### Add S3 Config -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. +It is likely easier to use containers like `podman` or `docker` or the corresponding compose file -There are 2 ways that you can get the needed credentials. -1. Reach out to an RTP for creds to the dev bucket -2. Create your own bucket using [DEaDASS](https://deadass.csh.rit.edu/), and the site will give you the credentials you need. +With podman, I have been using -```py -S3_URI = env.get("S3_URI", "https://s3.csh.rit.edu") -S3_BUCKET_ID = env.get("S3_BUCKET_ID", "major-project-media") -AWS_ACCESS_KEY_ID = env.get("AWS_ACCESS_KEY_ID", "") -AWS_SECRET_ACCESS_KEY = env.get("AWS_SECRET_ACCESS_KEY", "") +```sh +podman compose up --watch ``` -#### Database -You can either develop using the dev database, or use the local database provided in the docker compose file - -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 - -#### Forcing evals/rtp or anything else -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 +If you want, you can run without auto rebuild using +```sh +podman compose up --force-recreate --build +``` +Which can be restarted every time changes are made. -### Run (Without Docker) +## Run (Without Docker) 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: @@ -90,30 +68,53 @@ or python -m gunicorn ``` -### Run (containerized) +## Config -It is likely easier to use containers like `podman` or `docker` or the corresponding compose file - -With podman, I have been using +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. -```sh -podman compose up --watch +### Add OIDC Config +Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth +```py +# OIDC Config +OIDC_ISSUER = "https://sso.csh.rit.edu/auth/realms/csh" +OIDC_CLIENT_CONFIG = { + 'client_id': '', + 'client_secret': '', + 'post_logout_redirect_uris': ['http://0.0.0.0:6969/logout'] +} ``` -If you want, you can run without compose support using -```sh -podman compose up --force-recreate --build +### Add S3 Config +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. + +There are 2 ways that you can get the needed credentials. +1. Reach out to an RTP for creds to the dev bucket +2. Create your own bucket using [DEaDASS](https://deadass.csh.rit.edu/), and the site will give you the credentials you need. + +```py +S3_URI = env.get("S3_URI", "https://s3.csh.rit.edu") +S3_BUCKET_ID = env.get("S3_BUCKET_ID", "major-project-media") +AWS_ACCESS_KEY_ID = env.get("AWS_ACCESS_KEY_ID", "") +AWS_SECRET_ACCESS_KEY = env.get("AWS_SECRET_ACCESS_KEY", "") ``` -Which can be restarted every time changes are made +### Database +You can either develop using the dev database, or use the local database provided in the docker compose file + +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 + +### Forcing evals/rtp or anything else +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 + + -### Dependencies +## Dependencies 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. -### Database Stuff +## Database Stuff -#### Local database +### Local database You can run the database locally using the docker compose @@ -130,7 +131,7 @@ To run migration commands in the local database, you can run the commands inside podman exec conditional flask db upgrade ``` -#### Database Migrations +### Database Migrations If the database schema is changed after initializing the database, you must migrate it to the new schema by running: diff --git a/conditional/blueprints/attendance.py b/conditional/blueprints/attendance.py index 387b5b31..16c0f081 100644 --- a/conditional/blueprints/attendance.py +++ b/conditional/blueprints/attendance.py @@ -10,10 +10,12 @@ from conditional.models.models import FreshmanCommitteeAttendance from conditional.models.models import FreshmanHouseMeetingAttendance from conditional.models.models import FreshmanSeminarAttendance +from conditional.models.models import FreshmanSeminarHost from conditional.models.models import HouseMeeting from conditional.models.models import MemberCommitteeAttendance from conditional.models.models import MemberHouseMeetingAttendance from conditional.models.models import MemberSeminarAttendance +from conditional.models.models import MemberSeminarHost from conditional.models.models import TechnicalSeminar from conditional.util.auth import get_user from conditional.util.flask import render_template @@ -218,6 +220,8 @@ def submit_seminar_attendance(user_dict=None): seminar_name = post_data['name'] m_attendees = post_data['members'] f_attendees = post_data['freshmen'] + m_host = post_data['member_host'] + f_host = post_data['freshman_host'] timestamp = post_data['timestamp'] timestamp = datetime.strptime(timestamp, "%Y-%m-%d") @@ -228,12 +232,24 @@ def submit_seminar_attendance(user_dict=None): db.session.refresh(seminar) for m in m_attendees: + if m in m_host: + log.info(f'Skipped giving Attendence to {m} for {seminar_name}') + continue log.info(f'Gave Attendance to {m} for {seminar_name}') db.session.add(MemberSeminarAttendance(m, seminar.id)) + for m in m_host: + log.info(f'Gave Host Credit to {m} for {seminar_name}') + db.session.add(MemberSeminarHost(m, seminar.id)) for f in f_attendees: + if f in f_host: + log.info(f'Skipped giving Attendance to freshman-{f} for {seminar_name}') + continue log.info(f'Gave Attendance to freshman-{f} for {seminar_name}') db.session.add(FreshmanSeminarAttendance(f, seminar.id)) + for f in f_host: + log.info(f'Gave Host Credit to freshman-{f} for {seminar_name}') + db.session.add(FreshmanSeminarHost(f, seminar.id)) db.session.commit() return jsonify({"success": True}), 200 @@ -379,6 +395,18 @@ def get_seminar_attendees(meeting_id): FreshmanAccount.id == freshman).first().name) return attendees + def get_seminar_hosts(meeting_id): + hosts = [ldap_get_member(a.uid).displayName for a in + MemberSeminarHost.query.filter( + MemberSeminarHost.seminar_id == meeting_id).all()] + + for freshman in [a.fid for a in + FreshmanSeminarHost.query.filter( + FreshmanSeminarHost.seminar_id == meeting_id).all()]: + hosts.append(FreshmanAccount.query.filter( + FreshmanAccount.id == freshman).first().name) + return hosts + log = logger.new(request=request, auth_dict=user_dict) if not user_dict_is_eboard(user_dict): @@ -402,6 +430,7 @@ def get_seminar_attendees(meeting_id): "name": m.name, "dt_obj": m.timestamp, "date": m.timestamp.strftime("%a %m/%d/%Y"), + "hosts": get_seminar_hosts(m.id), "attendees": get_seminar_attendees(m.id), "type": "ts" } for m in TechnicalSeminar.query.filter( @@ -419,6 +448,7 @@ def get_seminar_attendees(meeting_id): "name": m.name, "dt_obj": m.timestamp, "date": m.timestamp.strftime("%a %m/%d/%Y"), + "hosts": get_seminar_hosts(m.id), "attendees": get_seminar_attendees(m.id) } for m in TechnicalSeminar.query.filter( TechnicalSeminar.timestamp > start_of_year(), @@ -433,6 +463,7 @@ def get_seminar_attendees(meeting_id): history=all_meetings, pending_cm=pend_cm, pending_ts=pend_ts, + all_ts=all_ts, num_pages=total_pages, current_page=int(page)) @@ -483,19 +514,37 @@ def alter_seminar_attendance(sid, user_dict=None): meeting_id = sid m_attendees = post_data['members'] f_attendees = post_data['freshmen'] + m_host = post_data['member_host'] + f_host = post_data['freshman_host'] FreshmanSeminarAttendance.query.filter( FreshmanSeminarAttendance.seminar_id == meeting_id).delete() + FreshmanSeminarHost.query.filter( + FreshmanSeminarHost.seminar_id == meeting_id).delete() + MemberSeminarAttendance.query.filter( MemberSeminarAttendance.seminar_id == meeting_id).delete() + MemberSeminarHost.query.filter( + MemberSeminarHost.seminar_id == meeting_id).delete() + for m in m_attendees: + if m in m_host: + continue db.session.add(MemberSeminarAttendance(m, meeting_id)) + for m in m_host: + db.session.add(MemberSeminarHost(m, meeting_id)) + for f in f_attendees: + if f in f_host: + continue db.session.add(FreshmanSeminarAttendance(f, meeting_id)) + for f in f_host: + db.session.add(FreshmanSeminarHost(f, meeting_id)) + db.session.flush() db.session.commit() return jsonify({"success": True}), 200 @@ -512,12 +561,25 @@ def get_cm_attendees(sid, user_dict=None): MemberSeminarAttendance.query.filter( MemberSeminarAttendance.seminar_id == sid).all()] + host = [{"value": a.uid, + "display": ldap_get_member(a.uid).displayName + } for a in + MemberSeminarHost.query.filter( + MemberSeminarHost.seminar_id == sid).all()] + for freshman in [{"value": a.fid, "display": FreshmanAccount.query.filter(FreshmanAccount.id == a.fid).first().name } for a in FreshmanSeminarAttendance.query.filter( FreshmanSeminarAttendance.seminar_id == sid).all()]: attendees.append(freshman) - return jsonify({"attendees": attendees}), 200 + + for freshman in [{"value": a.fid, + "display": FreshmanAccount.query.filter(FreshmanAccount.id == a.fid).first().name + } for a in FreshmanSeminarHost.query.filter( + FreshmanSeminarHost.seminar_id == sid).all()]: + host.append(freshman) + + return jsonify({"attendees": attendees, "host": host}), 200 log = logger.new(request=request, auth_dict=user_dict) log.info(f'Delete Technical Seminar {sid}') @@ -527,8 +589,12 @@ def get_cm_attendees(sid, user_dict=None): FreshmanSeminarAttendance.query.filter( FreshmanSeminarAttendance.seminar_id == sid).delete() + FreshmanSeminarHost.query.filter( + FreshmanSeminarHost.seminar_id == sid).delete() MemberSeminarAttendance.query.filter( MemberSeminarAttendance.seminar_id == sid).delete() + MemberSeminarHost.query.filter( + MemberSeminarHost.seminar_id == sid).delete() TechnicalSeminar.query.filter( TechnicalSeminar.id == sid).delete() diff --git a/conditional/blueprints/conditional.py b/conditional/blueprints/conditional.py index 74a4913f..6ba42e63 100644 --- a/conditional/blueprints/conditional.py +++ b/conditional/blueprints/conditional.py @@ -7,6 +7,7 @@ from conditional.models.models import Conditional, SpringEval, FreshmanEvalData from conditional.util.auth import get_user from conditional.util.flask import render_template +from conditional.util.ldap import ldap_get_member, ldap_set_housingpoints from conditional.util.user_dict import user_dict_is_eval_director conditionals_bp = Blueprint('conditionals_bp', __name__) @@ -91,6 +92,7 @@ def conditional_review(user_dict=None): log.info(f'Updated conditional-{cid} to {status}') conditional = Conditional.query.filter(Conditional.id == cid) cond_obj = conditional.first() + uid = cond_obj.uid conditional.update( { @@ -101,6 +103,12 @@ def conditional_review(user_dict=None): { 'status': status }) + + if status == 'Passed': + account = ldap_get_member(uid) + hp = int(account.housingPoints) + ldap_set_housingpoints(account, str(hp + 2)) + elif cond_obj.i_evaluation: FreshmanEvalData.query.filter(FreshmanEvalData.id == cond_obj.i_evaluation).update( { diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index 44a88037..0bd5f951 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -6,6 +6,7 @@ from conditional.models.models import HouseMeeting from conditional.models.models import MemberHouseMeetingAttendance from conditional.models.models import MemberSeminarAttendance +from conditional.models.models import MemberSeminarHost from conditional.models.models import TechnicalSeminar from conditional.models.models import SpringEval from conditional.util.auth import get_user @@ -106,11 +107,21 @@ def display_dashboard(user_dict=None): MemberSeminarAttendance.uid == uid, ) if is_seminar_attendance_valid(s)] data['ts_total'] = len(t_seminars) + # technical seminars hosted + t_seminars_hosted = [s.seminar_id for s in + MemberSeminarHost.query.filter( + MemberSeminarHost.uid == uid, + ) if is_seminar_attendance_valid(s)] + data['ts_hosted_total'] = len(t_seminars_hosted) attendance = [m.name for m in TechnicalSeminar.query.filter( TechnicalSeminar.id.in_(t_seminars) )] + hosted = [m.name for m in TechnicalSeminar.query.filter( + TechnicalSeminar.id.in_(t_seminars_hosted) + )] data['ts_list'] = attendance + data['ts_hosted'] = hosted spring['mp_status'] = "Failed" for mp in data['major_projects']: @@ -164,6 +175,7 @@ def display_dashboard(user_dict=None): 'status': gatekeep_result, 'committee_meetings': gatekeep_info['c_meetings'], 'technical_seminars': gatekeep_info['t_seminars'], + 'technical_seminars_hosted': gatekeep_info['t_seminars_hosted'], 'hm_missed': gatekeep_info['h_meetings_missed'] } diff --git a/conditional/blueprints/gatekeep.py b/conditional/blueprints/gatekeep.py index c7fb0ae6..1eba8668 100644 --- a/conditional/blueprints/gatekeep.py +++ b/conditional/blueprints/gatekeep.py @@ -4,7 +4,7 @@ from conditional import start_of_year, auth from conditional.models.models import CommitteeMeeting, HouseMeeting, MemberCommitteeAttendance, \ - MemberSeminarAttendance, TechnicalSeminar + MemberSeminarAttendance, MemberSeminarHost, TechnicalSeminar from conditional.models.models import MemberHouseMeetingAttendance from conditional.util.auth import get_user from conditional.util.flask import render_template @@ -23,7 +23,6 @@ def display_spring_evals(internal=False, user_dict=None): log.info('Display Gatekeep Status Listing') _, semester_start = get_semester_info() - active_members = ldap_get_active_members() cm_count = {row[0]: row[1] for row in MemberCommitteeAttendance.query.join( CommitteeMeeting, @@ -59,6 +58,23 @@ def display_spring_evals(internal=False, user_dict=None): MemberSeminarAttendance.uid ).all()} + ts_host_count = {row[0]: row[1] for row in MemberSeminarHost.query.join( + TechnicalSeminar, + MemberSeminarHost.seminar_id == TechnicalSeminar.id + ).with_entities( + MemberSeminarHost.uid, + TechnicalSeminar.timestamp, + TechnicalSeminar.approved, + ).filter( + TechnicalSeminar.approved, + TechnicalSeminar.timestamp >= semester_start + ).with_entities( + MemberSeminarHost.uid, + func.count(MemberSeminarHost.uid) #pylint: disable=not-callable + ).group_by( + MemberSeminarHost.uid + ).all()} + hm_missed = {row[0]: row[1] for row in MemberHouseMeetingAttendance.query.join( HouseMeeting, MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id @@ -73,7 +89,7 @@ def display_spring_evals(internal=False, user_dict=None): ).all()} gk_members = [] - for account in active_members: + for account in ldap_get_active_members(): uid = account.uid name = account.cn @@ -93,8 +109,10 @@ def display_spring_evals(internal=False, user_dict=None): cm_attended_count = cm_count.get(uid, 0) ts_attended_count = ts_count.get(uid, 0) + ts_hosted_count = ts_host_count.get(uid, 0) - passing = len(member_missed_hms) <= 1 and cm_attended_count >= 6 and ts_attended_count >= 2 + ts_passing = ts_attended_count >= 2 or ts_hosted_count >= 1 + passing = len(member_missed_hms) <= 1 and cm_attended_count >= 6 and ts_passing status = 'disenfranchised' @@ -107,8 +125,10 @@ def display_spring_evals(internal=False, user_dict=None): 'status': status, 'committee_meetings': cm_attended_count, 'technical_seminars': ts_attended_count, + 'technical_seminars_hosted': ts_hosted_count, 'req_meetings': 6, 'req_seminars': 2, + 'req_seminars_hosted': 1, 'house_meetings_missed': member_missed_hms, } @@ -121,11 +141,10 @@ def display_spring_evals(internal=False, user_dict=None): if internal: return gk_members - gatekeep_active = is_gatekeep_active() - return render_template('gatekeep.html', username=user_dict['username'], members=gk_members, - gatekeep_active=gatekeep_active, + gatekeep_active=is_gatekeep_active(), req_meetings=6, - req_seminars=2) + req_seminars=2, + req_seminars_hosted=1) diff --git a/conditional/blueprints/intro_evals.py b/conditional/blueprints/intro_evals.py index 262ae18b..3787fefb 100644 --- a/conditional/blueprints/intro_evals.py +++ b/conditional/blueprints/intro_evals.py @@ -9,10 +9,12 @@ from conditional.models.models import FreshmanEvalData from conditional.models.models import FreshmanHouseMeetingAttendance from conditional.models.models import FreshmanSeminarAttendance +from conditional.models.models import FreshmanSeminarHost from conditional.models.models import HouseMeeting from conditional.models.models import MemberCommitteeAttendance from conditional.models.models import MemberHouseMeetingAttendance from conditional.models.models import MemberSeminarAttendance +from conditional.models.models import MemberSeminarHost from conditional.models.models import TechnicalSeminar from conditional.util.auth import get_user from conditional.util.flask import render_template @@ -70,7 +72,23 @@ def get_intro_members_without_accounts(): TechnicalSeminar.name ).all() + freshman_ts_host_query = FreshmanSeminarHost.query.join( + TechnicalSeminar, + FreshmanSeminarHost.seminar_id == TechnicalSeminar.id + ).with_entities( + FreshmanSeminarHost.fid, + TechnicalSeminar.timestamp, + TechnicalSeminar.approved + ).filter( + TechnicalSeminar.approved, + TechnicalSeminar.timestamp >= semester_start + ).with_entities( + FreshmanSeminarHost.fid, + TechnicalSeminar.name + ).all() + freshman_ts_attendance_dict = {} + freshman_ts_host_dict = {} for row in freshman_ts_attendance_query: if not row[0] in freshman_ts_attendance_dict: @@ -78,6 +96,12 @@ def get_intro_members_without_accounts(): freshman_ts_attendance_dict[row[0]].append(row[1]) + for row in freshman_ts_host_query: + if not row[0] in freshman_ts_host_dict: + freshman_ts_host_dict[row[0]] = [] + + freshman_ts_host_dict[row[0]].append(row[1]) + # freshmen who don't have accounts freshman_accounts = list(FreshmanAccount.query.filter( FreshmanAccount.eval_date >= semester_start)) @@ -114,6 +138,7 @@ def get_intro_members_without_accounts(): 'committee_meetings_passed': cms_attended >= 6, 'house_meetings_missed': missed_hms, 'technical_seminars': freshman_ts_attendance_dict.get(freshman_account.id, []), + 'technical_seminars_hosted': freshman_ts_host_dict.get(freshman_account.id, []), 'social_events': '', 'comments': "", 'ldap_account': False, @@ -183,7 +208,23 @@ def display_intro_evals(internal=False, user_dict=None): TechnicalSeminar.name ).all() + account_ts_hosted_query = MemberSeminarHost.query.join( + TechnicalSeminar, + MemberSeminarHost.seminar_id == TechnicalSeminar.id + ).with_entities( + MemberSeminarHost.uid, + TechnicalSeminar.timestamp, + TechnicalSeminar.approved + ).filter( + TechnicalSeminar.approved, + TechnicalSeminar.timestamp >= semester_start + ).with_entities( + MemberSeminarHost.uid, + TechnicalSeminar.name + ).all() + account_ts_attendance_dict = {} + account_ts_hosted_dict = {} for row in account_ts_attendance_query: if not row[0] in account_ts_attendance_dict: @@ -191,6 +232,12 @@ def display_intro_evals(internal=False, user_dict=None): account_ts_attendance_dict[row[0]].append(row[1]) + for row in account_ts_hosted_query: + if not row[0] in account_ts_hosted_dict: + account_ts_hosted_dict[row[0]] = [] + + account_ts_hosted_dict[row[0]].append(row[1]) + # freshmen who have accounts for member in members: uid = member.uid @@ -229,6 +276,7 @@ def display_intro_evals(internal=False, user_dict=None): 'committee_meetings_passed': cms_attended >= 6, 'house_meetings_missed': member_missed_hms, 'technical_seminars': account_ts_attendance_dict.get(uid, []), + 'technical_seminars_hosted': account_ts_hosted_dict.get(uid, []), 'social_events': freshman_data.social_events, 'comments': freshman_data.other_notes, 'ldap_account': True, diff --git a/conditional/blueprints/member_management.py b/conditional/blueprints/member_management.py index 17adaddc..e3714e90 100644 --- a/conditional/blueprints/member_management.py +++ b/conditional/blueprints/member_management.py @@ -13,7 +13,9 @@ from conditional.models.models import FreshmanCommitteeAttendance from conditional.models.models import MemberCommitteeAttendance from conditional.models.models import FreshmanSeminarAttendance +from conditional.models.models import FreshmanSeminarHost from conditional.models.models import MemberSeminarAttendance +from conditional.models.models import MemberSeminarHost from conditional.models.models import FreshmanHouseMeetingAttendance from conditional.models.models import MemberHouseMeetingAttendance from conditional.models.models import HouseMeeting @@ -422,6 +424,9 @@ def member_management_deleteuser(fid, user_dict=None): for fts in FreshmanSeminarAttendance.query.filter(FreshmanSeminarAttendance.fid == fid): db.session.delete(fts) + for ftsh in FreshmanSeminarHost.query.filter(FreshmanSeminarHost.fid == fid): + db.session.delete(ftsh) + for fhm in FreshmanHouseMeetingAttendance.query.filter(FreshmanHouseMeetingAttendance.fid == fid): db.session.delete(fhm) @@ -465,6 +470,9 @@ def member_management_upgrade_user(user_dict=None): for fts in FreshmanSeminarAttendance.query.filter(FreshmanSeminarAttendance.fid == fid): db.session.add(MemberSeminarAttendance(uid, fts.seminar_id)) + for ftsh in FreshmanSeminarHost.query.filter(FreshmanSeminarHost.fid == fid): + db.session.add(MemberSeminarHost(uid, ftsh.seminar_id)) + for fhm in FreshmanHouseMeetingAttendance.query.filter(FreshmanHouseMeetingAttendance.fid == fid): # Don't duplicate HM attendance records mhm = MemberHouseMeetingAttendance.query.filter( diff --git a/conditional/blueprints/slideshow.py b/conditional/blueprints/slideshow.py index ec290657..9098ed97 100644 --- a/conditional/blueprints/slideshow.py +++ b/conditional/blueprints/slideshow.py @@ -11,7 +11,7 @@ from conditional.models.models import SpringEval from conditional.util.auth import get_user from conditional.util.flask import render_template -from conditional.util.ldap import ldap_is_intromember, ldap_set_failed, ldap_set_bad_standing, \ +from conditional.util.ldap import ldap_is_intromember, ldap_set_failed, ldap_set_bad_standing, ldap_set_housingpoints, \ ldap_set_inactive, ldap_get_member, ldap_set_not_intro_member from conditional.util.user_dict import user_dict_is_eval_director @@ -137,6 +137,9 @@ def slideshow_spring_review(user_dict=None): if status == "Passed": if ldap_is_intromember(account): ldap_set_not_intro_member(account) + + hp = int(account.housingPoints) + ldap_set_housingpoints(account, str(hp + 2)) elif status == "Failed": if ldap_is_intromember(account): ldap_set_failed(account) diff --git a/conditional/models/models.py b/conditional/models/models.py index f3a52072..51b58cc6 100644 --- a/conditional/models/models.py +++ b/conditional/models/models.py @@ -106,6 +106,28 @@ def __init__(self, name, timestamp, approved): self.active = True +class MemberSeminarHost(db.Model): + __tablename__ = 'member_seminar_host' + id = Column(Integer, primary_key=True) + uid = Column(String(32), nullable=False, index=True) + seminar_id = Column(ForeignKey('technical_seminars.id'), nullable=False) + + def __init__(self, uid, seminar_id): + self.uid = uid + self.seminar_id = seminar_id + + +class FreshmanSeminarHost(db.Model): + __tablename__ = 'freshman_seminar_host' + id = Column(Integer, primary_key=True) + fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False, index=True) + seminar_id = Column(ForeignKey('technical_seminars.id'), nullable=False) + + def __init__(self, fid, seminar_id): + self.fid = fid + self.seminar_id = seminar_id + + class MemberSeminarAttendance(db.Model): __tablename__ = 'member_seminar_attendance' id = Column(Integer, primary_key=True) diff --git a/conditional/templates/attendance_history.html b/conditional/templates/attendance_history.html index 7f50fb6a..0b534f1f 100644 --- a/conditional/templates/attendance_history.html +++ b/conditional/templates/attendance_history.html @@ -42,7 +42,12 @@
{{seminar["name"]}}

{{seminar["date"]}}

-

+

Host: + {% for name in seminar["hosts"] %} + {{name}}{% if not loop.last %}, {% endif %} + {% endfor %} +

+

Attendees: {% for name in seminar["attendees"] %} {{name}}{% if not loop.last %}, {% endif %} {% endfor %} @@ -68,7 +73,14 @@

{{meeting["name"]}}

{{meeting["date"]}}

-

+ {% if meeting in all_ts %} +

Host: + {% for name in meeting["hosts"] %} + {{name}}{% if not loop.last %}, {% endif %} + {% endfor %} +

+ {% endif %} +

Attendees: {% for name in meeting["attendees"] %} {{name}}{% if not loop.last %}, {% endif %} {% endfor %} @@ -121,6 +133,10 @@

+ +
+
+
+
+
+ + +
+
+
+
+
diff --git a/conditional/templates/dashboard.html b/conditional/templates/dashboard.html index 2b663661..572055e4 100644 --- a/conditional/templates/dashboard.html +++ b/conditional/templates/dashboard.html @@ -89,17 +89,27 @@

Freshman Evaluations
- Technical Seminars {% if freshman['ts_total'] == 0 %} -
- -
- {% else %} -
-
    - {% for ts in freshman['ts_list'] %} -
  • {{ts}}
  • - {% endfor %} -
+ {% if freshman['ts_hosted_total'] != 0 %} + Technical Seminars - Hosted +
+
    + {% for ts in freshman['ts_hosted'] %} +
  • {{ts}}
  • + {% endfor %} +
+
+ {% endif %} + Technical Seminars - Attendance {% if freshman['ts_total'] == 0 %} +
+ +
+ {% else %} +
+
    + {% for ts in freshman['ts_list'] %} +
  • {{ts}}
  • + {% endfor %} +
{% endif %}
@@ -120,7 +130,17 @@

Membership Evaluations

- Technical Seminars {% if ts_total == 0 %} + {% if ts_total != 0 %} + Technical Seminars - Hosted +
+
    + {% for ts in ts_hosted %} +
  • {{ts}}
  • + {% endfor %} +
+
+ {% endif %} + Technical Seminars - Attendance {% if ts_total == 0 %}
@@ -381,10 +401,23 @@

Gatekeep {% endif %} - Technical Seminars + Technical Seminars - Hosted + + + {% if gatekeep['technical_seminars_hosted'] >= 1 or gatekeep['technical_seminars'] >= 2 %} + + {% else %} + + {% endif %} + {{ gatekeep['technical_seminars_hosted'] }} / 1 + + + + + Technical Seminars - Attended - {% if gatekeep['technical_seminars'] >= 2 %} + {% if gatekeep['technical_seminars'] >= 2 or gatekeep['technical_seminars_hosted'] >= 1 %} {% else %} diff --git a/conditional/templates/gatekeep.html b/conditional/templates/gatekeep.html index 35e56aa3..749b6b6a 100644 --- a/conditional/templates/gatekeep.html +++ b/conditional/templates/gatekeep.html @@ -57,14 +57,25 @@
{{m['uid']}}
{% endif %}

- {% if m['technical_seminars'] < req_seminars %} + {% if m['technical_seminars_hosted'] < req_seminars_hosted and m['technical_seminars'] < req_seminars %}
- Technical Seminars + Technical Seminars - Hosted + {{m['technical_seminars_hosted']}} / {{ req_seminars_hosted }} +
+ {% else %} +
+ Technical Seminars - Hosted + {{m['technical_seminars_hosted']}} / {{ req_seminars_hosted }} +
+ {% endif %} + {% if m['technical_seminars'] < req_seminars and m['technical_seminars_hosted'] < req_seminars_hosted %} +
+ Technical Seminars - Attended {{m['technical_seminars']}} / {{ req_seminars }}
{% else %}
- Technical Seminars + Technical Seminars - Attended {{m['technical_seminars']}} / {{ req_seminars }}
{% endif %} @@ -161,10 +172,12 @@

Missed House Meetings

{% endif %} - {% if m['technical_seminars'] < req_seminars %} - {{ m['technical_seminars'] }} / {{ req_seminars }} + {% if m['technical_seminars'] < req_seminars and m['technical_seminars_hosted'] < req_seminars_hosted %} + Hosted: {{ m['technical_seminars_hosted'] }} / {{ req_seminars_hosted }}
+ Attended: {{ m['technical_seminars'] }} / {{ req_seminars }} {% else %} - {{ m['technical_seminars'] }} / {{ req_seminars }} + Hosted: {{ m['technical_seminars_hosted'] }} / {{ req_seminars_hosted }}
+ Attended: {{ m['technical_seminars'] }} / {{ req_seminars }} {% endif %} diff --git a/conditional/templates/intro_eval_slideshow.html b/conditional/templates/intro_eval_slideshow.html index 9369a166..b35ae39c 100644 --- a/conditional/templates/intro_eval_slideshow.html +++ b/conditional/templates/intro_eval_slideshow.html @@ -52,7 +52,7 @@

{{m['house_meetings_missed']|length}}

- {% set technical_seminars_passed = m['technical_seminars']|length >= 2 %} + {% set technical_seminars_passed = m['technical_seminars']|length >= 2 or m['technical_seminars_hosted']|length >= 1 %}
{% if technical_seminars_passed %} @@ -60,7 +60,9 @@

{{m['house_meetings_missed']|length}}

{% endif %}

{{m['technical_seminars']|length}}

-

Seminars

+

Seminars Attended

+

{{m['technical_seminars_hosted']|length}}

+

Seminars Hosted

diff --git a/conditional/templates/intro_evals.html b/conditional/templates/intro_evals.html index 1d530ef5..fb1a9c68 100644 --- a/conditional/templates/intro_evals.html +++ b/conditional/templates/intro_evals.html @@ -86,8 +86,34 @@
{{ m['uid'] }}
{% endif %} - -
+
+
+ {% if m['technical_seminars_hosted'] < 1 or m['technical_seminars'] < 2 %} +
+ Technical Seminars - Hosted + {{m['technical_seminars_hosted']}} / 1 +
+ {% else %} +
+ Technical Seminars - Hosted + {{m['technical_seminars_hosted']}} / 1 +
+ {% endif %} +
+
+ {% if m['technical_seminars'] < 2 or m['technical_seminars_hosted'] < 1 %} +
+ Technical Seminars - Attended + {{m['technical_seminars_attended']}} / 2 +
+ {% else %} +
+ Technical Seminars - Attended + {{m['technical_seminars']}} / 2 +
+ {% endif %} +
+
{% if m['house_meetings_missed']|length > 0 %}
House Meetings Missed @@ -98,13 +124,13 @@
{{ m['uid'] }}
House Meetings Missed {{m['house_meetings_missed']|length}}
- {% endif %} + {% endif %}
- {% if m['house_meetings_missed']|length != 0 or m['technical_seminars']|length != 0 or m['comments'] != "" %} + {% if m['house_meetings_missed']|length != 0 or m['technical_seminars']|length != 0 or m['technical_seminars_hosted']|length != 0 or m['comments'] != "" %}