From c4a03d69c617ce258aa76a249c643839385a7c48 Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Feb 2026 15:47:16 -0500 Subject: [PATCH 1/3] Gatekeep fix (#465) * Fix Gatekeep to constitional requirements * Gatekeep does all constitutional things now * rename variable for legibility * fix json response * lint fix --------- Co-authored-by: Noah Hanford (spaced) --- conditional/__init__.py | 11 +++- conditional/util/member.py | 105 ++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/conditional/__init__.py b/conditional/__init__.py index 1df474ed..83c02a63 100644 --- a/conditional/__init__.py +++ b/conditional/__init__.py @@ -108,7 +108,7 @@ def database_processor(logger, log_method, event_dict): # pylint: disable=unuse # pylint: disable=wrong-import-order from conditional.util import context_processors from conditional.util.auth import get_user -from conditional.util.member import gatekeep_status +from conditional.util.member import gatekeep_status, get_voting_members from .blueprints.dashboard import dashboard_bp # pylint: disable=ungrouped-imports from .blueprints.attendance import attendance_bp from .blueprints.major_project_submission import major_project_bp @@ -167,7 +167,7 @@ def health(): @app.route("/gatekeep/") -def gatekeep(username): +def gatekeep_user(username): token = request.headers.get("X-VOTE-TOKEN", "") if token != app.config["VOTE_TOKEN"]: return "Users cannot access this page", 403 @@ -178,6 +178,13 @@ def gatekeep(username): return gatekeep_data, 200 +@app.route("/gatekeep") +def gatekeep_all(): + token = request.headers.get("X-VOTE-TOKEN", "") + if token != app.config["VOTE_TOKEN"]: + return "Users cannot access this page", 403 + return list(get_voting_members()), 200 + @app.errorhandler(404) @app.errorhandler(500) diff --git a/conditional/util/member.py b/conditional/util/member.py index 8dc3d69a..b485d193 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -2,7 +2,7 @@ from sqlalchemy import func, or_ from conditional import start_of_year -from conditional.models.models import CommitteeMeeting +from conditional.models.models import CommitteeMeeting, FreshmanAccount from conditional.models.models import CurrentCoops from conditional.models.models import FreshmanEvalData from conditional.models.models import HouseMeeting @@ -21,6 +21,7 @@ from conditional.util.ldap import ldap_is_intromember from conditional.util.ldap import ldap_get_member + @service_cache(maxsize=1024) def get_members_info(): members = ldap_get_current_students() @@ -57,18 +58,18 @@ def get_freshman_data(user_name): MemberCommitteeAttendance.query.filter( MemberCommitteeAttendance.uid == user_name ) if CommitteeMeeting.query.filter( - CommitteeMeeting.id == m.meeting_id).first().approved] + CommitteeMeeting.id == m.meeting_id).first().approved] freshman['committee_meetings'] = len(c_meetings) # technical seminar total t_seminars = [s.seminar_id for s in MemberSeminarAttendance.query.filter( MemberSeminarAttendance.uid == user_name ) if TechnicalSeminar.query.filter( - TechnicalSeminar.id == s.seminar_id).first().approved] + TechnicalSeminar.id == s.seminar_id).first().approved] freshman['ts_total'] = len(t_seminars) attendance = [m.name for m in TechnicalSeminar.query.filter( TechnicalSeminar.id.in_(t_seminars) - )] + )] freshman['ts_list'] = attendance @@ -114,20 +115,20 @@ def get_cm(member): def get_hm(member, only_absent=False): h_meetings = MemberHouseMeetingAttendance.query.outerjoin( - HouseMeeting, - MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id).with_entities( - MemberHouseMeetingAttendance.meeting_id, - MemberHouseMeetingAttendance.attendance_status, - HouseMeeting.date).filter( - HouseMeeting.date > start_of_year(), - MemberHouseMeetingAttendance.uid == member.uid) + HouseMeeting, + MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id).with_entities( + MemberHouseMeetingAttendance.meeting_id, + MemberHouseMeetingAttendance.attendance_status, + HouseMeeting.date).filter( + HouseMeeting.date > start_of_year(), + MemberHouseMeetingAttendance.uid == member.uid) if only_absent: h_meetings = h_meetings.filter(MemberHouseMeetingAttendance.attendance_status == "Absent") return h_meetings # @service_cache(maxsize=128) -def req_cm(uid, members_on_coop = None): +def req_cm(uid, members_on_coop=None): # Get the number of required committee meetings based on if the member # is going on co-op in the current operating session. on_coop = False @@ -143,14 +144,16 @@ def req_cm(uid, members_on_coop = None): return 15 return 30 + @service_cache(maxsize=256) def get_voting_members(): - if datetime.today() < datetime(start_of_year().year, 12, 31): + today = datetime.today() + if today < datetime(start_of_year().year, 12, 31): semester = "Fall" - semester_start = datetime(start_of_year().year,6,1) + semester_start = datetime(start_of_year().year, 6, 1) else: semester = "Spring" - semester_start = datetime(start_of_year().year + 1,1,1) + semester_start = datetime(start_of_year().year + 1, 1, 1) active_members = set(ldap_get_active_members()) intro_members = set(ldap_get_intro_members()) @@ -169,8 +172,8 @@ def get_voting_members(): coop_members = set(coop_members) passed_fall_members = FreshmanEvalData.query.filter( - FreshmanEvalData.freshman_eval_result == "Passed", - FreshmanEvalData.eval_date > start_of_year(), + FreshmanEvalData.freshman_eval_result == "Passed", + FreshmanEvalData.eval_date > start_of_year(), ).with_entities( func.array_agg(FreshmanEvalData.uid) ).scalar() @@ -185,6 +188,12 @@ def get_voting_members(): elligible_members = (active_not_intro - coop_members) | passed_fall_members + # Check to see if there's an Intro Evals in the future of this semester. If there is, everyone gets to vote! + before_evals_one = len(FreshmanAccount.query.filter(FreshmanAccount.eval_date > today).limit(1).all()) + before_evals_two = len(FreshmanEvalData.query.filter(FreshmanEvalData.eval_date > today).limit(1).all()) + if before_evals_one > 0 or before_evals_two > 0: + return elligible_members + passing_dm = set(member.uid for member in MemberCommitteeAttendance.query.join( CommitteeMeeting, MemberCommitteeAttendance.meeting_id == CommitteeMeeting.id @@ -200,7 +209,7 @@ def get_voting_members(): ).group_by( MemberCommitteeAttendance.uid ).having( - func.count(MemberCommitteeAttendance.uid) >= 6 #pylint: disable=not-callable + func.count(MemberCommitteeAttendance.uid) >= 6 # pylint: disable=not-callable ).with_entities( MemberCommitteeAttendance.uid ).all()) @@ -216,36 +225,47 @@ def get_voting_members(): ).group_by( MemberSeminarAttendance.uid ).having( - func.count(MemberSeminarAttendance.uid) >= 2 #pylint: disable=not-callable + func.count(MemberSeminarAttendance.uid) >= 2 # pylint: disable=not-callable ).all()) - passing_hm = set(member.uid for member in MemberHouseMeetingAttendance.query.join( + absent_hm = set(member.uid for member in MemberHouseMeetingAttendance.query.join( HouseMeeting, MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id ).filter( HouseMeeting.date >= semester_start, or_( - MemberHouseMeetingAttendance.attendance_status == 'Attended', - # MemberHouseMeetingAttendance.attendance_status == 'Excused' + MemberHouseMeetingAttendance.attendance_status == 'Absent', ) ).with_entities( MemberHouseMeetingAttendance.uid ).group_by( MemberHouseMeetingAttendance.uid ).having( - func.count(MemberHouseMeetingAttendance.uid) >= 6 #pylint: disable=not-callable + func.count(MemberHouseMeetingAttendance.uid) > 1 # pylint: disable=not-callable ).all()) - passing_reqs = passing_dm & passing_ts & passing_hm + passing_reqs = (passing_dm & passing_ts) - absent_hm return elligible_members & passing_reqs + def gatekeep_status(username): - if datetime.today() < datetime(start_of_year().year, 12, 31): + today = datetime.today() + # Check to see if there's an Intro Evals in the future of this semester. If there is, everyone gets to vote! + before_evals_one = len(FreshmanAccount.query.filter(FreshmanAccount.eval_date > today).limit(1).all()) + before_evals_two = len(FreshmanEvalData.query.filter(FreshmanEvalData.eval_date > today).limit(1).all()) + if before_evals_one > 0 or before_evals_two > 0: + return { + "result": True, + "h_meetings_missed": 0, + "c_meetings": 0, + "t_seminars": 0, + } + if today < datetime(start_of_year().year, 12, 31): semester = "Fall" - semester_start = datetime(start_of_year().year,6,1) + semester_start = datetime(start_of_year().year, 6, 1) else: semester = "Spring" - semester_start = datetime(start_of_year().year + 1,1,1) + semester_start = datetime(start_of_year().year + 1, 1, 1) # groups ldap_member = ldap_get_member(username) @@ -253,21 +273,21 @@ def gatekeep_status(username): is_active_member = ldap_is_active(ldap_member) and not is_intro_member is_on_coop = ( - CurrentCoops.query.filter( - CurrentCoops.date_created > start_of_year(), - CurrentCoops.semester == semester, - CurrentCoops.uid == username, - ).first() - is not None + CurrentCoops.query.filter( + CurrentCoops.date_created > start_of_year(), + CurrentCoops.semester == semester, + CurrentCoops.uid == username, + ).first() + is not None ) passed_fall = ( - FreshmanEvalData.query.filter( - FreshmanEvalData.freshman_eval_result == "Passed", - FreshmanEvalData.eval_date > start_of_year(), - FreshmanEvalData.uid == username, - ).first() - is not None + FreshmanEvalData.query.filter( + FreshmanEvalData.freshman_eval_result == "Passed", + FreshmanEvalData.eval_date > start_of_year(), + FreshmanEvalData.uid == username, + ).first() + is not None ) eligibility_of_groups = (is_active_member and not is_on_coop) or passed_fall @@ -298,22 +318,23 @@ def gatekeep_status(username): .count() ) # number of house meetings attended in the current semester - h_meetings = ( + h_meetings_missed = ( MemberHouseMeetingAttendance.query.join( HouseMeeting, MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id, ) .filter( + MemberHouseMeetingAttendance.attendance_status == 'Absent', MemberHouseMeetingAttendance.uid == username, HouseMeeting.date >= semester_start ) .count() ) - result = eligibility_of_groups and (d_meetings >= 6 and t_seminars >= 2 and h_meetings >= 6) + result = eligibility_of_groups and (d_meetings >= 6 and t_seminars >= 2 and h_meetings_missed < 2) # pylint: disable=chained-comparison return { "result": result, - "h_meetings": h_meetings, + "h_meetings_missed": h_meetings_missed, "c_meetings": d_meetings, "t_seminars": t_seminars, } From 8c637fb2416dfec59012aa40068306d3d755e2c0 Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Thu, 12 Feb 2026 22:17:11 -0500 Subject: [PATCH 2/3] Fix frosh attendance (#464) * attempt to fix upgrading accounts breaking frosh attendance * actually fix hm attendance and reorder opperations to create then delete * local db explaination * trailing whitespace :)))))))) * fix typo * fix variable name --- README.md | 14 ++- conditional/blueprints/member_management.py | 9 +- conditional/models/models.py | 6 +- conditional/util/member.py | 6 +- config.env.py | 2 +- docker-compose.yaml | 24 ++++ .../7a3904cac24b_freshmen_data_cascade.py | 36 ++++++ migrations/versions/e38beaf3e875_update_db.py | 111 ++++++++++++++++++ 8 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 docker-compose.yaml create mode 100644 migrations/versions/7a3904cac24b_freshmen_data_cascade.py create mode 100644 migrations/versions/e38beaf3e875_update_db.py diff --git a/README.md b/README.md index 1dcf9a51..61bf97ef 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,19 @@ This will run the asset pipeline, start the Python server, and start BrowserSync 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. -after app initialization +### Local database + +You can run the database locally using the docker compose, make sure to upgrade it as explained below + +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 +``` + +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 ### Database Migrations diff --git a/conditional/blueprints/member_management.py b/conditional/blueprints/member_management.py index 1927e440..a0973dfc 100644 --- a/conditional/blueprints/member_management.py +++ b/conditional/blueprints/member_management.py @@ -462,22 +462,20 @@ def member_management_upgrade_user(user_dict=None): db.session.add(new_acct) for fca in FreshmanCommitteeAttendance.query.filter(FreshmanCommitteeAttendance.fid == fid): db.session.add(MemberCommitteeAttendance(uid, fca.meeting_id)) - db.session.delete(fca) for fts in FreshmanSeminarAttendance.query.filter(FreshmanSeminarAttendance.fid == fid): db.session.add(MemberSeminarAttendance(uid, fts.seminar_id)) - db.session.delete(fts) for fhm in FreshmanHouseMeetingAttendance.query.filter(FreshmanHouseMeetingAttendance.fid == fid): # Don't duplicate HM attendance records mhm = MemberHouseMeetingAttendance.query.filter( - MemberHouseMeetingAttendance.meeting_id == fhm.meeting_id).first() + MemberHouseMeetingAttendance.meeting_id == fhm.meeting_id, + MemberHouseMeetingAttendance.uid == uid).first() if mhm is None: db.session.add(MemberHouseMeetingAttendance( uid, fhm.meeting_id, fhm.excuse, fhm.attendance_status)) else: log.info(f'Duplicate house meeting attendance! fid: {fid}, uid: {uid}, id: {fhm.meeting_id}') - db.session.delete(fhm) new_account = ldap_get_member(uid) if acct.onfloor_status: @@ -487,6 +485,9 @@ def member_management_upgrade_user(user_dict=None): if acct.room_number: ldap_set_roomnumber(new_account, acct.room_number) + db.session.flush() + db.session.commit() + db.session.delete(acct) db.session.flush() diff --git a/conditional/models/models.py b/conditional/models/models.py index 97e8875f..69014b8a 100644 --- a/conditional/models/models.py +++ b/conditional/models/models.py @@ -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'), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False) meeting_id = Column(ForeignKey('committee_meetings.id'), nullable=False) def __init__(self, fid, meeting_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'), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False) seminar_id = Column(ForeignKey('technical_seminars.id'), nullable=False) def __init__(self, fid, seminar_id): @@ -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'), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id', ondelete="cascade"), nullable=False) meeting_id = Column(ForeignKey('house_meetings.id'), nullable=False) excuse = Column(Text) attendance_status = Column(attendance_enum) diff --git a/conditional/util/member.py b/conditional/util/member.py index b485d193..e9b28e3e 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -186,13 +186,13 @@ def get_voting_members(): active_not_intro = active_members - intro_members active_not_intro = set(map(lambda member: member.uid, active_not_intro)) - elligible_members = (active_not_intro - coop_members) | passed_fall_members + eligible_members = (active_not_intro - coop_members) | passed_fall_members # Check to see if there's an Intro Evals in the future of this semester. If there is, everyone gets to vote! before_evals_one = len(FreshmanAccount.query.filter(FreshmanAccount.eval_date > today).limit(1).all()) before_evals_two = len(FreshmanEvalData.query.filter(FreshmanEvalData.eval_date > today).limit(1).all()) if before_evals_one > 0 or before_evals_two > 0: - return elligible_members + return eligible_members passing_dm = set(member.uid for member in MemberCommitteeAttendance.query.join( CommitteeMeeting, @@ -245,7 +245,7 @@ def get_voting_members(): passing_reqs = (passing_dm & passing_ts) - absent_hm - return elligible_members & passing_reqs + return eligible_members & passing_reqs def gatekeep_status(username): diff --git a/config.env.py b/config.env.py index c0ede697..705b2454 100644 --- a/config.env.py +++ b/config.env.py @@ -18,7 +18,7 @@ PROFILING = env.get("CONDITIONAL_PROFILING", "false").lower() == "true" # DB Info -SQLALCHEMY_DATABASE_URI = env.get("SQLALCHEMY_DATABASE_URI", "") +SQLALCHEMY_DATABASE_URI = "postgresql://conditional:fancypantspassword@conditional-postgres:5432/conditional" SQLALCHEMY_TRACK_MODIFICATIONS = False # LDAP config diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..c5ff7a4b --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,24 @@ +version: "3" +services: + conditional: + build: . + container_name: conditional + depends_on: + - conditional-postgres + ports: + - "127.0.0.1:8080:8080" + conditional-postgres: + image: docker.io/postgres + container_name: conditional-postgres + environment: + POSTGRES_PASSWORD: fancypantspassword + POSTGRES_USER: conditional + POSTGRES_DATABASE: conditional + ports: + - "127.0.0.1:5432:5432" + volumes: + - pgdata:/var/lib/postgresql + +volumes: + pgdata: + diff --git a/migrations/versions/7a3904cac24b_freshmen_data_cascade.py b/migrations/versions/7a3904cac24b_freshmen_data_cascade.py new file mode 100644 index 00000000..18cac087 --- /dev/null +++ b/migrations/versions/7a3904cac24b_freshmen_data_cascade.py @@ -0,0 +1,36 @@ +"""Freshmen data cascade + +Revision ID: 7a3904cac24b +Revises: e38beaf3e875 +Create Date: 2026-02-03 12:14:37.119352 + +""" + +# revision identifiers, used by Alembic. +revision = '7a3904cac24b' +down_revision = 'e38beaf3e875' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('freshman_committee_attendance_fid_fkey', 'freshman_committee_attendance', type_='foreignkey') + op.create_foreign_key(None, 'freshman_committee_attendance', 'freshman_accounts', ['fid'], ['id'], ondelete='cascade') + op.drop_constraint('freshman_hm_attendance_fid_fkey', 'freshman_hm_attendance', type_='foreignkey') + op.create_foreign_key(None, 'freshman_hm_attendance', 'freshman_accounts', ['fid'], ['id'], ondelete='cascade') + op.drop_constraint('freshman_seminar_attendance_fid_fkey', 'freshman_seminar_attendance', type_='foreignkey') + op.create_foreign_key(None, 'freshman_seminar_attendance', 'freshman_accounts', ['fid'], ['id'], ondelete='cascade') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'freshman_seminar_attendance', type_='foreignkey') + op.create_foreign_key('freshman_seminar_attendance_fid_fkey', 'freshman_seminar_attendance', 'freshman_accounts', ['fid'], ['id']) + op.drop_constraint(None, 'freshman_hm_attendance', type_='foreignkey') + op.create_foreign_key('freshman_hm_attendance_fid_fkey', 'freshman_hm_attendance', 'freshman_accounts', ['fid'], ['id']) + op.drop_constraint(None, 'freshman_committee_attendance', type_='foreignkey') + op.create_foreign_key('freshman_committee_attendance_fid_fkey', 'freshman_committee_attendance', 'freshman_accounts', ['fid'], ['id']) + # ### end Alembic commands ### diff --git a/migrations/versions/e38beaf3e875_update_db.py b/migrations/versions/e38beaf3e875_update_db.py new file mode 100644 index 00000000..2a367f9a --- /dev/null +++ b/migrations/versions/e38beaf3e875_update_db.py @@ -0,0 +1,111 @@ +"""update db + +Revision ID: e38beaf3e875 +Revises: 757e18146d16 +Create Date: 2026-02-03 12:12:11.451367 + +""" + +# revision identifiers, used by Alembic. +revision = 'e38beaf3e875' +down_revision = '757e18146d16' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index('member_batch_users_id_idx', table_name='member_batch_users') + op.drop_table('member_batch_users') + op.drop_index('freshman_batch_pulls_id_idx', table_name='freshman_batch_pulls') + op.drop_table('freshman_batch_pulls') + op.drop_index('member_batch_pulls_id_idx', table_name='member_batch_pulls') + op.drop_table('member_batch_pulls') + op.drop_index('freshman_batch_users_id_pkey', table_name='freshman_batch_users') + op.drop_table('freshman_batch_users') + op.drop_table('batch_conditions') + op.alter_column('freshman_accounts', 'onfloor_status', + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column('freshman_accounts', 'rit_username', + existing_type=sa.VARCHAR(length=10), + nullable=True) + op.alter_column('freshman_hm_attendance', 'attendance_status', + existing_type=postgresql.ENUM('Attended', 'Excused', 'Absent', name='attendance_enum'), + nullable=True) + op.alter_column('member_hm_attendance', 'attendance_status', + existing_type=postgresql.ENUM('Attended', 'Excused', 'Absent', name='attendance_enum'), + nullable=True) + op.drop_table('batch') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('member_hm_attendance', 'attendance_status', + existing_type=postgresql.ENUM('Attended', 'Excused', 'Absent', name='attendance_enum'), + nullable=False) + op.alter_column('freshman_hm_attendance', 'attendance_status', + existing_type=postgresql.ENUM('Attended', 'Excused', 'Absent', name='attendance_enum'), + nullable=False) + op.alter_column('freshman_accounts', 'rit_username', + existing_type=sa.VARCHAR(length=10), + nullable=False) + op.alter_column('freshman_accounts', 'onfloor_status', + existing_type=sa.BOOLEAN(), + nullable=False) + op.create_table('batch_conditions', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('value', sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column('condition', postgresql.ENUM('packet', 'seminar', 'committee', 'house', name='batch_ctype_enum'), autoincrement=False, nullable=False), + sa.Column('comparison', postgresql.ENUM('less', 'equal', 'greater', name='batch_comparison'), autoincrement=False, nullable=False), + sa.Column('batch_id', sa.INTEGER(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['batch_id'], ['batch.id'], name='batch_conditions_fk'), + sa.PrimaryKeyConstraint('id', name='batch_conditions_pkey') + ) + op.create_table('freshman_batch_users', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('fid', sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column('batch_id', sa.INTEGER(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['batch_id'], ['batch.id'], name='freshman_batch_users_fk'), + sa.ForeignKeyConstraint(['fid'], ['freshman_accounts.id'], name='freshman_batch_users_fk_1'), + sa.PrimaryKeyConstraint('id', name='freshman_batch_users_pkey') + ) + op.create_index('freshman_batch_users_id_pkey', 'freshman_batch_users', ['id'], unique=True) + op.create_table('batch', + sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('batch_id_seq'::regclass)"), autoincrement=True, nullable=False), + sa.Column('name', sa.TEXT(), autoincrement=False, nullable=False), + sa.Column('uid', sa.VARCHAR(length=32), autoincrement=False, nullable=False), + sa.Column('approved', sa.BOOLEAN(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('id', name='batch_pkey'), + postgresql_ignore_search_path=False + ) + op.create_table('member_batch_pulls', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('uid', sa.VARCHAR(length=32), autoincrement=False, nullable=False), + sa.Column('approved', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False), + sa.Column('reason', sa.TEXT(), server_default=sa.text("''::text"), autoincrement=False, nullable=False), + sa.Column('puller', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('id', name='member_batch_pulls_pkey') + ) + op.create_index('member_batch_pulls_id_idx', 'member_batch_pulls', ['id'], unique=True) + op.create_table('freshman_batch_pulls', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('fid', sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column('approved', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False), + sa.Column('reason', sa.TEXT(), server_default=sa.text("''::text"), autoincrement=False, nullable=False), + sa.Column('puller', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['fid'], ['freshman_accounts.id'], name='freshman_batch_pulls_fk_1'), + sa.PrimaryKeyConstraint('id', name='freshman_batch_pulls_pkey') + ) + op.create_index('freshman_batch_pulls_id_idx', 'freshman_batch_pulls', ['id'], unique=True) + op.create_table('member_batch_users', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('uid', sa.VARCHAR(length=32), autoincrement=False, nullable=False), + sa.Column('batch_id', sa.INTEGER(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['batch_id'], ['batch.id'], name='member_batch_users_fk'), + sa.PrimaryKeyConstraint('id', name='member_batch_users_pkey') + ) + op.create_index('member_batch_users_id_idx', 'member_batch_users', ['id'], unique=False) + # ### end Alembic commands ### From 45c3351bdfffcf3e178a39dc038117c4b92e3fb2 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Thu, 12 Feb 2026 22:35:53 -0500 Subject: [PATCH 3/3] guys i'm dumb --- config.env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.env.py b/config.env.py index 705b2454..77da644b 100644 --- a/config.env.py +++ b/config.env.py @@ -18,7 +18,7 @@ PROFILING = env.get("CONDITIONAL_PROFILING", "false").lower() == "true" # DB Info -SQLALCHEMY_DATABASE_URI = "postgresql://conditional:fancypantspassword@conditional-postgres:5432/conditional" +SQLALCHEMY_DATABASE_URI = env.get("SQLALCHEMY_DATABASE_URI", "postgresql://conditional:fancypantspassword@conditional-postgres:5432/conditional") SQLALCHEMY_TRACK_MODIFICATIONS = False # LDAP config