From 54cfe3e7fcfde800301c63e579b4f1182239ac2b Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 17 Feb 2026 15:15:21 -0500 Subject: [PATCH 1/6] Added uids and fids as indicies in the database to speed up queries --- Dockerfile | 1 + conditional/models/models.py | 14 +++---- ...3b870_set_uid_and_fid_as_table_indicies.py | 38 +++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 migrations/versions/f1d08673b870_set_uid_and_fid_as_table_indicies.py diff --git a/Dockerfile b/Dockerfile index 2817415d..3396bd6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,7 @@ ENV PORT=${PORT} EXPOSE ${PORT} COPY conditional /opt/conditional/conditional +COPY migrations /opt/conditional/migrations COPY *.py package.json /opt/conditional COPY --from=build-frontend /opt/conditional/conditional/static /opt/conditional/conditional/static diff --git a/conditional/models/models.py b/conditional/models/models.py index 69014b8a..2683a599 100644 --- a/conditional/models/models.py +++ b/conditional/models/models.py @@ -72,7 +72,7 @@ def __init__(self, committee, timestamp, approved): class MemberCommitteeAttendance(db.Model): __tablename__ = 'member_committee_attendance' id = Column(Integer, primary_key=True) - uid = Column(String(32), nullable=False) + uid = Column(String(32), nullable=False, index=True) meeting_id = Column(ForeignKey('committee_meetings.id'), nullable=False) def __init__(self, uid, meeting_id): @@ -83,7 +83,7 @@ def __init__(self, uid, meeting_id): class FreshmanCommitteeAttendance(db.Model): __tablename__ = 'freshman_committee_attendance' id = Column(Integer, primary_key=True) - fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False, index=True) meeting_id = Column(ForeignKey('committee_meetings.id'), nullable=False) def __init__(self, fid, meeting_id): @@ -109,7 +109,7 @@ def __init__(self, name, timestamp, approved): class MemberSeminarAttendance(db.Model): __tablename__ = 'member_seminar_attendance' id = Column(Integer, primary_key=True) - uid = Column(String(32), nullable=False) + uid = Column(String(32), nullable=False, index=True) seminar_id = Column(ForeignKey('technical_seminars.id'), nullable=False) def __init__(self, uid, seminar_id): @@ -120,7 +120,7 @@ def __init__(self, uid, seminar_id): class FreshmanSeminarAttendance(db.Model): __tablename__ = 'freshman_seminar_attendance' id = Column(Integer, primary_key=True) - fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False) + 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): @@ -132,7 +132,7 @@ class MajorProject(db.Model): __tablename__ = 'major_projects' id = Column(Integer, primary_key=True) date = Column(Date, nullable=False) - uid = Column(String(32), nullable=False) + uid = Column(String(32), nullable=False, index=True) name = Column(String(64), nullable=False) description = Column(Text) active = Column(Boolean, nullable=False) @@ -163,7 +163,7 @@ def __init__(self, hm_date): class MemberHouseMeetingAttendance(db.Model): __tablename__ = 'member_hm_attendance' id = Column(Integer, primary_key=True) - uid = Column(String(32), nullable=False) + uid = Column(String(32), nullable=False, index=True) meeting_id = Column(ForeignKey('house_meetings.id'), nullable=False) excuse = Column(Text) attendance_status = Column(attendance_enum) @@ -178,7 +178,7 @@ def __init__(self, uid, meeting_id, excuse, status): class FreshmanHouseMeetingAttendance(db.Model): __tablename__ = 'freshman_hm_attendance' id = Column(Integer, primary_key=True) - fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False, index=True) meeting_id = Column(ForeignKey('house_meetings.id'), nullable=False) excuse = Column(Text) attendance_status = Column(attendance_enum) diff --git a/migrations/versions/f1d08673b870_set_uid_and_fid_as_table_indicies.py b/migrations/versions/f1d08673b870_set_uid_and_fid_as_table_indicies.py new file mode 100644 index 00000000..1f3ee0bd --- /dev/null +++ b/migrations/versions/f1d08673b870_set_uid_and_fid_as_table_indicies.py @@ -0,0 +1,38 @@ +"""Set uid and fid as table indicies + +Revision ID: f1d08673b870 +Revises: 7a3904cac24b +Create Date: 2026-02-17 15:02:08.721333 + +""" + +# revision identifiers, used by Alembic. +revision = 'f1d08673b870' +down_revision = '7a3904cac24b' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_index(op.f('ix_freshman_committee_attendance_fid'), 'freshman_committee_attendance', ['fid'], unique=False) + op.create_index(op.f('ix_freshman_hm_attendance_fid'), 'freshman_hm_attendance', ['fid'], unique=False) + op.create_index(op.f('ix_freshman_seminar_attendance_fid'), 'freshman_seminar_attendance', ['fid'], unique=False) + op.create_index(op.f('ix_major_projects_uid'), 'major_projects', ['uid'], unique=False) + op.create_index(op.f('ix_member_committee_attendance_uid'), 'member_committee_attendance', ['uid'], unique=False) + op.create_index(op.f('ix_member_hm_attendance_uid'), 'member_hm_attendance', ['uid'], unique=False) + op.create_index(op.f('ix_member_seminar_attendance_uid'), 'member_seminar_attendance', ['uid'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_member_seminar_attendance_uid'), table_name='member_seminar_attendance') + op.drop_index(op.f('ix_member_hm_attendance_uid'), table_name='member_hm_attendance') + op.drop_index(op.f('ix_member_committee_attendance_uid'), table_name='member_committee_attendance') + op.drop_index(op.f('ix_major_projects_uid'), table_name='major_projects') + op.drop_index(op.f('ix_freshman_seminar_attendance_fid'), table_name='freshman_seminar_attendance') + op.drop_index(op.f('ix_freshman_hm_attendance_fid'), table_name='freshman_hm_attendance') + op.drop_index(op.f('ix_freshman_committee_attendance_fid'), table_name='freshman_committee_attendance') + # ### end Alembic commands ### From c0a6c8dca1c316e5a53e324fb14fb368c67e8de2 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 17 Feb 2026 18:00:59 -0500 Subject: [PATCH 2/6] speedup member management --- conditional/blueprints/member_management.py | 10 +++---- conditional/util/member.py | 32 +++++++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/conditional/blueprints/member_management.py b/conditional/blueprints/member_management.py index a0973dfc..99e6ee25 100644 --- a/conditional/blueprints/member_management.py +++ b/conditional/blueprints/member_management.py @@ -42,9 +42,10 @@ from conditional.util.ldap import _ldap_add_member_to_group as ldap_add_member_to_group from conditional.util.ldap import _ldap_remove_member_from_group as ldap_remove_member_from_group +from conditional.util.member import get_members_info_active_and_onfloor + from conditional.util.flask import render_template from conditional.models.models import attendance_enum -from conditional.util.member import get_members_info, get_onfloor_members logger = structlog.get_logger() @@ -61,8 +62,7 @@ def display_member_management(user_dict=None): if not ldap_is_eval_director(user_dict['account']) and not ldap_is_financial_director(user_dict['account']): return "must be eval director", 403 - member_list = get_members_info() - onfloor_list = get_onfloor_members() + member_list, active_members, onfloor_members = get_members_info_active_and_onfloor() freshmen = FreshmanAccount.query freshmen_list = [] @@ -90,9 +90,9 @@ def display_member_management(user_dict=None): username=user_dict['username'], active=member_list, num_current=len(member_list), - num_active=len(ldap_get_active_members()), + num_active=len(active_members), num_fresh=len(freshmen_list), - num_onfloor=len(onfloor_list), + num_onfloor=len(onfloor_members), freshmen=freshmen_list, site_lockdown=lockdown, accept_dues_until=accept_dues_until, diff --git a/conditional/util/member.py b/conditional/util/member.py index e9b28e3e..688570d2 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -17,21 +17,31 @@ from conditional.util.ldap import ldap_get_onfloor_members from conditional.util.ldap import ldap_get_roomnumber from conditional.util.ldap import ldap_is_active -from conditional.util.ldap import ldap_is_onfloor from conditional.util.ldap import ldap_is_intromember from conditional.util.ldap import ldap_get_member @service_cache(maxsize=1024) -def get_members_info(): +def get_members_info_active_and_onfloor(): members = ldap_get_current_students() member_list = [] + onfloor_set = set() + active_set = set() + for account in members: uid = account.uid name = account.cn - active = ldap_is_active(account) - onfloor = ldap_is_onfloor(account) + groups = "".join(account.groups()) + active = "active" in groups + onfloor = "onfloor" in groups + + if active: + active_set.add(uid) + + if onfloor: + onfloor_set.add(uid) + room = ldap_get_roomnumber(account) hp = account.housingPoints member_list.append({ @@ -43,8 +53,7 @@ def get_members_info(): "hp": hp }) - return member_list - + return member_list, active_set, onfloor_set def get_freshman_data(user_name): freshman = {} @@ -83,12 +92,17 @@ def get_freshman_data(user_name): freshman['eval_date'] = freshman_data.eval_date return freshman +@service_cache(maxsize=1024) +def get_active_members() -> set[str]: + return {members.uid for members in ldap_get_active_members()} @service_cache(maxsize=1024) -def get_onfloor_members(): - return [uid for uid in [members.uid for members in ldap_get_active_members()] - if uid in [members.uid for members in ldap_get_onfloor_members()]] +def get_all_onfloor_members() -> set[str]: + return {members.uid for members in ldap_get_onfloor_members()} +@service_cache(maxsize=1024) +def get_onfloor_members(): + return get_active_members() & get_all_onfloor_members() def get_cm(member): query_result = CommitteeMeeting.query.join( From a219603b298f48962601ef9a2cb78c539bf2195d Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 17 Feb 2026 20:58:29 -0500 Subject: [PATCH 3/6] use user dict instead of ldap where possible --- conditional/__init__.py | 6 ++-- conditional/blueprints/attendance.py | 25 ++++++------- conditional/blueprints/cache_management.py | 5 +-- conditional/blueprints/co_op.py | 11 +++--- conditional/blueprints/conditional.py | 7 ++-- conditional/blueprints/dashboard.py | 39 +++++++++++---------- conditional/blueprints/logs.py | 3 +- conditional/blueprints/member_management.py | 36 +++++++++---------- conditional/blueprints/slideshow.py | 9 ++--- conditional/util/auth.py | 7 ++-- conditional/util/flask.py | 15 ++++---- conditional/util/member.py | 10 ++++-- conditional/util/user_dict.py | 32 +++++++++++++++++ 13 files changed, 127 insertions(+), 78 deletions(-) create mode 100644 conditional/util/user_dict.py diff --git a/conditional/__init__.py b/conditional/__init__.py index 83c02a63..4d7cf1df 100644 --- a/conditional/__init__.py +++ b/conditional/__init__.py @@ -1,6 +1,7 @@ import os from datetime import datetime +from pstats import SortKey import structlog from csh_ldap import CSHLDAP from flask import Flask, redirect, render_template, request, g @@ -33,7 +34,8 @@ if app.config['PROFILING']: app.wsgi_app = ProfilerMiddleware( app.wsgi_app, - restrictions=[30] + sort_by=('cumulative',), + restrictions=[80] ) # Sentry setup @@ -195,7 +197,7 @@ def route_errors(error, user_dict=None): # Handle the case where the header isn't present if user_dict['username'] is not None: - data['username'] = user_dict['account'].uid + data['username'] = user_dict['username'] data['name'] = user_dict['account'].cn else: data['username'] = "unknown" diff --git a/conditional/blueprints/attendance.py b/conditional/blueprints/attendance.py index 22475703..35a30f2e 100644 --- a/conditional/blueprints/attendance.py +++ b/conditional/blueprints/attendance.py @@ -22,6 +22,7 @@ from conditional.util.ldap import ldap_get_member from conditional.util.ldap import ldap_is_eboard from conditional.util.ldap import ldap_is_eval_director +from conditional.util.user_dict import user_dict_is_eboard, user_dict_is_eval_director logger = structlog.get_logger() @@ -160,7 +161,7 @@ def display_attendance_hm(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Display House Meeting Attendance Page') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return redirect("/dashboard") return render_template('attendance_hm.html', @@ -175,7 +176,7 @@ def display_attendance_hm(user_dict=None): def submit_committee_attendance(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - approved = ldap_is_eboard(user_dict['account']) + approved = user_dict_is_eval_director(user_dict) post_data = request.get_json() committee = post_data['committee'] @@ -211,7 +212,7 @@ def submit_seminar_attendance(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Submit Technical Seminar Attendance') - approved = ldap_is_eboard(user_dict['account']) + approved = user_dict_is_eboard(user_dict) post_data = request.get_json() @@ -248,7 +249,7 @@ def submit_house_attendance(user_dict=None): # status: Attended | Excused | Absent - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be evals", 403 post_data = request.get_json() @@ -289,7 +290,7 @@ def submit_house_attendance(user_dict=None): def alter_house_attendance(uid, hid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be evals", 403 if not uid.isdigit(): @@ -319,7 +320,7 @@ def alter_house_attendance(uid, hid, user_dict=None): def alter_house_excuse(uid, hid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 post_data = request.get_json() @@ -381,7 +382,7 @@ def get_seminar_attendees(meeting_id): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eboard(user_dict['account']): + if not user_dict_is_eboard(user_dict): return jsonify({"success": False, "error": "Not EBoard"}), 403 @@ -444,7 +445,7 @@ def alter_committee_attendance(cid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Edit Committee Meeting Attendance') - if not ldap_is_eboard(user_dict['account']): + if not user_dict_is_eboard(user_dict): return jsonify({"success": False, "error": "Not EBoard"}), 403 post_data = request.get_json() @@ -476,7 +477,7 @@ def alter_seminar_attendance(sid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Edit Technical Seminar Attendance') - if not ldap_is_eboard(user_dict['account']): + if not user_dict_is_eboard(user_dict): return jsonify({"success": False, "error": "Not EBoard"}), 403 post_data = request.get_json() @@ -559,7 +560,7 @@ def get_ts_attendees(cid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Delete Committee Meeting {cid}') - if not ldap_is_eboard(user_dict['account']): + if not user_dict_is_eboard(user_dict): return jsonify({"success": False, "error": "Not EBoard"}), 403 FreshmanCommitteeAttendance.query.filter( @@ -582,7 +583,7 @@ def approve_cm(cid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Approve Committee Meeting {cid} Attendance') - if not ldap_is_eboard(user_dict['account']): + if not user_dict_is_eboard(user_dict): return jsonify({"success": False, "error": "Not EBoard"}), 403 CommitteeMeeting.query.filter( @@ -600,7 +601,7 @@ def approve_ts(sid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Approve Technical Seminar {sid} Attendance') - if not ldap_is_eboard(user_dict['account']): + if not user_dict_is_eboard(user_dict): return jsonify({"success": False, "error": "Not EBoard"}), 403 TechnicalSeminar.query.filter( diff --git a/conditional/blueprints/cache_management.py b/conditional/blueprints/cache_management.py index 14290a51..4bceff3d 100644 --- a/conditional/blueprints/cache_management.py +++ b/conditional/blueprints/cache_management.py @@ -1,6 +1,7 @@ import os import signal +from conditional.util.user_dict import user_dict_is_eval_director, user_dict_is_rtp import structlog from flask import Blueprint, request, redirect @@ -24,7 +25,7 @@ @auth.oidc_auth("default") @get_user def restart_app(user_dict=None): - if not ldap_is_rtp(user_dict['account']): + if not user_dict_is_rtp(user_dict): return redirect("/dashboard") log = logger.new(request=request, auth_dict=user_dict) @@ -37,7 +38,7 @@ def restart_app(user_dict=None): @auth.oidc_auth("default") @get_user def clear_cache(user_dict=None): - if not ldap_is_eval_director(user_dict['account']) and not ldap_is_rtp(user_dict['account']): + if not user_dict_is_eval_director(user_dict) and not user_dict_is_rtp(user_dict): return redirect("/dashboard") log = logger.new(request=request, auth_dict=user_dict) diff --git a/conditional/blueprints/co_op.py b/conditional/blueprints/co_op.py index 94bf107a..7d125fe0 100644 --- a/conditional/blueprints/co_op.py +++ b/conditional/blueprints/co_op.py @@ -1,3 +1,4 @@ +from conditional.util.user_dict import user_dict_is_current_student, user_dict_is_eval_director, user_dict_is_in_group import structlog from flask import Blueprint, request, jsonify @@ -43,7 +44,7 @@ def submit_co_op_form(user_dict=None): semester = post_data['semester'] if post_data['semester'] not in valid_semesters: return "Invalid semester submitted", 400 - if not ldap_is_current_student(user_dict['account']): + if not user_dict_is_current_student(user_dict): return "Must be current student", 403 log.info(f'Submit {semester} Co-Op') @@ -70,15 +71,15 @@ def submit_co_op_form(user_dict=None): def delete_co_op(uid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 log.info(f'Delete {uid}\'s Co-Op') # Remove from corresponding co-op ldap group - if ldap_is_member_of_group(user_dict['account'], 'fall_coop'): + if user_dict_is_in_group(user_dict, 'fall_coop'): ldap_remove_member_from_group(user_dict['account'], 'fall_coop') - if ldap_is_member_of_group(user_dict['account'], 'spring_coop'): + if user_dict_is_in_group(user_dict, 'spring_coop'): ldap_remove_member_from_group(user_dict['account'], 'spring_coop') CurrentCoops.query.filter(CurrentCoops.uid == uid, CurrentCoops.date_created > start_of_year()).delete() @@ -97,7 +98,7 @@ def display_co_op_management(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Display Co-Op Management') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 co_op_list = [(member.semester, member.uid) diff --git a/conditional/blueprints/conditional.py b/conditional/blueprints/conditional.py index 1082ca4f..ae46c65d 100644 --- a/conditional/blueprints/conditional.py +++ b/conditional/blueprints/conditional.py @@ -1,5 +1,6 @@ from datetime import datetime +from conditional.util.user_dict import user_dict_is_eval_director import structlog from flask import Blueprint, request, jsonify, redirect @@ -44,7 +45,7 @@ def display_conditionals(user_dict=None): def create_conditional(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 post_data = request.get_json() @@ -81,7 +82,7 @@ def create_conditional(user_dict=None): def conditional_review(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return redirect("/dashboard", code=302) post_data = request.get_json() @@ -119,7 +120,7 @@ def conditional_delete(cid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Delete conditional-{cid}') - if ldap_is_eval_director(user_dict['account']): + if user_dict_is_eval_director(user_dict): Conditional.query.filter( Conditional.id == cid ).delete() diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index a0a5c865..5c1f94fb 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -1,3 +1,4 @@ +from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_intromember, user_dict_is_onfloor import structlog from flask import Blueprint, request @@ -16,7 +17,7 @@ from conditional.util.ldap import ldap_is_active from conditional.util.ldap import ldap_is_intromember from conditional.util.ldap import ldap_is_onfloor -from conditional.util.member import get_freshman_data, get_voting_members, get_cm, get_hm, req_cm +from conditional.util.member import get_active_members, get_freshman_data, get_voting_members, get_cm, get_hm, req_cm logger = structlog.get_logger() @@ -35,32 +36,34 @@ def display_dashboard(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('display dashboard') - # Get the list of voting members. + uid = user_dict["username"] can_vote = get_voting_members() + on_floor = user_dict_is_onfloor(user_dict) + data = {} - data['username'] = user_dict['account'].uid - data['active'] = ldap_is_active(user_dict['account']) - data['bad_standing'] = ldap_is_bad_standing(user_dict['account']) - data['onfloor'] = ldap_is_onfloor(user_dict['account']) - data['voting'] = bool(user_dict['account'].uid in can_vote) + data['username'] = uid + data['active'] = user_dict_is_active(user_dict) + data['bad_standing'] = user_dict_is_bad_standing(user_dict) + data['onfloor'] = on_floor + data['voting'] = bool(uid in can_vote) data['voting_count'] = {"Voting Members": len(can_vote), - "Active Members": len(ldap_get_active_members())} + "Active Members": len(get_active_members())} # freshman shit - if ldap_is_intromember(user_dict['account']): - data['freshman'] = get_freshman_data(user_dict['account'].uid) + if user_dict_is_intromember(user_dict): + data['freshman'] = get_freshman_data(uid) else: data['freshman'] = None spring = {} c_meetings = get_cm(user_dict['account']) spring['committee_meetings'] = len(c_meetings) - spring['req_meetings'] = req_cm(user_dict['account'].uid) + spring['req_meetings'] = req_cm(uid) h_meetings = [(m.meeting_id, m.attendance_status) for m in get_hm(user_dict['account'])] spring['hm_missed'] = len([h for h in h_meetings if h[1] == "Absent"]) - eval_entry = SpringEval.query.filter(SpringEval.uid == user_dict['account'].uid, + eval_entry = SpringEval.query.filter(SpringEval.uid == uid, SpringEval.date_created > start_of_year(), SpringEval.active == True).first() # pylint: disable=singleton-comparison if eval_entry is not None: @@ -71,11 +74,11 @@ def display_dashboard(user_dict=None): data['spring'] = spring # only show housing if member has onfloor status - if ldap_is_onfloor(user_dict['account']): + if on_floor: housing = {} housing['points'] = user_dict['account'].housingPoints housing['room'] = user_dict['account'].roomNumber - housing['queue_pos'] = get_queue_position(user_dict['account'].uid) + housing['queue_pos'] = get_queue_position(uid) else: housing = None @@ -88,7 +91,7 @@ def display_dashboard(user_dict=None): 'status': p.status, 'description': p.description } for p in - MajorProject.query.filter(MajorProject.uid == user_dict['account'].uid, + MajorProject.query.filter(MajorProject.uid == uid, MajorProject.date > start_of_year())] data['major_projects_count'] = len(data['major_projects']) @@ -96,7 +99,7 @@ def display_dashboard(user_dict=None): # technical seminar total t_seminars = [s.seminar_id for s in MemberSeminarAttendance.query.filter( - MemberSeminarAttendance.uid == user_dict['account'].uid, + MemberSeminarAttendance.uid == uid, ) if is_seminar_attendance_valid(s)] data['ts_total'] = len(t_seminars) attendance = [m.name for m in TechnicalSeminar.query.filter( @@ -122,7 +125,7 @@ def display_dashboard(user_dict=None): 'status': c.status } for c in Conditional.query.filter( - Conditional.uid == user_dict['account'].uid, + Conditional.uid == uid, Conditional.date_due > start_of_year())] data['conditionals'] = conditionals data['conditionals_len'] = len(conditionals) @@ -137,7 +140,7 @@ def display_dashboard(user_dict=None): MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id).with_entities( MemberHouseMeetingAttendance.excuse, HouseMeeting.date).filter( - MemberHouseMeetingAttendance.uid == user_dict['account'].uid, + MemberHouseMeetingAttendance.uid == uid, MemberHouseMeetingAttendance.attendance_status == "Absent", HouseMeeting.date > start_of_year())] diff --git a/conditional/blueprints/logs.py b/conditional/blueprints/logs.py index 92b671be..1a4c46d2 100644 --- a/conditional/blueprints/logs.py +++ b/conditional/blueprints/logs.py @@ -1,3 +1,4 @@ +from conditional.util.user_dict import user_dict_is_eboard, user_dict_is_rtp import structlog from flask import Blueprint, request @@ -22,7 +23,7 @@ def display_logs(user_dict=None): log.info(user_dict['account'].displayName) - if not ldap_is_eboard(user_dict['account']) and not ldap_is_rtp(user_dict['account']): + if not user_dict_is_eboard(user_dict) and not user_dict_is_rtp(user_dict): return "must be rtp or eboard", 403 logs = UserLog.query.all() diff --git a/conditional/blueprints/member_management.py b/conditional/blueprints/member_management.py index 99e6ee25..f93b76dc 100644 --- a/conditional/blueprints/member_management.py +++ b/conditional/blueprints/member_management.py @@ -46,6 +46,7 @@ from conditional.util.flask import render_template from conditional.models.models import attendance_enum +from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_current_student, user_dict_is_eval_director, user_dict_is_financial_director logger = structlog.get_logger() @@ -59,7 +60,7 @@ def display_member_management(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Display Member Management') - if not ldap_is_eval_director(user_dict['account']) and not ldap_is_financial_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict) and not user_dict_is_financial_director(user_dict): return "must be eval director", 403 member_list, active_members, onfloor_members = get_members_info_active_and_onfloor() @@ -105,7 +106,7 @@ def display_member_management(user_dict=None): def member_management_eval(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 post_data = request.get_json() @@ -135,7 +136,7 @@ def member_management_eval(user_dict=None): def member_management_financial(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_financial_director(user_dict['account']): + if not user_dict_is_financial_director(user_dict): return "must be financial director", 403 post_data = request.get_json() @@ -159,7 +160,7 @@ def member_management_financial(user_dict=None): def member_management_adduser(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 post_data = request.get_json() @@ -185,7 +186,7 @@ def member_management_adduser(user_dict=None): def member_management_uploaduser(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 f = request.files['file'] @@ -224,7 +225,7 @@ def member_management_uploaduser(user_dict=None): @auth.oidc_auth("default") @get_user def member_management_edituser(uid, user_dict=None): - if not ldap_is_eval_director(user_dict['account']) and not ldap_is_financial_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict) and not user_dict_is_financial_director(user_dict): return "must be eval director", 403 if not uid.isdigit(): @@ -292,7 +293,6 @@ def edit_fid(uid, flask_request): log.info(f'Edit freshman-{uid} - Room: {post_data['roomNumber']} On-Floor: {post_data['onfloorStatus']} Eval: {post_data['evalDate']} SigMiss: {post_data['sigMissed']}') #pylint: disable=line-too-long - name = post_data['name'] if post_data['roomNumber'] == "": @@ -324,7 +324,7 @@ def member_management_getuserinfo(uid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Get {uid}\'s Information') - if not ldap_is_eval_director(user_dict['account']) and not ldap_is_financial_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict) and not user_dict_is_financial_director(user_dict): return "must be eval or financial director", 403 acct = None @@ -368,7 +368,7 @@ def get_hm_date(hm_id): account = ldap_get_member(uid) - if ldap_is_eval_director(ldap_get_member(user_dict['username'])): + if user_dict_is_eval_director(user_dict): missed_hm = [ { 'date': get_hm_date(hma.meeting_id), @@ -409,7 +409,7 @@ def member_management_deleteuser(fid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Delete freshman-{fid}') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 if not fid.isdigit(): @@ -442,7 +442,7 @@ def member_management_deleteuser(fid, user_dict=None): def member_management_upgrade_user(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 post_data = request.get_json() @@ -504,9 +504,9 @@ def member_management_upgrade_user(user_dict=None): def member_management_make_user_active(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_current_student(user_dict['account']) \ - or ldap_is_active(user_dict['account']) \ - or ldap_is_bad_standing(user_dict['account']): + if not user_dict_is_current_student(user_dict) \ + or user_dict_is_active(user_dict) \ + or user_dict_is_bad_standing(user_dict): return "must be current student, not in bad standing and not active", 403 ldap_set_active(user_dict['account']) @@ -523,7 +523,7 @@ def get_member(uid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info(f'Get {uid}\'s Information') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 member = ldap_get_member(uid) @@ -542,7 +542,7 @@ def get_member(uid, user_dict=None): def clear_active_members(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 # Get the active group. members = ldap_get_active_members() @@ -588,7 +588,7 @@ def export_active_list(): def remove_current_student(uid, user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 member = ldap_get_member(uid) @@ -608,7 +608,7 @@ def new_year(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Display New Year Page') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return "must be eval director", 403 current_students = ldap_get_current_students() diff --git a/conditional/blueprints/slideshow.py b/conditional/blueprints/slideshow.py index 1ea953d3..2b3de163 100644 --- a/conditional/blueprints/slideshow.py +++ b/conditional/blueprints/slideshow.py @@ -13,6 +13,7 @@ from conditional.util.flask import render_template from conditional.util.ldap import ldap_is_eval_director, ldap_is_intromember, ldap_set_failed, ldap_set_bad_standing, \ ldap_set_inactive, ldap_get_member, ldap_set_not_intro_member +from conditional.util.user_dict import user_dict_is_eval_director logger = structlog.get_logger() @@ -26,7 +27,7 @@ def slideshow_intro_display(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Display Intro Slideshow') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return redirect("/dashboard") return render_template('intro_eval_slideshow.html', @@ -54,7 +55,7 @@ def slideshow_intro_members(user_dict=None): def slideshow_intro_review(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return redirect("/dashboard", code=302) post_data = request.get_json() @@ -82,7 +83,7 @@ def slideshow_spring_display(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Display Membership Evaluations Slideshow') - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return redirect("/dashboard") return render_template('spring_eval_slideshow.html', @@ -110,7 +111,7 @@ def slideshow_spring_members(user_dict=None): def slideshow_spring_review(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict['account']): + if not user_dict_is_eval_director(user_dict): return redirect("/dashboard", code=302) post_data = request.get_json() diff --git a/conditional/util/auth.py b/conditional/util/auth.py index bee8e261..83b51e4a 100644 --- a/conditional/util/auth.py +++ b/conditional/util/auth.py @@ -2,19 +2,20 @@ from flask import session -from conditional.util.ldap import ldap_get_member, ldap_is_current_student +from conditional.util.ldap import ldap_get_member def get_user(func): @wraps(func) def wrapped_function(*args, **kwargs): username = str(session["userinfo"].get("preferred_username", "")) account = ldap_get_member(username) - current_student = ldap_is_current_student(account) + + print(session["userinfo"]) user_dict = { 'username': username, 'account': account, - 'student': current_student + 'groups': session["userinfo"]["groups"], } kwargs["user_dict"] = user_dict diff --git a/conditional/util/flask.py b/conditional/util/flask.py index 1ebc0ddc..0c9f435f 100644 --- a/conditional/util/flask.py +++ b/conditional/util/flask.py @@ -16,6 +16,7 @@ from conditional.models.models import TechnicalSeminar from conditional import db +from conditional.util.user_dict import user_dict_is_active, user_dict_is_alumni, user_dict_is_eboard, user_dict_is_eval_director, user_dict_is_financial_director, user_dict_is_intromember, user_dict_is_rtp @get_user @@ -27,13 +28,13 @@ def render_template(template_name, user_dict=None, **kwargs): db.session.commit() lockdown = EvalSettings.query.first().site_lockdown accepting_dues = EvalSettings.query.first().accept_dues_until > date.today() - is_active = ldap_is_active(user_dict['account']) - is_alumni = ldap_is_alumni(user_dict['account']) - is_eboard = ldap_is_eboard(user_dict['account']) - is_financial = ldap_is_financial_director(user_dict['account']) - is_eval = ldap_is_eval_director(user_dict['account']) - is_intromember = ldap_is_intromember(user_dict['account']) - is_rtp = ldap_is_rtp(user_dict['account']) + is_active = user_dict_is_active(user_dict) + is_alumni = user_dict_is_alumni(user_dict) + is_eboard = user_dict_is_eboard(user_dict) + is_financial = user_dict_is_financial_director(user_dict) + is_eval = user_dict_is_eval_director(user_dict) + is_intromember = user_dict_is_intromember(user_dict) + is_rtp = user_dict_is_rtp(user_dict) cm_review = len(CommitteeMeeting.query.filter( CommitteeMeeting.approved == False).all()) # pylint: disable=singleton-comparison diff --git a/conditional/util/member.py b/conditional/util/member.py index 688570d2..f4e8efa5 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -1,4 +1,5 @@ from datetime import datetime +from sys import maxsize from sqlalchemy import func, or_ from conditional import start_of_year @@ -96,6 +97,10 @@ def get_freshman_data(user_name): def get_active_members() -> set[str]: return {members.uid for members in ldap_get_active_members()} +@service_cache(maxsize=1024) +def get_intro_members() -> set[str]: + return {member.uid for member in ldap_get_intro_members()} + @service_cache(maxsize=1024) def get_all_onfloor_members() -> set[str]: return {members.uid for members in ldap_get_onfloor_members()} @@ -169,8 +174,8 @@ def get_voting_members(): semester = "Spring" semester_start = datetime(start_of_year().year + 1, 1, 1) - active_members = set(ldap_get_active_members()) - intro_members = set(ldap_get_intro_members()) + active_members = get_active_members() + intro_members = get_intro_members() coop_members = CurrentCoops.query.filter( CurrentCoops.date_created > start_of_year(), @@ -198,7 +203,6 @@ def get_voting_members(): passed_fall_members = set(passed_fall_members) active_not_intro = active_members - intro_members - active_not_intro = set(map(lambda member: member.uid, active_not_intro)) eligible_members = (active_not_intro - coop_members) | passed_fall_members diff --git a/conditional/util/user_dict.py b/conditional/util/user_dict.py new file mode 100644 index 00000000..0015239b --- /dev/null +++ b/conditional/util/user_dict.py @@ -0,0 +1,32 @@ +def user_dict_is_in_group(user_dict, group) -> bool: + return group in user_dict['groups'] + +def user_dict_is_active(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'active') + +def user_dict_is_bad_standing(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'bad_standing') + +def user_dict_is_alumni(user_dict) -> bool: + return not user_dict_is_active(user_dict) + +def user_dict_is_eboard(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'eboard') + +def user_dict_is_rtp(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'active_rtp') + +def user_dict_is_intromember(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'intromembers') + +def user_dict_is_onfloor(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'onfloor') + +def user_dict_is_financial_director(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'financial') + +def user_dict_is_eval_director(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'evaluations') + +def user_dict_is_current_student(user_dict) -> bool: + return user_dict_is_in_group(user_dict, 'current_student') From bd0981fbe52ae4bc904d14fcbe207d2d5f04b062 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Wed, 18 Feb 2026 10:25:29 -0500 Subject: [PATCH 4/6] fix linting :) --- conditional/blueprints/attendance.py | 1 - conditional/blueprints/cache_management.py | 4 +--- conditional/blueprints/co_op.py | 4 +--- conditional/blueprints/conditional.py | 3 +-- conditional/blueprints/dashboard.py | 7 ++----- conditional/blueprints/logs.py | 4 +--- conditional/blueprints/member_management.py | 7 +++---- conditional/blueprints/slideshow.py | 2 +- conditional/util/flask.py | 11 ++--------- conditional/util/member.py | 6 ++---- 10 files changed, 14 insertions(+), 35 deletions(-) diff --git a/conditional/blueprints/attendance.py b/conditional/blueprints/attendance.py index 35a30f2e..ebc05415 100644 --- a/conditional/blueprints/attendance.py +++ b/conditional/blueprints/attendance.py @@ -21,7 +21,6 @@ from conditional.util.ldap import ldap_get_current_students from conditional.util.ldap import ldap_get_member from conditional.util.ldap import ldap_is_eboard -from conditional.util.ldap import ldap_is_eval_director from conditional.util.user_dict import user_dict_is_eboard, user_dict_is_eval_director logger = structlog.get_logger() diff --git a/conditional/blueprints/cache_management.py b/conditional/blueprints/cache_management.py index 4bceff3d..fad5924c 100644 --- a/conditional/blueprints/cache_management.py +++ b/conditional/blueprints/cache_management.py @@ -1,7 +1,6 @@ import os import signal -from conditional.util.user_dict import user_dict_is_eval_director, user_dict_is_rtp import structlog from flask import Blueprint, request, redirect @@ -14,8 +13,7 @@ from conditional.util.ldap import ldap_get_intro_members from conditional.util.ldap import ldap_get_member from conditional.util.ldap import ldap_get_onfloor_members -from conditional.util.ldap import ldap_is_eval_director -from conditional.util.ldap import ldap_is_rtp +from conditional.util.user_dict import user_dict_is_eval_director, user_dict_is_rtp logger = structlog.get_logger() cache_bp = Blueprint('cache_bp', __name__) diff --git a/conditional/blueprints/co_op.py b/conditional/blueprints/co_op.py index 7d125fe0..84207422 100644 --- a/conditional/blueprints/co_op.py +++ b/conditional/blueprints/co_op.py @@ -1,4 +1,3 @@ -from conditional.util.user_dict import user_dict_is_current_student, user_dict_is_eval_director, user_dict_is_in_group import structlog from flask import Blueprint, request, jsonify @@ -7,10 +6,9 @@ from conditional.util.member import req_cm from conditional.util.auth import get_user from conditional.util.flask import render_template -from conditional.util.ldap import ldap_is_eval_director, ldap_is_current_student from conditional.util.ldap import _ldap_add_member_to_group as ldap_add_member_to_group from conditional.util.ldap import _ldap_remove_member_from_group as ldap_remove_member_from_group -from conditional.util.ldap import _ldap_is_member_of_group as ldap_is_member_of_group +from conditional.util.user_dict import user_dict_is_current_student, user_dict_is_eval_director, user_dict_is_in_group co_op_bp = Blueprint('co_op_bp', __name__) diff --git a/conditional/blueprints/conditional.py b/conditional/blueprints/conditional.py index ae46c65d..74a4913f 100644 --- a/conditional/blueprints/conditional.py +++ b/conditional/blueprints/conditional.py @@ -1,6 +1,5 @@ from datetime import datetime -from conditional.util.user_dict import user_dict_is_eval_director import structlog from flask import Blueprint, request, jsonify, redirect @@ -8,7 +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_is_eval_director +from conditional.util.user_dict import user_dict_is_eval_director conditionals_bp = Blueprint('conditionals_bp', __name__) diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index 5c1f94fb..c080ddaf 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -1,4 +1,3 @@ -from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_intromember, user_dict_is_onfloor import structlog from flask import Blueprint, request @@ -13,11 +12,9 @@ from conditional.util.auth import get_user from conditional.util.flask import render_template from conditional.util.housing import get_queue_position -from conditional.util.ldap import ldap_get_active_members, ldap_is_bad_standing -from conditional.util.ldap import ldap_is_active -from conditional.util.ldap import ldap_is_intromember -from conditional.util.ldap import ldap_is_onfloor from conditional.util.member import get_active_members, get_freshman_data, get_voting_members, get_cm, get_hm, req_cm +from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_intromember, \ + user_dict_is_onfloor logger = structlog.get_logger() diff --git a/conditional/blueprints/logs.py b/conditional/blueprints/logs.py index 1a4c46d2..5bed2616 100644 --- a/conditional/blueprints/logs.py +++ b/conditional/blueprints/logs.py @@ -1,4 +1,3 @@ -from conditional.util.user_dict import user_dict_is_eboard, user_dict_is_rtp import structlog from flask import Blueprint, request @@ -6,8 +5,7 @@ from conditional.models.models import UserLog from conditional.util.auth import get_user from conditional.util.flask import render_template -from conditional.util.ldap import ldap_is_eboard -from conditional.util.ldap import ldap_is_rtp +from conditional.util.user_dict import user_dict_is_eboard, user_dict_is_rtp logger = structlog.get_logger() diff --git a/conditional/blueprints/member_management.py b/conditional/blueprints/member_management.py index f93b76dc..17adaddc 100644 --- a/conditional/blueprints/member_management.py +++ b/conditional/blueprints/member_management.py @@ -24,11 +24,9 @@ from conditional.blueprints.cache_management import clear_members_cache -from conditional.util.ldap import ldap_is_eval_director, ldap_is_bad_standing -from conditional.util.ldap import ldap_is_financial_director +from conditional.util.ldap import ldap_is_eval_director from conditional.util.ldap import ldap_is_active from conditional.util.ldap import ldap_is_onfloor -from conditional.util.ldap import ldap_is_current_student from conditional.util.ldap import ldap_set_roomnumber from conditional.util.ldap import ldap_set_active from conditional.util.ldap import ldap_set_inactive @@ -46,7 +44,8 @@ from conditional.util.flask import render_template from conditional.models.models import attendance_enum -from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_current_student, user_dict_is_eval_director, user_dict_is_financial_director +from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_current_student, \ + user_dict_is_eval_director, user_dict_is_financial_director logger = structlog.get_logger() diff --git a/conditional/blueprints/slideshow.py b/conditional/blueprints/slideshow.py index 2b3de163..ec290657 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_eval_director, 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_inactive, ldap_get_member, ldap_set_not_intro_member from conditional.util.user_dict import user_dict_is_eval_director diff --git a/conditional/util/flask.py b/conditional/util/flask.py index 0c9f435f..dbb337d3 100644 --- a/conditional/util/flask.py +++ b/conditional/util/flask.py @@ -4,19 +4,12 @@ from conditional.models.models import EvalSettings from conditional.util.auth import get_user -from conditional.util.ldap import ldap_is_active -from conditional.util.ldap import ldap_is_alumni -from conditional.util.ldap import ldap_is_eboard -from conditional.util.ldap import ldap_is_financial_director -from conditional.util.ldap import ldap_is_eval_director -from conditional.util.ldap import ldap_is_intromember -from conditional.util.ldap import ldap_is_rtp - from conditional.models.models import CommitteeMeeting from conditional.models.models import TechnicalSeminar from conditional import db -from conditional.util.user_dict import user_dict_is_active, user_dict_is_alumni, user_dict_is_eboard, user_dict_is_eval_director, user_dict_is_financial_director, user_dict_is_intromember, user_dict_is_rtp +from conditional.util.user_dict import user_dict_is_active, user_dict_is_alumni, user_dict_is_eboard, \ + user_dict_is_eval_director, user_dict_is_financial_director, user_dict_is_intromember, user_dict_is_rtp @get_user diff --git a/conditional/util/member.py b/conditional/util/member.py index f4e8efa5..fd68205a 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -1,5 +1,4 @@ from datetime import datetime -from sys import maxsize from sqlalchemy import func, or_ from conditional import start_of_year @@ -16,7 +15,6 @@ from conditional.util.ldap import ldap_get_current_students from conditional.util.ldap import ldap_get_intro_members from conditional.util.ldap import ldap_get_onfloor_members -from conditional.util.ldap import ldap_get_roomnumber from conditional.util.ldap import ldap_is_active from conditional.util.ldap import ldap_is_intromember from conditional.util.ldap import ldap_get_member @@ -43,7 +41,7 @@ def get_members_info_active_and_onfloor(): if onfloor: onfloor_set.add(uid) - room = ldap_get_roomnumber(account) + room = account.roomNumber hp = account.housingPoints member_list.append({ "uid": uid, @@ -106,7 +104,7 @@ def get_all_onfloor_members() -> set[str]: return {members.uid for members in ldap_get_onfloor_members()} @service_cache(maxsize=1024) -def get_onfloor_members(): +def get_onfloor_members() -> set[str]: return get_active_members() & get_all_onfloor_members() def get_cm(member): From 61b091214c92ff2c69d6218cc25c355fb444c7d3 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Fri, 20 Feb 2026 11:15:00 -0500 Subject: [PATCH 5/6] fix eboard group names SIGH --- conditional/util/user_dict.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conditional/util/user_dict.py b/conditional/util/user_dict.py index 0015239b..bfc9ad3a 100644 --- a/conditional/util/user_dict.py +++ b/conditional/util/user_dict.py @@ -23,10 +23,10 @@ def user_dict_is_onfloor(user_dict) -> bool: return user_dict_is_in_group(user_dict, 'onfloor') def user_dict_is_financial_director(user_dict) -> bool: - return user_dict_is_in_group(user_dict, 'financial') + return user_dict_is_in_group(user_dict, 'eboard-financial') def user_dict_is_eval_director(user_dict) -> bool: - return user_dict_is_in_group(user_dict, 'evaluations') + return user_dict_is_in_group(user_dict, 'eboard-evaluations') def user_dict_is_current_student(user_dict) -> bool: return user_dict_is_in_group(user_dict, 'current_student') From c9828dfc3bbe506d2464dba741aaaa3eb0dd0ed2 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Fri, 20 Feb 2026 11:42:51 -0500 Subject: [PATCH 6/6] moved migrations out of dockerfile and updated documentation --- Dockerfile | 1 - README.md | 85 +++++++++++++++++++++++++++++++-------------- docker-compose.yaml | 2 ++ 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3396bd6e..2817415d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,6 @@ ENV PORT=${PORT} EXPOSE ${PORT} COPY conditional /opt/conditional/conditional -COPY migrations /opt/conditional/migrations COPY *.py package.json /opt/conditional COPY --from=build-frontend /opt/conditional/conditional/static /opt/conditional/conditional/static diff --git a/README.md b/README.md index 61bf97ef..033275be 100644 --- a/README.md +++ b/README.md @@ -8,54 +8,82 @@ A comprehensive membership evaluations solution for Computer Science House. Development ----------- -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: +### Config + +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'] +} ``` + +#### 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 + +### 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: + +```sh virtualenv .conditionalenv -p `which python3` source .conditionalenv/bin/activate pip install -r requirements.txt -export FLASK_APP=app.py ``` -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): +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): -``` +```sh nvm install nvm use -npm install -g gulp +npm install -g webpack ``` -Then, install the pipeline and frontend dependencies: +Then, install the pipeline and frontend dependencies: (do this in the `frontend` directory) -``` +```sh npm install ``` -### Config +Once you have all of the dependencies installed, run -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. +```sh +npm webpack +``` -#### Add OIDC Config -Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth +This will build the frontend assets and put them in the correct place for use with flask + +Finally, start the flask app with `gunicorn` + +```sh +gunicorn ``` -# 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'] -} + +or + +```sh +python -m gunicorn ``` -### Run +### Run (containerized) -Once you have all of the dependencies installed, simply run: +It is likely easier to use containers like `podman` or `docker` or the corresponding compose file -``` -npm start +With podman, I have been using + +```sh +podman compose up --force-recreate --build ``` -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. +Which can be restarted every time changes are made ### Dependencies @@ -63,17 +91,20 @@ To add new dependencies, add them to `requirements.in` and then run `pip-compile ### Local database -You can run the database locally using the docker compose, make sure to upgrade it as explained below +You can run the database locally using the docker compose To populate it with dev data for example, you can use the command -``` -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 +```sh +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 ``` This can be helpful for changing the database schema -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 +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 +```sh +podman exec conditional flask db upgrade +``` ### Database Migrations diff --git a/docker-compose.yaml b/docker-compose.yaml index c5ff7a4b..755ee6df 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,6 +7,8 @@ services: - conditional-postgres ports: - "127.0.0.1:8080:8080" + volumes: + - ./migrations:/opt/conditional/migrations conditional-postgres: image: docker.io/postgres container_name: conditional-postgres