From cff4adcaf4c23e81d2265f26e475dd6a41dd01fe Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 25 Aug 2026 15:55:02 -0400 Subject: [PATCH 1/6] FIX: docker build + running locally --- .dockerignore | 2 + Dockerfile | 4 +- Dockerfile.dev | 34 ---------- README.md | 2 +- config.env.py | 2 +- docker-compose.yml | 12 ++-- packet/__init__.py | 3 +- requirements.in | 35 +++++----- requirements.txt | 166 +++++++++++++++++++++------------------------ 9 files changed, 110 insertions(+), 150 deletions(-) delete mode 100644 Dockerfile.dev diff --git a/.dockerignore b/.dockerignore index c2658d7..6b325ce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,3 @@ node_modules/ +.env +.env.example diff --git a/Dockerfile b/Dockerfile index 78d9e79..7994cd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/python:3.9-slim-trixie +FROM docker.io/python:3.14-slim-trixie RUN ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime RUN apt-get -yq update && \ @@ -32,4 +32,4 @@ RUN gulp production && \ # Set version for apm RUN echo "export DD_VERSION=\"$(python3 packet/git.py)\"" >> /tmp/version -CMD ["/bin/bash", "-c", "source /tmp/version && ddtrace-run gunicorn packet:app --bind=0.0.0.0:8080 --access-logfile=- --timeout=600"] +CMD ["/bin/bash", "-c", "source /tmp/version && gunicorn packet:app --bind=0.0.0.0:8080 --access-logfile=- --timeout=600"] diff --git a/Dockerfile.dev b/Dockerfile.dev deleted file mode 100644 index 473c469..0000000 --- a/Dockerfile.dev +++ /dev/null @@ -1,34 +0,0 @@ -FROM docker.io/python:3.9-slim-trixie - -RUN ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime -RUN apt-get -yq update && \ - apt-get -yq --no-install-recommends install gcc curl libsasl2-dev libldap2-dev libssl-dev gnupg2 git && \ - apt-get -yq clean all \ - curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor -o /usr/share/keyrings/yarn-archive-keyring.gpg && \ - echo "deb [signed-by=/usr/share/keyrings/yarn-archive-keyring.gpg] https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ - apt-get -yq update && \ - apt-get -yq --no-install-recommends install nodejs yarn - -RUN mkdir /opt/packet -WORKDIR /opt/packet - -COPY requirements.txt /opt/packet/ -RUN pip install -r requirements.txt - -COPY package.json /opt/packet/ -COPY yarn.lock /opt/packet/ - -RUN yarn install && \ - yarn global add gulp - -COPY . /opt/packet -RUN gulp production && \ - rm -rf node_modules && \ - apt-get -yq remove nodejs npm yarn && \ - apt-get -yq autoremove && \ - apt-get -yq clean all - -EXPOSE 8000 - -CMD ["/bin/bash", "-c", "python3 wsgi.py"] diff --git a/README.md b/README.md index 197f50c..04cc310 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ the second major iteration of packet on the web. The first version was 2. Build the dev environment with `docker compose build` 3. Start the dev environment with `docker compose up` 4. Run the database migrations with `docker exec -it packet-packet-1 flask db upgrade` -5. Go check out the app at http://localhost.localdomain:8000 +5. Go check out the app at http://localhost:8080 > Useful Tip: use `docker compose up --watch` so that you don't need to rebuild the container every time you want to test a change diff --git a/config.env.py b/config.env.py index 7e7b883..a36b06b 100644 --- a/config.env.py +++ b/config.env.py @@ -24,7 +24,7 @@ OIDC_CLIENT_SECRET = environ.get("PACKET_OIDC_CLIENT_SECRET", "PLEASE_REPLACE_ME") # SQLAlchemy config -SQLALCHEMY_DATABASE_URI = environ.get("PACKET_DATABASE_URI", "postgresql://postgres:mysecretpassword@localhost:5432/postgres") +SQLALCHEMY_DATABASE_URI = environ.get("PACKET_DATABASE_URI", "postgresql://postgres:mysecretpassword@postgres:5432/postgres") SQLALCHEMY_TRACK_MODIFICATIONS = False # LDAP config diff --git a/docker-compose.yml b/docker-compose.yml index dc452c4..4448bd0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,12 +2,11 @@ services: packet: build: context: . - dockerfile: ./Dockerfile.dev env_file: - path: ".env" required: true ports: - - "8000:8000" + - "8080:8080" develop: watch: - action: sync+restart @@ -23,14 +22,15 @@ services: postgres: condition: service_healthy restart: true + postgres: - image: "docker.io/postgres:17" + image: docker.io/postgres networks: - packet-network-dev environment: - POSTGRES_USER: "postgres" - POSTGRES_DB: "postgres" - POSTGRES_PASSWORD: "mysecretpassword" + POSTGRES_USER: postgres + POSTGRES_DATABASE: postgres + POSTGRES_PASSWORD: mysecretpassword ports: - "5432:5432" healthcheck: diff --git a/packet/__init__.py b/packet/__init__.py index 85102f5..8870ae1 100644 --- a/packet/__init__.py +++ b/packet/__init__.py @@ -44,7 +44,8 @@ # Initialize the extensions db = SQLAlchemy(app) migrate = Migrate(app, db) -app.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) +with app.app_context(): + app.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) APP_CONFIG = ProviderConfiguration(issuer=app.config['OIDC_ISSUER'], client_metadata=ClientMetadata(app.config['OIDC_CLIENT_ID'], diff --git a/requirements.in b/requirements.in index 910537c..715d606 100644 --- a/requirements.in +++ b/requirements.in @@ -1,21 +1,20 @@ -Flask-Gzip==0.2 -Flask-Mail==0.10.0 -Flask-Migrate~=2.7.0 -Flask-pyoidc~=3.7.0 -Flask~=1.1.4 -csh-ldap @ git+https://github.com/costowell/csh_ldap@67dd183744746c758d6c13878f539437d2628b63 -ddtrace==3.12.2 -flask_sqlalchemy~=2.5.1 -gunicorn~=20.0.4 -mypy==1.17.1 +Flask-Gzip +Flask-Mail +Flask-Migrate +Flask-pyoidc +Flask +csh-ldap +flask_sqlalchemy +gunicorn +mypy onesignal-sdk~=1.0.0 -psycopg2-binary~=2.9.3 -pylint-quotes==0.2.3 -pylint~=2.8.0 -sentry-sdk~=1.5.12 -sqlalchemy[mypy]~=1.4.31 - +psycopg2-binary +pylint-quotes +pylint +sentry-sdk +sqlalchemy[mypy] +setuptools # Broken transitive dependency we should version -MarkupSafe~=2.0.1 +MarkupSafe # Tool to generate requirements.txt -pip-tools~=6.6.2 +pip-tools diff --git a/requirements.txt b/requirements.txt index 986ffab..bcab3da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,44 +1,42 @@ -# -# This file is autogenerated by pip-compile with Python 3.13 -# by the following command: -# -# pip-compile requirements.in -# -alembic==1.16.4 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.in +alembic==1.19.1 # via flask-migrate -annotated-types==0.7.0 +annotated-types==0.8.0 # via pydantic -astroid==2.5.6 +ast-serialize==0.8.0 + # via mypy +astroid==4.0.4 # via pylint blinker==1.9.0 - # via flask-mail -bytecode==0.16.2 - # via ddtrace -certifi==2025.8.3 + # via + # flask + # flask-mail +build==1.5.0 + # via pip-tools +certifi==2026.7.22 # via # requests # sentry-sdk -cffi==1.17.1 +cffi==2.1.1 # via cryptography -charset-normalizer==3.4.3 +charset-normalizer==3.5.1 # via requests -click==7.1.2 +click==8.4.2 # via # flask # pip-tools -cryptography==45.0.6 +cryptography==50.0.0 # via oic -csh-ldap @ git+https://github.com/costowell/csh_ldap@67dd183744746c758d6c13878f539437d2628b63 - # via -r requirements.in -ddtrace==3.12.2 +csh-ldap==2.5.8 # via -r requirements.in defusedxml==0.7.1 # via oic -dnspython==2.7.0 +dill==0.4.1 + # via pylint +dnspython==2.8.0 # via srvlookup -envier==0.6.1 - # via ddtrace -flask==1.1.4 +flask==3.1.3 # via # -r requirements.in # flask-gzip @@ -50,149 +48,143 @@ flask-gzip==0.2 # via -r requirements.in flask-mail==0.10.0 # via -r requirements.in -flask-migrate==2.7.0 +flask-migrate==4.1.0 # via -r requirements.in -flask-pyoidc==3.7.0 +flask-pyoidc==3.14.3 # via -r requirements.in -flask-sqlalchemy==2.5.1 +flask-sqlalchemy==3.1.1 # via # -r requirements.in # flask-migrate future==1.0.0 # via pyjwkest -greenlet==3.2.4 +greenlet==3.5.5 # via sqlalchemy -gunicorn==20.0.4 +gunicorn==26.2.0 # via -r requirements.in -idna==3.10 +idna==3.19 # via requests -importlib-metadata==8.7.0 - # via opentelemetry-api -importlib-resources==6.5.2 +importlib-resources==7.1.0 # via flask-pyoidc -isort==5.13.2 +isort==8.0.1 # via pylint -itsdangerous==1.1.0 +itsdangerous==2.2.0 # via flask -jinja2==2.11.3 +jinja2==3.1.6 # via flask -lazy-object-proxy==1.12.0 - # via astroid -legacy-cgi==2.6.3 - # via ddtrace -mako==1.3.10 +librt==0.15.0 + # via mypy +mako==1.4.1 # via # alembic # oic -markupsafe==2.0.1 +markupsafe==3.0.3 # via # -r requirements.in + # flask # jinja2 # mako -mccabe==0.6.1 + # werkzeug +mccabe==0.7.0 # via pylint -mypy==1.17.1 +mypy==2.3.1 # via # -r requirements.in # sqlalchemy mypy-extensions==1.1.0 # via mypy -oic==1.7.0 +oic==1.6.1 # via flask-pyoidc onesignal-sdk==1.0.0 # via -r requirements.in -opentelemetry-api==1.36.0 - # via ddtrace -pathspec==0.12.1 +packaging==26.3 + # via + # build + # wheel +pathspec==1.1.1 # via mypy -pep517==0.13.1 +pip==26.2.1 # via pip-tools -pip-tools==6.6.2 +pip-tools==7.6.1 # via -r requirements.in -protobuf==6.32.0 - # via ddtrace -psycopg2-binary==2.9.10 +platformdirs==4.11.4 + # via pylint +psycopg2-binary==2.9.12 # via -r requirements.in -pyasn1==0.6.1 +pyasn1==0.6.4 # via # pyasn1-modules # python-ldap pyasn1-modules==0.4.2 # via python-ldap -pycparser==2.22 +pycparser==3.0 # via cffi pycryptodomex==3.23.0 # via # oic # pyjwkest -pydantic==2.11.7 +pydantic==2.13.4 # via pydantic-settings -pydantic-core==2.33.2 +pydantic-core==2.46.4 # via pydantic -pydantic-settings==2.10.1 +pydantic-settings==2.15.0 # via oic -pyjwkest==1.4.2 +pyjwkest==1.4.4 # via oic -pylint==2.8.3 +pylint==4.0.7 # via # -r requirements.in # pylint-quotes pylint-quotes==0.2.3 # via -r requirements.in -python-dotenv==1.1.1 +pyproject-hooks==1.2.0 + # via + # build + # pip-tools +python-dotenv==1.2.3 # via pydantic-settings -python-ldap==3.4.4 +python-ldap==3.4.5 # via csh-ldap -requests==2.32.5 +requests==2.34.2 # via # flask-pyoidc # oic # onesignal-sdk # pyjwkest -sentry-sdk==1.5.12 +sentry-sdk==2.68.1 # via -r requirements.in +setuptools==84.0.0 + # via + # -r requirements.in + # pip-tools six==1.17.0 # via pyjwkest -sqlalchemy[mypy]==1.4.54 +sqlalchemy==2.0.52 # via # -r requirements.in # alembic # flask-sqlalchemy -sqlalchemy2-stubs==0.0.2a38 - # via sqlalchemy -srvlookup==2.0.0 +srvlookup==3.0.0 # via csh-ldap -toml==0.10.2 +tomlkit==0.15.1 # via pylint -typing-extensions==4.14.1 +typing-extensions==4.16.0 # via # alembic - # ddtrace # mypy - # opentelemetry-api # pydantic # pydantic-core - # sqlalchemy2-stubs + # sqlalchemy # typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.4 # via # pydantic # pydantic-settings -urllib3==2.5.0 +urllib3==2.7.0 # via # requests # sentry-sdk -werkzeug==1.0.1 +werkzeug==3.1.8 # via flask -wheel==0.45.1 +wheel==0.48.0 # via pip-tools -wrapt==1.12.1 - # via - # astroid - # ddtrace -zipp==3.23.0 - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -# pip -# setuptools From 9c83646610aa03eb819b77217e43f196193a5307 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 25 Aug 2026 16:20:29 -0400 Subject: [PATCH 2/6] FIX: update example env --- .env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index a479ace..131ab2d 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ PACKET_IP=0.0.0.0 -PACKET_SERVER_NAME=localhost.localdomain:8000 +PACKET_SERVER_NAME=localhost:8000 PACKET_DATABASE_URI=postgresql://postgres:mysecretpassword@postgres:5432/postgres +PACKET_OIDC_CLIENT_ID=develop PACKET_OIDC_CLIENT_SECRET= PACKET_LDAP_BIND_PASS= PACKET_LDAP_BIND_DN= From cca0a73877bbc469a1a179c7a987cd3f370cb154 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 25 Aug 2026 17:30:35 -0400 Subject: [PATCH 3/6] fix: lint worokflow --- .github/workflows/python-app.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 29dc65f..4e94a81 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: [3.14] steps: - name: Install ldap dependencies @@ -38,7 +38,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: [3.14] steps: - name: Install ldap dependencies From 1b6e18f8a16ce368fb9f4f6cb13bf7157fb209ce Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 1 Sep 2026 22:37:20 -0400 Subject: [PATCH 4/6] fix: lint runs --- .pylintrc | 35 ++++++++++++----------------------- requirements.in | 1 - requirements.txt | 24 +++++++++++------------- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/.pylintrc b/.pylintrc index bd778b5..bd2d0b9 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,39 +1,31 @@ [MASTER] ignore = ,input persistent = yes -load-plugins = pylint_quotes [MESSAGES CONTROL] disable = missing-docstring, - fixme, - duplicate-code, - no-member, - parse-error, - bad-continuation, - too-few-public-methods, - global-statement, - cyclic-import, - locally-disabled, - file-ignored, - no-else-return, - unnecessary-lambda + fixme, + duplicate-code, + no-member, + parse-error, + too-few-public-methods, + global-statement, + cyclic-import, + locally-disabled, + file-ignored, + no-else-return, + unnecessary-lambda [REPORTS] output-format = text -files-output = no reports = no [FORMAT] max-line-length = 120 -max-statement-lines = 75 single-line-if-stmt = no -no-space-check = trailing-comma,dict-separator max-module-lines = 1000 indent-string = ' ' -string-quote=single-avoid-escape -triple-quote=single -docstring-quote=double [MISCELLANEOUS] notes = FIXME,XXX,TODO @@ -79,9 +71,6 @@ good-names=logger,id,ID # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata -# List of builtins function names that should not be used, separated by a comma -bad-functions=apply,input - [DESIGN] max-args = 10 ignored-argument-names = _.* @@ -95,4 +84,4 @@ min-public-methods = 2 max-public-methods = 20 [EXCEPTIONS] -overgeneral-exceptions = Exception +overgeneral-exceptions = builtins.Exception diff --git a/requirements.in b/requirements.in index 715d606..e8602a5 100644 --- a/requirements.in +++ b/requirements.in @@ -9,7 +9,6 @@ gunicorn mypy onesignal-sdk~=1.0.0 psycopg2-binary -pylint-quotes pylint sentry-sdk sqlalchemy[mypy] diff --git a/requirements.txt b/requirements.txt index bcab3da..818db6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ blinker==1.9.0 # via # flask # flask-mail -build==1.5.0 +build==1.6.0 # via pip-tools certifi==2026.7.22 # via @@ -22,11 +22,11 @@ cffi==2.1.1 # via cryptography charset-normalizer==3.5.1 # via requests -click==8.4.2 +click==8.5.0 # via # flask # pip-tools -cryptography==50.0.0 +cryptography==50.0.1 # via oic csh-ldap==2.5.8 # via -r requirements.in @@ -66,7 +66,7 @@ idna==3.19 # via requests importlib-resources==7.1.0 # via flask-pyoidc -isort==8.0.1 +isort==9.0.1 # via pylint itsdangerous==2.2.0 # via flask @@ -92,7 +92,9 @@ mypy==2.3.1 # -r requirements.in # sqlalchemy mypy-extensions==1.1.0 - # via mypy + # via + # isort + # mypy oic==1.6.1 # via flask-pyoidc onesignal-sdk==1.0.0 @@ -107,7 +109,7 @@ pip==26.2.1 # via pip-tools pip-tools==7.6.1 # via -r requirements.in -platformdirs==4.11.4 +platformdirs==4.11.7 # via pylint psycopg2-binary==2.9.12 # via -r requirements.in @@ -123,19 +125,15 @@ pycryptodomex==3.23.0 # via # oic # pyjwkest -pydantic==2.13.4 +pydantic==2.13.5 # via pydantic-settings -pydantic-core==2.46.4 +pydantic-core==2.46.5 # via pydantic pydantic-settings==2.15.0 # via oic pyjwkest==1.4.4 # via oic -pylint==4.0.7 - # via - # -r requirements.in - # pylint-quotes -pylint-quotes==0.2.3 +pylint==4.0.8 # via -r requirements.in pyproject-hooks==1.2.0 # via From 5b9624f9f604769a98715b6352de7b3d0f559781 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Tue, 1 Sep 2026 23:06:34 -0400 Subject: [PATCH 5/6] fix: lint passses --- packet/__init__.py | 6 +++--- packet/commands.py | 38 +++++++++++++++++------------------- packet/context_processors.py | 11 +++++++---- packet/git.py | 2 +- packet/ldap.py | 4 ++-- packet/log_utils.py | 7 +++---- packet/mail.py | 4 ++-- packet/notifications.py | 4 ++-- packet/stats.py | 8 ++++---- packet/utils.py | 10 +++++----- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/packet/__init__.py b/packet/__init__.py index 8870ae1..3c6273d 100644 --- a/packet/__init__.py +++ b/packet/__init__.py @@ -38,14 +38,14 @@ # Logger configuration logging.getLogger().setLevel(app.config['LOG_LEVEL']) -app.logger.info('Launching packet ' + app.config['VERSION']) -app.logger.info('Using the {} realm'.format(app.config['REALM'])) +app.logger.info('Launching packet %s', app.config['VERSION']) +app.logger.info('Using the %s realm', app.config['REALM']) # Initialize the extensions db = SQLAlchemy(app) migrate = Migrate(app, db) with app.app_context(): - app.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) + app.logger.info('SQLAlchemy pointed at %s', repr(db.engine.url)) APP_CONFIG = ProviderConfiguration(issuer=app.config['OIDC_ISSUER'], client_metadata=ClientMetadata(app.config['OIDC_CLIENT_ID'], diff --git a/packet/commands.py b/packet/commands.py index 179e973..3f3881a 100644 --- a/packet/commands.py +++ b/packet/commands.py @@ -37,7 +37,7 @@ def __init__(self, row: list[str]) -> None: def parse_csv(freshmen_csv: str) -> dict[str, CSVFreshman]: print('Parsing file...') try: - with open(freshmen_csv, newline='') as freshmen_csv_file: + with open(freshmen_csv, newline='', encoding="locale") as freshmen_csv_file: return {freshman.rit_username: freshman for freshman in map(CSVFreshman, csv.reader(freshmen_csv_file))} except Exception as e: print('Failure while parsing CSV') @@ -108,34 +108,32 @@ def fetch_results(file_path: str, use_csv: bool, date_str: str) -> None: end_date = datetime.combine(input_date("Enter the last day of the packet season you'd like to retrieve results " 'from'), packet_end_time) - - file_handle = open(file_path, 'w', newline='') if file_path else sys.stdout - column_titles = ['Name (RIT Username)', 'Upperclassmen Score', 'Total Score', 'Upperclassmen', 'Freshmen', 'Miscellaneous', 'Total Missed'] - data = list() + data = [] for packet in Packet.query.filter_by(end=end_date).all(): received = packet.signatures_received() required = packet.signatures_required() row = [ - '{} ({}):'.format(packet.freshman.name, packet.freshman.rit_username), - '{:0.2f}%'.format(received.member_total / required.member_total * 100), - '{:0.2f}%'.format(received.total / required.total * 100), - '{}/{}'.format(received.upper, required.upper), - '{}/{}'.format(received.fresh, required.fresh), - '{}/{}'.format(received.misc, required.misc), - required.total - received.total, + f'{packet.freshman.name} ({packet.freshman.rit_username}):', + f'{received.member_total / required.member_total * 100:0.2f}%', + f'{received.total / required.total * 100:0.2f}%', + f'{received.upper}/{required.upper}', + f'{received.fresh}/{required.fresh}', + f'{received.misc}/{required.misc}', + required.total - received.total, ] data.append(row) - if use_csv: - writer = csv.writer(file_handle) - writer.writerow(column_titles) - writer.writerows(data) - else: - for row in data: - file_handle.write(f''' + with open(file_path, 'w', newline='', encoding='locale') if file_path else sys.stdout as file_handle: + if use_csv: + writer = csv.writer(file_handle) + writer.writerow(column_titles) + writer.writerows(data) + else: + for row in data: + file_handle.write(f''' {row[0]} \t{column_titles[1]}: {row[1]} @@ -160,7 +158,7 @@ def extend_packet(packet_id: int) -> None: print('Packet is already closed so it cannot be extended') return else: - print('Ready to extend packet #{} for {}'.format(packet_id, packet.freshman_username)) + print(f'Ready to extend packet #{packet_id} for {packet.freshman_username}') packet.end = datetime.combine(input_date('Enter the new end date for this packet'), packet_end_time) db.session.commit() diff --git a/packet/context_processors.py b/packet/context_processors.py index c13adf6..25642f0 100644 --- a/packet/context_processors.py +++ b/packet/context_processors.py @@ -79,7 +79,10 @@ def log_time(label: str) -> None: @app.context_processor def utility_processor() -> dict[str, Callable]: - return dict( - get_csh_name=get_csh_name, get_rit_name=get_rit_name, get_rit_image=get_rit_image, log_time=log_time, - get_roles=get_roles - ) + return { + "get_csh_name": get_csh_name, + "get_rit_name": get_rit_name, + "get_rit_image": get_rit_image, + "log_time": log_time, + "get_roles": get_roles + } diff --git a/packet/git.py b/packet/git.py index 506276d..500f514 100644 --- a/packet/git.py +++ b/packet/git.py @@ -42,7 +42,7 @@ def get_version(commit_ish: str = 'HEAD') -> str: return sha else: root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - with open(os.path.join(root_dir, 'package.json')) as package_file: + with open(os.path.join(root_dir, 'package.json'), encoding='locale') as package_file: return json.load(package_file)['version'] if __name__ == '__main__': diff --git a/packet/ldap.py b/packet/ldap.py index 005ac43..8a2bb00 100644 --- a/packet/ldap.py +++ b/packet/ldap.py @@ -19,7 +19,7 @@ def __init__(self, cn: Optional[str] = None, room_number: Optional[int] = None): self.uid = uid - self.groups = groups if groups else list() + self.groups = groups if groups else [] if room_number: self.room_number = room_number self.cn = cn if cn else uid.title() # pylint: disable=invalid-name @@ -263,7 +263,7 @@ def is_on_coop(self, member: CSHMember) -> bool: return self._is_member_of_group(member, 'spring_coop') - def get_roomnumber(self, member: CSHMember) -> Optional[int]: # pylint: disable=no-self-use + def get_roomnumber(self, member: CSHMember) -> Optional[int]: """ :param member: A CSHMember instance """ diff --git a/packet/log_utils.py b/packet/log_utils.py index 2d69f16..96964eb 100644 --- a/packet/log_utils.py +++ b/packet/log_utils.py @@ -23,7 +23,7 @@ def wrapped_function(*args: list, **kwargs: dict) -> Any: result = func(*args, **kwargs) seconds = (datetime.now() - start).total_seconds() - app.logger.info('{}.{}() returned after {} seconds'.format(func.__module__, func.__name__, seconds)) + app.logger.info('%s.%s() returned after %f seconds', func.__module__, func.__name__, seconds) return result @@ -35,8 +35,7 @@ def _format_cache(func: Any) -> str: :return: The output of func.cache_info() as a compactly formatted string """ info = func.cache_info() - return '{}[hits={}, misses={}, size={}/{}]'.format(func.__name__, info.hits, info.misses, info.currsize, - info.maxsize) + return f'{func.__name__}[hits={info.hits}, misses={info.misses}, size={info.currsize}/{info.maxsize}]' # Tuple of lru_cache functions to log stats from @@ -52,7 +51,7 @@ def log_cache(func: WrappedFunc) -> WrappedFunc: def wrapped_function(*args: list, **kwargs: dict) -> Any: result = func(*args, **kwargs) - app.logger.info('Cache stats: ' + ', '.join(map(_format_cache, _caches))) + app.logger.info('Cache stats: %s', ', '.join(map(_format_cache, _caches))) return result diff --git a/packet/mail.py b/packet/mail.py index c0f9db6..e0f94d2 100644 --- a/packet/mail.py +++ b/packet/mail.py @@ -23,7 +23,7 @@ def send_start_packet_mail(packet: Packet) -> None: template = 'mail/packet_start' msg.body = render_template(template + '.txt', packet=packet) msg.html = render_template(template + '.html', packet=packet) - app.logger.info('Sending mail to ' + recipients[0]) + app.logger.info('Sending mail to %s', recipients[0]) mail.send(msg) def send_report_mail(form_results: ReportForm, reporter: str) -> None: @@ -39,5 +39,5 @@ def send_report_mail(form_results: ReportForm, reporter: str) -> None: template = 'mail/report' msg.body = render_template(template + '.txt', person=person, report=report, reporter=reporter) msg.html = render_template(template + '.html', person=person, report=report, reporter=reporter) - app.logger.info('Sending mail to ' + recipients[0]) + app.logger.info('Sending mail to %s', recipients[0]) mail.send(msg) diff --git a/packet/notifications.py b/packet/notifications.py index 5ec511a..7a27fd5 100644 --- a/packet/notifications.py +++ b/packet/notifications.py @@ -38,9 +38,9 @@ def send_notification(notification_body: dict, subscriptions: list, client: ones notification.post_body['include_player_ids'] = tokens onesignal_response = client.send_notification(notification) if onesignal_response.status_code == 200: - app.logger.info('The notification ({}) sent out successfully'.format(notification.post_body)) + app.logger.info('The notification (%s) sent out successfully', notification.post_body) else: - app.logger.warn('The notification ({}) was unsuccessful'.format(notification.post_body)) + app.logger.warning('The notification (%s) was unsuccessful', notification.post_body) @require_onesignal_intro diff --git a/packet/stats.py b/packet/stats.py index c6d6103..7ea08f3 100644 --- a/packet/stats.py +++ b/packet/stats.py @@ -54,21 +54,21 @@ def packet_stats(packet_id: int) -> PacketStats: print(dates) - upper_stats: Stats = {date: list() for date in dates} + upper_stats: Stats = {date: [] for date in dates} for uid, date in map(lambda sig: (sig.member, sig.updated), filter(lambda sig: sig.signed, packet.upper_signatures)): upper_stats[date.date()].append(uid) - fresh_stats: Stats = {date: list() for date in dates} + fresh_stats: Stats = {date: [] for date in dates} for username, date in map(lambda sig: (sig.freshman_username, sig.updated), filter(lambda sig: sig.signed, packet.fresh_signatures)): fresh_stats[date.date()].append(username) - misc_stats: Stats = {date: list() for date in dates} + misc_stats: Stats = {date: [] for date in dates} for uid, date in map(lambda sig: (sig.member, sig.updated), packet.misc_signatures): misc_stats[date.date()].append(uid) - total_stats = dict() + total_stats = {} for date in dates: total_stats[date.isoformat()] = { 'upper': upper_stats[date], diff --git a/packet/utils.py b/packet/utils.py index 496bf5c..2be1e55 100644 --- a/packet/utils.py +++ b/packet/utils.py @@ -87,7 +87,7 @@ def wrapped_function(*args: list, **kwargs: dict) -> Any: if app.config['REALM'] == 'csh': username = str(session['userinfo'].get('preferred_username', '')) if ldap.is_intromember(ldap.get_member(username)): - app.logger.warn('Stopped intro member {} from accessing upperclassmen packet'.format(username)) + app.logger.warning('Stopped intro member %s from accessing upperclassmen packet', username) return redirect(app.config['PROTOCOL'] + app.config['PACKET_INTRO'], code=301) return func(*args, **kwargs) @@ -107,7 +107,7 @@ def wrapped_function(*args: list, **kwargs: dict) -> Any: username = str(session['userinfo'].get('preferred_username', '')) member = ldap.get_member(username) if not ldap.is_evals(member): - app.logger.warn('Stopped member {} from accessing admin UI'.format(username)) + app.logger.warning('Stopped member %s from accessing admin UI', username) return redirect(app.config['PROTOCOL'] + app.config['PACKET_UPPER'], code=301) else: return redirect(app.config['PROTOCOL'] + app.config['PACKET_INTRO'], code=301) @@ -122,12 +122,12 @@ def notify_slack(name: str) -> None: Sends a congratulate on sight decree to Slack """ if app.config['SLACK_WEBHOOK_URL'] is None: - app.logger.warn('SLACK_WEBHOOK_URL not configured, not sending message to slack.') + app.logger.warning('SLACK_WEBHOOK_URL not configured, not sending message to slack.') return msg = f':pizza-party: {name} got :100: on packet! :pizza-party:' - requests.put(app.config['SLACK_WEBHOOK_URL'], json={'text': msg}) - app.logger.info('Posted 100% notification to slack for ' + name) + requests.put(app.config['SLACK_WEBHOOK_URL'], json={'text': msg}, timeout=5) + app.logger.info('Posted 100%% notification to slack for %s', name) def sync_freshman(freshmen_list: dict) -> None: From 1af767c8a4951235429de37b28afc7c5d6ba04e5 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Wed, 2 Sep 2026 00:37:25 -0400 Subject: [PATCH 6/6] fix: more lint --- packet/routes/api.py | 16 ++++++++-------- packet/routes/upperclassmen.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packet/routes/api.py b/packet/routes/api.py index d991dfe..bfdf160 100644 --- a/packet/routes/api.py +++ b/packet/routes/api.py @@ -15,7 +15,7 @@ create_new_packets, sync_with_ldap from packet.models import Packet, MiscSignature, NotificationSubscription, Freshman from packet.notifications import packet_signed_notification, packet_100_percent_notification -import packet.stats as stats +from packet import stats class POSTFreshman: @@ -173,21 +173,21 @@ def sign(packet_id: int, info: Dict[str, Any]) -> str: # Check if the CSHer is an upperclassman and if so, sign that row for sig in filter(lambda sig: sig.member == info['uid'], packet.upper_signatures): sig.signed = True - app.logger.info('Member {} signed packet {} as an upperclassman'.format(info['uid'], packet_id)) + app.logger.info('Member %s signed packet %d as an upperclassman', info['uid'], packet_id) return commit_sig(packet, was_100, info['uid']) # The CSHer is a misc so add a new row db.session.add(MiscSignature(packet=packet, member=info['uid'])) - app.logger.info('Member {} signed packet {} as a misc'.format(info['uid'], packet_id)) + app.logger.info('Member %s signed packet %d as a misc', info['uid'], packet_id) return commit_sig(packet, was_100, info['uid']) else: # Check if the freshman is onfloor and if so, sign that row for sig in filter(lambda sig: sig.freshman_username == info['uid'], packet.fresh_signatures): sig.signed = True - app.logger.info('Freshman {} signed packet {}'.format(info['uid'], packet_id)) + app.logger.info('Freshman %s signed packet %d', info['uid'], packet_id) return commit_sig(packet, was_100, info['uid']) - app.logger.warn("Failed to add {}'s signature to packet {}".format(info['uid'], packet_id)) + app.logger.warning("Failed to add %s's signature to packet %d", info['uid'], packet_id) return 'Error: Signature not valid. Reason: Unknown' @@ -203,7 +203,7 @@ def subscribe(info: Dict[str, Any]) -> str: subscription = NotificationSubscription(token=data['token'], freshman_username=info['uid']) db.session.add(subscription) db.session.commit() - return 'Token subscribed for ' + info['uid'] + return f'Token subscribed for {info['uid']}' @app.route('/api/v1/report/', methods=['POST']) @@ -212,7 +212,7 @@ def subscribe(info: Dict[str, Any]) -> str: def report(info: Dict[str, Any]) -> str: form_results = request.form send_report_mail(form_results, get_rit_name(info['uid'])) - return 'Success: ' + get_rit_name(info['uid']) + ' sent a report' + return f'Success: {get_rit_name(info['uid'])} sent a report' @app.route('/api/v1/stats/packet/') @@ -247,4 +247,4 @@ def commit_sig(packet: Packet, was_100: bool, uid: str) -> str: packet_100_percent_notification(packet) notify_slack(packet.freshman.name) - return 'Success: Signed Packet: ' + packet.freshman_username + return f'Success: Signed Packet: {packet.freshman_username}' diff --git a/packet/routes/upperclassmen.py b/packet/routes/upperclassmen.py index 7c21916..2e33472 100644 --- a/packet/routes/upperclassmen.py +++ b/packet/routes/upperclassmen.py @@ -50,8 +50,8 @@ def upperclassmen_total(info: Optional[Dict[str, Any]] = None) -> str: open_packets = Packet.open_packets() # Sum up the signed packets per upperclassman - upperclassmen: Dict[str, int] = dict() - misc: Dict[str, int] = dict() + upperclassmen: Dict[str, int] = {} + misc: Dict[str, int] = {} for packet in open_packets: for sig in packet.upper_signatures: if sig.member not in upperclassmen: