diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 38ed5fa..980b1ab 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -31,4 +31,4 @@ jobs: if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with pylint run: | - pylint conditional --disable=logging-fstring-interpolation + pylint conditional diff --git a/conditional/__init__.py b/conditional/__init__.py index 5fe7269..1c2f5ee 100644 --- a/conditional/__init__.py +++ b/conditional/__init__.py @@ -31,26 +31,19 @@ # Sentry setup sentry_sdk.init( - dsn=app.config["SENTRY_DSN"], + dsn=app.config['SENTRY_DSN'], integrations=[FlaskIntegration(), SqlalchemyIntegration()], - environment=app.config["SENTRY_ENV"], + environment=app.config['SENTRY_ENV'], ) -ldap = CSHLDAP( - app.config["LDAP_BIND_DN"], app.config["LDAP_BIND_PW"], ro=app.config["LDAP_RO"] -) +ldap = CSHLDAP(app.config['LDAP_BIND_DN'], + app.config['LDAP_BIND_PW'], + ro=app.config['LDAP_RO']) client_metadata = ClientMetadata(app.config["OIDC_CLIENT_CONFIG"]) -provider_config = ProviderConfiguration( - issuer=app.config["OIDC_ISSUER"], client_registration_info=client_metadata -) -frosh_provider_config = ProviderConfiguration( - issuer=app.config["FROSH_OIDC_ISSUER"], client_registration_info=client_metadata -) +provider_config = ProviderConfiguration(issuer=app.config["OIDC_ISSUER"], client_registration_info=client_metadata) -auth = OIDCAuthentication( - {"default": provider_config, "frosh": frosh_provider_config}, app -) +auth = OIDCAuthentication({'default': provider_config}, app) app.secret_key = app.config["SECRET_KEY"] @@ -67,48 +60,42 @@ def start_of_year(): # Configure Logging -def request_processor( - logger, log_method, event_dict -): # pylint: disable=unused-argument, redefined-outer-name - if "request" in event_dict: - flask_request = event_dict["request"] - event_dict["ip"] = flask_request.remote_addr - event_dict["method"] = flask_request.method - event_dict["blueprint"] = flask_request.blueprint - event_dict["path"] = flask_request.full_path - if "auth_dict" in event_dict: - auth_dict = event_dict["auth_dict"] - event_dict["user"] = auth_dict["username"] +def request_processor(logger, log_method, event_dict): # pylint: disable=unused-argument, redefined-outer-name + if 'request' in event_dict: + flask_request = event_dict['request'] + event_dict['ip'] = flask_request.remote_addr + event_dict['method'] = flask_request.method + event_dict['blueprint'] = flask_request.blueprint + event_dict['path'] = flask_request.full_path + if 'auth_dict' in event_dict: + auth_dict = event_dict['auth_dict'] + event_dict['user'] = auth_dict['username'] return event_dict -def database_processor( - logger, log_method, event_dict -): # pylint: disable=unused-argument, redefined-outer-name - if "request" in event_dict: - if event_dict["method"] != "GET": +def database_processor(logger, log_method, event_dict): # pylint: disable=unused-argument, redefined-outer-name + if 'request' in event_dict: + if event_dict['method'] != 'GET': log = UserLog( - ipaddr=event_dict["ip"], - user=event_dict["user"], - method=event_dict["method"], - blueprint=event_dict["blueprint"], - path=event_dict["path"], - description=event_dict["event"], + ipaddr=event_dict['ip'], + user=event_dict['user'], + method=event_dict['method'], + blueprint=event_dict['blueprint'], + path=event_dict['path'], + description=event_dict['event'] ) db.session.add(log) db.session.flush() db.session.commit() - del event_dict["request"] + del event_dict['request'] return event_dict -structlog.configure( - processors=[ - request_processor, - database_processor, - structlog.processors.KeyValueRenderer(), - ] -) +structlog.configure(processors=[ + request_processor, + database_processor, + structlog.processors.KeyValueRenderer() +]) logger = structlog.get_logger() @@ -128,7 +115,6 @@ def database_processor( from .blueprints.cache_management import cache_bp from .blueprints.co_op import co_op_bp from .blueprints.logs import log_bp -from .blueprints.packet import packet_bp app.register_blueprint(dashboard_bp) app.register_blueprint(attendance_bp) @@ -143,21 +129,20 @@ def database_processor( app.register_blueprint(cache_bp) app.register_blueprint(co_op_bp) app.register_blueprint(log_bp) -app.register_blueprint(packet_bp) from .util.ldap import ldap_get_member -@app.route("/") +@app.route('/') def static_proxy(path): # send_static_file will guess the correct MIME type return app.send_static_file(path) -@app.route("/") +@app.route('/') @auth.oidc_auth("default") def default_route(): - return redirect("/dashboard") + return redirect('/dashboard') @app.route("/logout") @@ -171,7 +156,7 @@ def health(): """ Shows an ok status if the application is up and running """ - return {"status": "ok"} + return {'status': 'ok'} @app.errorhandler(404) @@ -182,17 +167,17 @@ def route_errors(error, user_dict=None): data = {} # Handle the case where the header isn't present - if user_dict["username"] is not None: - data["username"] = user_dict["account"].uid - data["name"] = user_dict["account"].cn + if user_dict['username'] is not None: + data['username'] = user_dict['account'].uid + data['name'] = user_dict['account'].cn else: - data["username"] = "unknown" - data["name"] = "Unknown" + data['username'] = "unknown" + data['name'] = "Unknown" # Figure out what kind of error was passed if isinstance(error, int): code = error - elif hasattr(error, "code"): + elif hasattr(error, 'code'): code = error.code else: # Unhandled exception @@ -204,13 +189,11 @@ def route_errors(error, user_dict=None): else: error_desc = type(error).__name__ - return render_template( - "errors.html", - error=error_desc, - error_code=code, - event_id=sentry_sdk.last_event_id(), - **data - ), int(code) + return render_template('errors.html', + error=error_desc, + error_code=code, + event_id=sentry_sdk.last_event_id(), + **data), int(code) -logger.info("conditional started") +logger.info('conditional started') diff --git a/conditional/blueprints/packet.py b/conditional/blueprints/packet.py deleted file mode 100644 index 3a2a533..0000000 --- a/conditional/blueprints/packet.py +++ /dev/null @@ -1,443 +0,0 @@ -import json -from datetime import datetime -from operator import itemgetter - -import structlog -from flask import Blueprint, redirect, render_template, request, session - -from conditional import auth, app, db -from conditional.util import stats as stats_module -from conditional.util.context_processors import get_freshman_name -from conditional.util.mail import send_report_mail -from conditional.util.auth import get_user, needs_auth -from conditional.util.ldap import ldap_is_eval_director -from conditional.util.packet import ( - create_new_packets, - sync_freshman_list, - sync_with_ldap, -) -from conditional.models.models import ( - MiscSignature, - Packet, - Freshman, -) - -logger = structlog.get_logger() - -packet_bp = Blueprint("packet_bp", __name__, url_prefix="/packet") - - -class POSTFreshman: - def __init__(self, freshman): - self.name = freshman["name"].strip() - self.rit_username = freshman["rit_username"].strip() - self.onfloor = freshman["onfloor"].strip() == "TRUE" - - -@packet_bp.route("/admin/packets") -@auth.oidc_auth("default") -@get_user -def admin_packets(user_dict=None): - if not ldap_is_eval_director(user_dict["account"]): - return redirect("/dashboard") - - open_packets = Packet.open_packets() - - # Pre-calculate and store the return values of did_sign(), signatures_received(), and signatures_required() - for packet in open_packets: - packet.did_sign_result = packet.did_sign( - user_dict["username"], session["provider"] == "csh" - ) - packet.signatures_received_result = packet.signatures_received() - packet.signatures_required_result = packet.signatures_required() - - open_packets.sort(key=packet_sort_key, reverse=True) - - return render_template( - "admin_packets.html", open_packets=open_packets, info=user_dict - ) - - -@packet_bp.route("/admin/freshmen") -@auth.oidc_auth("default") -@get_user -def admin_freshmen(user_dict=None): - if not ldap_is_eval_director(user_dict["account"]): - return redirect("/dashboard") - - all_freshmen = Freshman.get_all() - - return render_template( - "admin_freshmen.html", all_freshmen=all_freshmen, info=user_dict - ) - - -@packet_bp.route("/api/v1/freshmen", methods=["POST"]) -@auth.oidc_auth("default") -@get_user -def sync_freshman(user_dict=None): - """ - Create or update freshmen entries from a list - - Body parameters: [ - { - rit_username: string - name: string - onfloor: boolean - } - ] - """ - - # Only allow evals to create new frosh - if not ldap_is_eval_director(user_dict["account"]): - redirect("/dashboard") - - freshmen_in_post = { - freshman.rit_username: freshman for freshman in map(POSTFreshman, request.json) - } - sync_freshman_list(freshmen_in_post) - return json.dumps("Done"), 200 - - -@packet_bp.route("/api/v1/packets", methods=["POST"]) -@auth.oidc_auth("default") -@get_user -def create_packet(user_dict=None): - """ - Create a new packet. - - Body parameters: { - start_date: the start date of the packets in MM/DD/YYYY format - freshmen: [ - { - rit_username: string - name: string - onfloor: boolean - } - ] - } - """ - - # Only allow evals to create new packets - if not ldap_is_eval_director(user_dict["account"]): - redirect("/dashboard") - - base_date = datetime.strptime(request.json["start_date"], "%m/%d/%Y").date() - - freshmen_in_post = { - freshman.rit_username: freshman - for freshman in map(POSTFreshman, request.json["freshmen"]) - } - - create_new_packets(base_date, freshmen_in_post) - - return json.dumps("Done"), 201 - - -@packet_bp.route("/api/v1/sync", methods=["POST"]) -@auth.oidc_auth("default") -@get_user -def sync_ldap(user_dict=None): - # Only allow evals to sync ldap - if not ldap_is_eval_director(user_dict["account"]): - redirect("/dashboard") - sync_with_ldap() - return json.dumps("Done"), 201 - - -@packet_bp.route("/api/v1/packets/", methods=["GET"]) -@auth.oidc_auth("default") -@get_user -def get_packets_by_user(username: str, user_dict=None) -> dict: - """ - Return a dictionary of packets for a freshman by username, giving packet start and end date by packet id - """ - - if user_dict["ritdn"] != username: - redirect("/dashboard") - frosh = Freshman.by_username(username) - - return { - packet.id: { - "start": packet.start, - "end": packet.end, - } - for packet in frosh.packets - } - - -@packet_bp.route("/api/v1/packets//newest", methods=["GET"]) -@auth.oidc_auth("default") -@get_user -def get_newest_packet_by_user(username: str, user_dict=None) -> dict: - """ - Return a user's newest packet - """ - - if not user_dict["is_upper"] and user_dict["ritdn"] != username: - redirect("/dashboard") - - frosh = Freshman.by_username(username) - - packet = frosh.packets[-1] - - return { - packet.id: { - "start": packet.start, - "end": packet.end, - "required": vars(packet.signatures_required()), - "received": vars(packet.signatures_received()), - } - } - - -@packet_bp.route("/api/v1/packet/", methods=["GET"]) -@auth.oidc_auth("default") -@get_user -def get_packet_by_id(packet_id: int, user_dict=None) -> dict: - """ - Return the scores of the packet in question - """ - - packet = Packet.by_id(packet_id) - - if user_dict["ritdn"] != packet.freshman.rit_username: - redirect("/dashboard") - - return { - "required": vars(packet.signatures_required()), - "received": vars(packet.signatures_received()), - } - - -@packet_bp.route("/api/v1/sign//", methods=["POST"]) -@needs_auth -def sign(packet_id, user_dict=None): - packet = Packet.by_id(packet_id) - - if packet is not None and packet.is_open(): - if session["provider"] == "csh": - # Check if the CSHer is an upperclassman and if so, sign that row - for sig in filter( - lambda sig: sig.member == user_dict["uid"], packet.upper_signatures - ): - sig.signed = True - app.logger.info( - f"Member {user_dict['uid']} signed packet {packet_id} as an upperclassman" - ) - return commit_sig(packet) - - # The CSHer is a misc so add a new row - db.session.add(MiscSignature(packet=packet, member=user_dict["uid"])) - app.logger.info( - f"Member {user_dict['uid']} signed packet {packet_id} as a misc" - ) - return commit_sig(packet) - if session["provider"] == "frosh": - # Check if the freshman is onfloor and if so, sign that row - for sig in filter( - lambda sig: sig.freshman_username == user_dict["uid"], - packet.fresh_signatures, - ): - sig.signed = True - app.logger.info( - f"Freshman {user_dict['uid']} signed packet {packet_id}" - ) - return commit_sig(packet) - - app.logger.warning( - f"Failed to add {user_dict['uid']}'s signature to packet {packet_id}" - ) - return "Error: Signature not valid. Reason: Unknown" - - -@packet_bp.route("/api/v1/report/", methods=["POST"]) -@needs_auth -def report(user_dict=None): - if session["provider"] != "frosh": - return "Failure", 403 - - form_results = request.form - send_report_mail(form_results, get_freshman_name(user_dict["username"])) - return "Success: " + get_freshman_name(user_dict["username"]) + " sent a report" - - -@packet_bp.route("/api/v1/stats/packet/") -@auth.oidc_auth("default") -@get_user -def packet_stats(packet_id, user_dict=None): - if user_dict["ritdn"] != Packet.by_id(packet_id).freshman.rit_username: - return redirect("/dashboard") - return stats_module.packet_stats(packet_id) - - -@packet_bp.route("/api/v1/stats/upperclassman/") -@auth.oidc_auth("default") -@get_user -def upperclassman_stats(uid): - return stats_module.upperclassman_stats(uid) - - -def commit_sig(packet): - db.session.commit() - - return "Success: Signed Packet: " + packet.freshman_username - - -@packet_bp.route("/packet//") -@needs_auth -def freshman_packet(packet_id, user_dict=None): - packet = Packet.by_id(packet_id) - - if packet is None: - return "Invalid packet or freshman", 404 - - # The current user's freshman signature on this packet - fresh_sig = list( - filter( - lambda sig: ( - sig.freshman_username == user_dict["ritdn"] if user_dict else "" - ), - packet.fresh_signatures, - ) - ) - - return render_template( - "packet.html", - info=user_dict, - packet=packet, - did_sign=packet.did_sign(user_dict["uid"], session["provider"] == "csh"), - required=packet.signatures_required(), - received=packet.signatures_received(), - upper=packet.upper_signatures, - fresh_sig=fresh_sig, - ) - - -def packet_sort_key(packet): - """ - Utility function for generating keys for sorting packets - """ - return ( - packet.freshman.name, - -packet.signatures_received_result.total, - not packet.did_sign_result, - ) - - -@packet_bp.route("/packets/") -@needs_auth -def packets(user_dict=None): - open_packets = Packet.open_packets() - - # Pre-calculate and store the return values of did_sign(), signatures_received(), and signatures_required() - for packet in open_packets: - packet.did_sign_result = packet.did_sign( - user_dict["uid"], session["provider"] == "csh" - ) - packet.signatures_received_result = packet.signatures_received() - packet.signatures_required_result = packet.signatures_required() - - open_packets.sort(key=packet_sort_key) - - return render_template("active_packets.html", info=user_dict, packets=open_packets) - - -@packet_bp.route("/") -def index(): - return """ -

Hello, world! 2

- Click here 4 frosh - Click here 4 upper - """ - - -@packet_bp.route("/upperclassmen/") -@auth.oidc_auth("default") -@get_user -def upperclassmen_total(user_dict=None): - open_packets = Packet.open_packets() - - # Sum up the signed packets per upperclassman - upperclassmen = {} - misc = {} - for packet in open_packets: - for sig in packet.upper_signatures: - if sig.member not in upperclassmen: - upperclassmen[sig.member] = 0 - - if sig.signed: - upperclassmen[sig.member] += 1 - for sig in packet.misc_signatures: - misc[sig.member] = 1 + misc.get(sig.member, 0) - - return render_template( - "upperclassmen_totals.html", - info=user_dict, - num_open_packets=len(open_packets), - upperclassmen=sorted(upperclassmen.items(), key=itemgetter(1), reverse=True), - misc=sorted(misc.items(), key=itemgetter(1), reverse=True), - ) - - -@packet_bp.route("/stats/packet/") -@auth.oidc_auth("default") -@get_user -def packet_graphs(packet_id, user_dict=None): - stats = packet_stats(packet_id) - fresh = [] - misc = [] - upper = [] - - # Make a rolling sum of signatures over time - def agg(l, attr, date): - l.append((l[-1] if l else 0) + len(stats["dates"][date][attr])) - - dates = list(stats["dates"].keys()) - for date in dates: - agg(fresh, "fresh", date) - agg(misc, "misc", date) - agg(upper, "upper", date) - - # Stack misc and upper on top of fresh for a nice stacked line graph - for i in range(len(dates)): - misc[i] = misc[i] + fresh[i] - upper[i] = upper[i] + misc[i] - - return render_template( - "packet_stats.html", - info=user_dict, - data=json.dumps( - { - "dates": dates, - "accum": { - "fresh": fresh, - "misc": misc, - "upper": upper, - }, - "daily": {}, - } - ), - fresh=stats["freshman"], - packet=Packet.by_id(packet_id), - ) - - -@packet_bp.route("/auth/csh") -@auth.oidc_auth("default") -def csh_login(): - session["provider"] = "csh" - return redirect("/packet", code=301) - - -@packet_bp.route("/auth/frosh") -@auth.oidc_auth("frosh") -def frosh_login(): - session["provider"] = "frosh" - return redirect("/packet", code=301) - - -@packet_bp.route("/logout") -@auth.oidc_logout -def logout(): - return redirect("/", 302) diff --git a/conditional/models/models.py b/conditional/models/models.py index 5bb3dd6..423795e 100644 --- a/conditional/models/models.py +++ b/conditional/models/models.py @@ -1,27 +1,15 @@ -from typing import cast, Optional import time from datetime import date, timedelta, datetime -from itertools import chain -from sqlalchemy import ( - Column, - Integer, - String, - Enum, - ForeignKey, - DateTime, - Date, - Text, - Boolean, -) +from sqlalchemy import Column, Integer, String, Enum, ForeignKey, DateTime, \ + Date, Text, Boolean from sqlalchemy.dialects import postgresql -from sqlalchemy.orm import relationship from conditional import db -attendance_enum = Enum("Attended", "Excused", "Absent", name="attendance_enum") +attendance_enum = Enum('Attended', 'Excused', 'Absent', name='attendance_enum') class FreshmanAccount(db.Model): - __tablename__ = "freshman_accounts" + __tablename__ = 'freshman_accounts' id = Column(Integer, primary_key=True) name = Column(String(64), nullable=False) eval_date = Column(Date, nullable=False) @@ -30,9 +18,7 @@ class FreshmanAccount(db.Model): signatures_missed = Column(Integer) rit_username = Column(String(10), nullable=True) - def __init__( - self, name, onfloor, room=None, missed=None, rit_username=None - ): # pylint: disable=too-many-positional-arguments + def __init__(self, name, onfloor, room=None, missed=None, rit_username=None): # pylint: disable=too-many-positional-arguments self.name = name today = date.fromtimestamp(time.time()) self.eval_date = today + timedelta(weeks=6) @@ -43,25 +29,22 @@ def __init__( class FreshmanEvalData(db.Model): - __tablename__ = "freshman_eval_data" + __tablename__ = 'freshman_eval_data' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) - freshman_project = Column( - Enum("Pending", "Passed", "Failed", name="freshman_project_enum"), nullable=True - ) + freshman_project = Column(Enum('Pending', 'Passed', 'Failed', name="freshman_project_enum"), nullable=True) eval_date = Column(DateTime, nullable=False) signatures_missed = Column(Integer, nullable=False) social_events = Column(Text) other_notes = Column(Text) - freshman_eval_result = Column( - Enum("Pending", "Passed", "Failed", name="freshman_eval_enum"), nullable=False - ) + freshman_eval_result = Column(Enum('Pending', 'Passed', 'Failed', + name="freshman_eval_enum"), nullable=False) active = Column(Boolean) def __init__(self, uid, signatures_missed): self.uid = uid self.freshman_project = None - self.freshman_eval_result = "Pending" + self.freshman_eval_result = 'Pending' self.signatures_missed = signatures_missed self.social_events = "" self.other_notes = "" @@ -69,24 +52,12 @@ def __init__(self, uid, signatures_missed): class CommitteeMeeting(db.Model): - __tablename__ = "committee_meetings" + __tablename__ = 'committee_meetings' id = Column(Integer, primary_key=True) - committee = Column( - Enum( - "Evaluations", - "History", - "Social", - "Opcomm", - "R&D", - "House Improvements", - "Financial", - "Public Relations", - "Chairman", - "Ad-Hoc", - name="committees_enum", - ), - nullable=False, - ) + committee = Column(Enum('Evaluations', 'History', 'Social', 'Opcomm', + 'R&D', 'House Improvements', 'Financial', + 'Public Relations', 'Chairman', 'Ad-Hoc', name="committees_enum"), + nullable=False) timestamp = Column(DateTime, nullable=False) approved = Column(Boolean, nullable=False) active = Column(Boolean) @@ -99,10 +70,10 @@ def __init__(self, committee, timestamp, approved): class MemberCommitteeAttendance(db.Model): - __tablename__ = "member_committee_attendance" + __tablename__ = 'member_committee_attendance' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) - meeting_id = Column(ForeignKey("committee_meetings.id"), nullable=False) + meeting_id = Column(ForeignKey('committee_meetings.id'), nullable=False) def __init__(self, uid, meeting_id): self.uid = uid @@ -110,10 +81,10 @@ def __init__(self, uid, meeting_id): class FreshmanCommitteeAttendance(db.Model): - __tablename__ = "freshman_committee_attendance" + __tablename__ = 'freshman_committee_attendance' id = Column(Integer, primary_key=True) - fid = Column(ForeignKey("freshman_accounts.id"), nullable=False) - meeting_id = Column(ForeignKey("committee_meetings.id"), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id'), nullable=False) + meeting_id = Column(ForeignKey('committee_meetings.id'), nullable=False) def __init__(self, fid, meeting_id): self.fid = fid @@ -121,7 +92,7 @@ def __init__(self, fid, meeting_id): class TechnicalSeminar(db.Model): - __tablename__ = "technical_seminars" + __tablename__ = 'technical_seminars' id = Column(Integer, primary_key=True) name = Column(String(128), nullable=False) timestamp = Column(DateTime, nullable=False) @@ -136,10 +107,10 @@ def __init__(self, name, timestamp, approved): class MemberSeminarAttendance(db.Model): - __tablename__ = "member_seminar_attendance" + __tablename__ = 'member_seminar_attendance' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) - seminar_id = Column(ForeignKey("technical_seminars.id"), nullable=False) + seminar_id = Column(ForeignKey('technical_seminars.id'), nullable=False) def __init__(self, uid, seminar_id): self.uid = uid @@ -147,10 +118,10 @@ def __init__(self, uid, seminar_id): class FreshmanSeminarAttendance(db.Model): - __tablename__ = "freshman_seminar_attendance" + __tablename__ = 'freshman_seminar_attendance' id = Column(Integer, primary_key=True) - fid = Column(ForeignKey("freshman_accounts.id"), nullable=False) - seminar_id = Column(ForeignKey("technical_seminars.id"), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id'), nullable=False) + seminar_id = Column(ForeignKey('technical_seminars.id'), nullable=False) def __init__(self, fid, seminar_id): self.fid = fid @@ -158,7 +129,7 @@ def __init__(self, fid, seminar_id): class MajorProject(db.Model): - __tablename__ = "major_projects" + __tablename__ = 'major_projects' id = Column(Integer, primary_key=True) date = Column(Date, nullable=False) uid = Column(String(32), nullable=False) @@ -167,25 +138,23 @@ class MajorProject(db.Model): time = Column(Text, nullable=False) description = Column(Text, nullable=False) active = Column(Boolean, nullable=False) - status = Column( - Enum("Pending", "Passed", "Failed", name="major_project_enum"), nullable=False - ) + status = Column(Enum('Pending', 'Passed', 'Failed', + name="major_project_enum"), + nullable=False) - def __init__( - self, uid, name, tldr, time, desc - ): # pylint: disable=too-many-positional-arguments,redefined-outer-name + def __init__(self, uid, name, tldr, time, desc): # pylint: disable=too-many-positional-arguments,redefined-outer-name self.uid = uid self.date = datetime.now() self.name = name self.tldr = tldr self.time = time self.description = desc - self.status = "Pending" + self.status = 'Pending' self.active = True class HouseMeeting(db.Model): - __tablename__ = "house_meetings" + __tablename__ = 'house_meetings' id = Column(Integer, primary_key=True) date = Column(Date, nullable=False) active = Column(Boolean, nullable=False) @@ -196,10 +165,10 @@ def __init__(self, hm_date): class MemberHouseMeetingAttendance(db.Model): - __tablename__ = "member_hm_attendance" + __tablename__ = 'member_hm_attendance' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) - meeting_id = Column(ForeignKey("house_meetings.id"), nullable=False) + meeting_id = Column(ForeignKey('house_meetings.id'), nullable=False) excuse = Column(Text) attendance_status = Column(attendance_enum) @@ -211,10 +180,10 @@ def __init__(self, uid, meeting_id, excuse, status): class FreshmanHouseMeetingAttendance(db.Model): - __tablename__ = "freshman_hm_attendance" + __tablename__ = 'freshman_hm_attendance' id = Column(Integer, primary_key=True) - fid = Column(ForeignKey("freshman_accounts.id"), nullable=False) - meeting_id = Column(ForeignKey("house_meetings.id"), nullable=False) + fid = Column(ForeignKey('freshman_accounts.id'), nullable=False) + meeting_id = Column(ForeignKey('house_meetings.id'), nullable=False) excuse = Column(Text) attendance_status = Column(attendance_enum) @@ -226,11 +195,11 @@ def __init__(self, fid, meeting_id, excuse, status): class CurrentCoops(db.Model): - __tablename__ = "current_coops" + __tablename__ = 'current_coops' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) date_created = Column(Date, nullable=False) - semester = Column(Enum("Fall", "Spring", name="co_op_enum"), nullable=False) + semester = Column(Enum('Fall', 'Spring', name="co_op_enum"), nullable=False) def __init__(self, uid, semester): self.uid = uid @@ -240,7 +209,7 @@ def __init__(self, uid, semester): class OnFloorStatusAssigned(db.Model): - __tablename__ = "onfloor_datetime" + __tablename__ = 'onfloor_datetime' uid = Column(String(32), primary_key=True) onfloor_granted = Column(DateTime, primary_key=True) @@ -250,22 +219,20 @@ def __init__(self, uid, time_granted): class Conditional(db.Model): - __tablename__ = "conditional" + __tablename__ = 'conditional' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) description = Column(String(512), nullable=False) date_created = Column(Date, nullable=False) date_due = Column(Date, nullable=False) active = Column(Boolean, nullable=False) - status = Column( - Enum("Pending", "Passed", "Failed", name="conditional_enum"), nullable=False - ) - s_evaluation = Column(ForeignKey("spring_evals.id")) - i_evaluation = Column(ForeignKey("freshman_eval_data.id")) - - def __init__( - self, uid, description, due, s_eval=None, i_eval=None - ): # pylint: disable=too-many-positional-arguments + status = Column(Enum('Pending', 'Passed', 'Failed', + name="conditional_enum"), + nullable=False) + s_evaluation = Column(ForeignKey('spring_evals.id')) + i_evaluation = Column(ForeignKey('freshman_eval_data.id')) + + def __init__(self, uid, description, due, s_eval=None, i_eval=None): # pylint: disable=too-many-positional-arguments self.uid = uid self.description = description self.date_due = due @@ -277,7 +244,7 @@ def __init__( class EvalSettings(db.Model): - __tablename__ = "settings" + __tablename__ = 'settings' id = Column(Integer, primary_key=True) housing_form_active = Column(Boolean) intro_form_active = Column(Boolean) @@ -292,14 +259,14 @@ def __init__(self): class SpringEval(db.Model): - __tablename__ = "spring_evals" + __tablename__ = 'spring_evals' id = Column(Integer, primary_key=True) uid = Column(String(32), nullable=False) active = Column(Boolean, nullable=False) date_created = Column(Date, nullable=False) - status = Column( - Enum("Pending", "Passed", "Failed", name="spring_eval_enum"), nullable=False - ) + status = Column(Enum('Pending', 'Passed', 'Failed', + name="spring_eval_enum"), + nullable=False) def __init__(self, uid): self.uid = uid @@ -309,26 +276,13 @@ def __init__(self, uid): class InHousingQueue(db.Model): - __tablename__ = "in_housing_queue" + __tablename__ = 'in_housing_queue' uid = Column(String(32), primary_key=True) - -http_enum = Enum( - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "CONNECT", - "OPTIONS", - "TRACE", - "PATCH", - name="http_enum", -) - +http_enum = Enum('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH', name='http_enum') class UserLog(db.Model): - __tablename__ = "user_log" + __tablename__ = 'user_log' id = Column(Integer, primary_key=True) ipaddr = Column(postgresql.INET, nullable=False) timestamp = Column(DateTime, nullable=False) @@ -338,9 +292,7 @@ class UserLog(db.Model): path = Column(String(128), nullable=False) description = Column(String(128), nullable=False) - def __init__( - self, ipaddr, user, method, blueprint, path, description - ): # pylint: disable=too-many-positional-arguments + def __init__(self, ipaddr, user, method, blueprint, path, description): # pylint: disable=too-many-positional-arguments self.ipaddr = ipaddr self.timestamp = datetime.now() self.uid = user @@ -348,213 +300,3 @@ def __init__( self.blueprint = blueprint self.path = path self.description = description - - -# The required number of honorary member, advisor, and alumni signatures -REQUIRED_MISC_SIGNATURES = 10 - - -class SigCounts: - """ - Utility class for returning counts of signatures broken out by type - """ - - def __init__(self, upper: int, fresh: int, misc: int): - # Base fields - self.upper = upper - self.fresh = fresh - self.misc = misc - - # Capped version of misc so it will never be greater than REQUIRED_MISC_SIGNATURES - self.misc_capped = ( - misc if misc <= REQUIRED_MISC_SIGNATURES else REQUIRED_MISC_SIGNATURES - ) - - # Totals (calculated using misc_capped) - self.member_total = upper + self.misc_capped - self.total = upper + fresh + self.misc_capped - - -class Freshman(db.Model): - __tablename__ = "freshman" - rit_username = cast(str, Column(String(10), primary_key=True)) - name = cast(str, Column(String(64), nullable=False)) - onfloor = cast(bool, Column(Boolean, nullable=False)) - fresh_signatures = cast("FreshSignature", relationship("FreshSignature")) - - # One freshman can have multiple packets if they repeat the intro process - packets = cast("Packet", relationship("Packet", order_by="desc(Packet.id)")) - - @classmethod - def by_username(cls, username: str) -> "Packet": - """ - Helper method to retrieve a freshman by their RIT username - """ - return cls.query.filter_by(rit_username=username).first() - - @classmethod - def get_all(cls) -> list["Packet"]: - """ - Helper method to get all freshmen easily - """ - return cls.query.all() - - -class Packet(db.Model): - __tablename__ = "packet" - id = cast(int, Column(Integer, primary_key=True, autoincrement=True)) - freshman_username = cast(str, Column(ForeignKey("freshman.rit_username"))) - start = cast(datetime, Column(DateTime, nullable=False)) - end = cast(datetime, Column(DateTime, nullable=False)) - - freshman = cast(Freshman, relationship("Freshman", back_populates="packets")) - - # The `lazy='subquery'` kwarg enables eager loading for signatures which makes signature calculations much faster - # See the docs here for details: https://docs.sqlalchemy.org/en/latest/orm/loading_relationships.html - upper_signatures = cast( - "UpperSignature", - relationship( - "UpperSignature", - lazy="subquery", - order_by="UpperSignature.signed.desc(), UpperSignature.updated", - ), - ) - fresh_signatures = cast( - "FreshSignature", - relationship( - "FreshSignature", - lazy="subquery", - order_by="FreshSignature.signed.desc(), FreshSignature.updated", - ), - ) - misc_signatures = cast( - "MiscSignature", - relationship( - "MiscSignature", lazy="subquery", order_by="MiscSignature.updated" - ), - ) - - def is_open(self) -> bool: - return self.start < datetime.now() < self.end - - def signatures_required(self) -> SigCounts: - """ - :return: A SigCounts instance with the fields set to the number of signatures received by this packet - """ - upper = len(self.upper_signatures) - fresh = len(self.fresh_signatures) - - return SigCounts(upper, fresh, REQUIRED_MISC_SIGNATURES) - - def signatures_received(self) -> SigCounts: - """ - :return: A SigCounts instance with the fields set to the number of required signatures for this packet - """ - upper = sum(map(lambda sig: 1 if sig.signed else 0, self.upper_signatures)) - fresh = sum(map(lambda sig: 1 if sig.signed else 0, self.fresh_signatures)) - - return SigCounts(upper, fresh, len(self.misc_signatures)) - - def did_sign(self, username: str, is_csh: bool) -> bool: - """ - :param username: The CSH or RIT username to check for - :param is_csh: Set to True for CSH accounts and False for freshmen - :return: Boolean value for if the given account signed this packet - """ - if is_csh: - for sig in filter( - lambda sig: sig.member == username, - chain(self.upper_signatures, self.misc_signatures), - ): - if isinstance(sig, MiscSignature): - return True - return sig.signed - else: - for sig in filter( - lambda sig: sig.freshman_username == username, self.fresh_signatures - ): - return sig.signed - - # The user must be a misc CSHer that hasn't signed this packet or an off-floor freshmen - return False - - def is_100(self) -> bool: - """ - Checks if this packet has reached 100% - """ - return self.signatures_required().total == self.signatures_received().total - - @classmethod - def open_packets(cls) -> list["Packet"]: - """ - Helper method for fetching all currently open packets - """ - return cls.query.filter( - cls.start < datetime.now(), cls.end > datetime.now() - ).all() - - @classmethod - def by_id(cls, packet_id: int) -> "Packet": - """ - Helper method for fetching 1 packet by its id - """ - return cls.query.filter_by(id=packet_id).first() - - -class UpperSignature(db.Model): - __tablename__ = "signature_upper" - packet_id = cast(int, Column(Integer, ForeignKey("packet.id"), primary_key=True)) - member = cast(str, Column(String(36), primary_key=True)) - signed = cast(bool, Column(Boolean, default=False, nullable=False)) - eboard = cast(Optional[str], Column(String(12), nullable=True)) - active_rtp = cast(bool, Column(Boolean, default=False, nullable=False)) - three_da = cast(bool, Column(Boolean, default=False, nullable=False)) - webmaster = cast(bool, Column(Boolean, default=False, nullable=False)) - c_m = cast(bool, Column(Boolean, default=False, nullable=False)) - w_m = cast(bool, Column(Boolean, default=False, nullable=False)) - drink_admin = cast(bool, Column(Boolean, default=False, nullable=False)) - updated = cast( - datetime, - Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False), - ) - - packet = cast(Packet, relationship("Packet", back_populates="upper_signatures")) - - -class FreshSignature(db.Model): - __tablename__ = "signature_fresh" - packet_id = cast(int, Column(Integer, ForeignKey("packet.id"), primary_key=True)) - freshman_username = cast( - str, Column(ForeignKey("freshman.rit_username"), primary_key=True) - ) - signed = cast(bool, Column(Boolean, default=False, nullable=False)) - updated = cast( - datetime, - Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False), - ) - - packet = cast(Packet, relationship("Packet", back_populates="fresh_signatures")) - freshman = cast( - Freshman, relationship("Freshman", back_populates="fresh_signatures") - ) - - -class MiscSignature(db.Model): - __tablename__ = "signature_misc" - packet_id = cast(int, Column(Integer, ForeignKey("packet.id"), primary_key=True)) - member = cast(str, Column(String(36), primary_key=True)) - updated = cast( - datetime, - Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False), - ) - - packet = cast(Packet, relationship("Packet", back_populates="misc_signatures")) - - -class NotificationSubscription(db.Model): - __tablename__ = "notification_subscriptions" - member = cast(str, Column(String(36), nullable=True)) - freshman_username = cast( - str, Column(ForeignKey("freshman.rit_username"), nullable=True) - ) - token = cast(str, Column(String(256), primary_key=True, nullable=False)) diff --git a/conditional/templates/active_packets.html b/conditional/templates/active_packets.html deleted file mode 100644 index 3fa6564..0000000 --- a/conditional/templates/active_packets.html +++ /dev/null @@ -1,117 +0,0 @@ -{% extends "extend/base.html" %} - -{% block body %} -
-
-
-
-

Active Packets

-
- {% if info.is_upper %} -
- -
- {% endif %} -
-
-
- {% if packets|length > 0 %} -
-
-
-
- - - - - {% if info.is_upper %} - - - - {% endif %} - - - - - {% for packet in packets %} - - - {% if info.is_upper %} - - - - {% endif %} - - - {% endfor %} - -
NameSignaturesSignaturesSignaturesSign
- {% if info.is_upper %} - - {% endif %} - {{ get_rit_name(packet.freshman_username) }} {{ get_rit_name(packet.freshman_username) }} - {% if info.is_upper %} - - {% endif %} - - {% if packet.signatures_received_result.member_total == packet.signatures_required_result.member_total %} - 💯 {# 100% emoji #} - {% else %} - {{ packet.signatures_received_result.member_total }} / - {{ packet.signatures_required_result.member_total }} - {% endif %} - - {% if packet.signatures_received_result.fresh == packet.signatures_required_result.fresh %} - 💯 {# 100% emoji #} - {% else %} - {{ packet.signatures_received_result.fresh }} / - {{ packet.signatures_required_result.fresh }} - {% endif %} - - {% if packet.signatures_received_result.total == packet.signatures_required_result.total %} - 💯 {# 100% emoji #} - {% else %} - {{ packet.signatures_received_result.total }} / - {{ packet.signatures_required_result.total }} - {% endif %} - - {% if not packet.did_sign_result and info.ritdn != packet.freshman_username %} - - {% elif info.ritdn != packet.freshman_username %} - - {% endif %} -
-
-
-
-
- {% else %} - - {% endif %} -
-
-{% endblock %} - -{% block scripts %} - {{ super() }} - {% if info.realm == "csh" %} - - {% endif %} -{% endblock %} diff --git a/conditional/templates/admin_freshmen.html b/conditional/templates/admin_freshmen.html deleted file mode 100644 index e922375..0000000 --- a/conditional/templates/admin_freshmen.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "extend/base.html" %} - -{% block body %} -
-
-
-
-

All Freshmen

-
-
- - {% include 'include/admin/sync_freshmen.html' %} -
-
-
-
- {% include 'include/admin/all_freshmen.html' %} -
-
-{% endblock %} - -{% block scripts %} - {{ super() }} - -{% endblock %} diff --git a/conditional/templates/admin_packets.html b/conditional/templates/admin_packets.html deleted file mode 100644 index 9db36fd..0000000 --- a/conditional/templates/admin_packets.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "extend/base.html" %} - -{% block body %} -
-
-
-
-

Active Packets

-
-
- - - {% include 'include/admin/new_packets.html' %} -
-
-
-
- {% include 'include/admin/open_packets.html' %} -
-
-{% endblock %} - -{% block scripts %} - {{ super() }} - -{% endblock %} diff --git a/conditional/templates/error.html b/conditional/templates/error.html deleted file mode 100644 index de33536..0000000 --- a/conditional/templates/error.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'extend/base.html' %} - -{% block body %} -
-

Oops!

-
-
-
- I guess this is what you get when you trust a bunch of college kids. -
-

-

{{ e }}
-

-
- Do us a favor, try again. If you end up here on the second try, shoot us an email. -
-
-
-
-{% endblock %} diff --git a/conditional/templates/extend/base.html b/conditional/templates/extend/base.html deleted file mode 100644 index 418e93d..0000000 --- a/conditional/templates/extend/base.html +++ /dev/null @@ -1,29 +0,0 @@ - - - -{% block head %} - {% include "include/head.html" %} -{% endblock %} - - - -{% block nav %} - {% include "include/nav.html" %} -{% endblock %} - -{% block body %} -{% endblock %} - -{% block footer %} - {% include "include/footer.html" %} -{% endblock %} - -{% block includes %} -{% endblock %} - -{% block scripts %} - {% include "include/scripts.html" %} -{% endblock %} - - - diff --git a/conditional/templates/extend/email.html b/conditional/templates/extend/email.html deleted file mode 100644 index 20fb7de..0000000 --- a/conditional/templates/extend/email.html +++ /dev/null @@ -1,17 +0,0 @@ - - - -{% block head %} - - CSH Packet - - -{% endblock %} - - -{% block body %} -{% endblock %} - - diff --git a/conditional/templates/include/admin/all_freshmen.html b/conditional/templates/include/admin/all_freshmen.html deleted file mode 100644 index a3e79e3..0000000 --- a/conditional/templates/include/admin/all_freshmen.html +++ /dev/null @@ -1,33 +0,0 @@ -
-
-
-
- - - - - - - - - {% for freshman in all_freshmen %} - {% set freshman_name = freshman.name + ' (' + freshman.rit_username + ')' %} - - - - - {% endfor %} - -
NameOn-Floor
- {{ freshman_name }} {{ freshman_name }} - - {{ freshman.onfloor }} -
-
-
-
-
diff --git a/conditional/templates/include/admin/new_packets.html b/conditional/templates/include/admin/new_packets.html deleted file mode 100644 index c6dd307..0000000 --- a/conditional/templates/include/admin/new_packets.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/conditional/templates/include/admin/open_packets.html b/conditional/templates/include/admin/open_packets.html deleted file mode 100644 index 2db5d4b..0000000 --- a/conditional/templates/include/admin/open_packets.html +++ /dev/null @@ -1,39 +0,0 @@ -
-
-
-
- - - - - - - - - {% for packet in open_packets %} - - - - - {% endfor %} - -
NameSignatures
- - {{ get_rit_name(packet.freshman_username) }} {{ get_rit_name(packet.freshman_username) }} - - - {% if packet.signatures_received_result.total == packet.signatures_required_result.total %} - 💯 {# 100% emoji #} - {% else %} - {{ packet.signatures_received_result.total }} / - {{ packet.signatures_required_result.total }} - {% endif %} -
-
-
-
-
diff --git a/conditional/templates/include/admin/sync_freshmen.html b/conditional/templates/include/admin/sync_freshmen.html deleted file mode 100644 index 6f9b480..0000000 --- a/conditional/templates/include/admin/sync_freshmen.html +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/conditional/templates/include/footer.html b/conditional/templates/include/footer.html deleted file mode 100644 index b1e9bdc..0000000 --- a/conditional/templates/include/footer.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/conditional/templates/include/head.html b/conditional/templates/include/head.html deleted file mode 100644 index 20d0f42..0000000 --- a/conditional/templates/include/head.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - CSH Packet - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/conditional/templates/include/nav.html b/conditional/templates/include/nav.html deleted file mode 100644 index 6c03562..0000000 --- a/conditional/templates/include/nav.html +++ /dev/null @@ -1,67 +0,0 @@ - diff --git a/conditional/templates/include/scripts.html b/conditional/templates/include/scripts.html deleted file mode 100644 index af0b763..0000000 --- a/conditional/templates/include/scripts.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - -{% if info.realm == "intro" %} - -{% endif %} diff --git a/conditional/templates/mail/packet_start.html b/conditional/templates/mail/packet_start.html deleted file mode 100644 index 722e576..0000000 --- a/conditional/templates/mail/packet_start.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "extend/email.html" %} - -{% block body %} -
-

Hello {{ packet.freshman.name }},

-

Welcome to Computer Science House!

-

Soon you'll starting the introductory process for CSH, and the first part of that is Packet.

-

Your packet will start on {{ packet.start.strftime('%A, %B %-d') }} at {{ packet.start.strftime('%-I:%M %p') }}

-

You can view your packet at {{ config["PACKET_INTRO"] }} with - the credentials you should have been sent.

-

If you don't know your credentials, reach out to an RTP

-

If you have any questions about Packet or the introductory process, email evals@csh.rit.edu

-

If you have any questions about login credentials or any technical issues, email rtp@csh.rit.edu

-
-{% endblock %} diff --git a/conditional/templates/mail/packet_start.txt b/conditional/templates/mail/packet_start.txt deleted file mode 100644 index 162ec14..0000000 --- a/conditional/templates/mail/packet_start.txt +++ /dev/null @@ -1,14 +0,0 @@ -Hello {{ packet.freshman.name }}, - -Welcome to Computer Science House! - -Soon you'll starting the introductory process for CSH, and the first part of that is Packet. - -Your packet will start on {{ packet.start.strftime('%A, %B %-d') }} at {{ packet.start.strftime('%-I:%M %p') }} - -You can view your packet at {{ config["PROTOCOL"] + config["PACKET_INTRO"] }} with the credentials you should have been sent. -If you don't know your credentials, reach out to an RTP - -If you have any questions about Packet or the introductory process, email evals@csh.rit.edu - -If you have any questions about login credentials or any technical issues, email rtp@csh.rit.edu diff --git a/conditional/templates/mail/report.html b/conditional/templates/mail/report.html deleted file mode 100644 index dbfedcb..0000000 --- a/conditional/templates/mail/report.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "extend/email.html" %} - -{% block body %} -
-

Hello,

-

{{ reporter }} just made a report against {{ person }}

-

The report reads:

-
{{ report }}
-
-{% endblock %} diff --git a/conditional/templates/mail/report.txt b/conditional/templates/mail/report.txt deleted file mode 100644 index 7a5576c..0000000 --- a/conditional/templates/mail/report.txt +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -{{ reporter }} just made a report against {{ person }} - -The report reads: - -{{ report }} diff --git a/conditional/templates/nav.html b/conditional/templates/nav.html index ffb3ec9..6e4173f 100644 --- a/conditional/templates/nav.html +++ b/conditional/templates/nav.html @@ -54,8 +54,6 @@ -
  • Packet
  • - {% if is_eboard or is_rtp%}