From 00f00ac55c0118e0b903d397bb92d5ae0279c0fe Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sat, 28 Feb 2026 00:46:30 -0500 Subject: [PATCH 01/16] fix intro evals dying --- conditional/blueprints/intro_evals.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/conditional/blueprints/intro_evals.py b/conditional/blueprints/intro_evals.py index e4464c3d..1085fcdc 100644 --- a/conditional/blueprints/intro_evals.py +++ b/conditional/blueprints/intro_evals.py @@ -82,8 +82,7 @@ def get_intro_members_without_accounts(): # freshmen who don't have accounts freshman_accounts = list(FreshmanAccount.query.filter( - FreshmanAccount.eval_date > semester_start, - FreshmanAccount.eval_date > datetime.now())) + FreshmanAccount.eval_date >= semester_start)) ie_members = [] @@ -124,6 +123,8 @@ def get_intro_members_without_accounts(): } ie_members.append(freshman) + print(ie_members) + return ie_members @intro_evals_bp.route('/intro_evals/') @@ -197,7 +198,7 @@ def display_intro_evals(internal=False, user_dict=None): uid = member.uid name = member.cn freshman_data = FreshmanEvalData.query.filter( - FreshmanEvalData.eval_date > semester_start, + FreshmanEvalData.eval_date >= semester_start, FreshmanEvalData.uid == uid).first() if freshman_data is None: From a388b70c7405e9974d1f9a9b70ad3407d20637d7 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sat, 28 Feb 2026 00:50:45 -0500 Subject: [PATCH 02/16] fix gatekeep active calcuations --- conditional/util/member.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conditional/util/member.py b/conditional/util/member.py index f091cd4f..098ba0b9 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -162,9 +162,9 @@ def req_cm(uid, members_on_coop=None): def is_gatekeep_active(): - today = datetime.today() - 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()) + today = datetime.today().date() + 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()) return not (before_evals_one > 0 or before_evals_two > 0) From c43630d9c5327ee7eda66879f321e8a7b107d1b0 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sat, 28 Feb 2026 01:09:00 -0500 Subject: [PATCH 03/16] fix spring evals 500 --- conditional/blueprints/spring_evals.py | 3 ++- conditional/util/member.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/conditional/blueprints/spring_evals.py b/conditional/blueprints/spring_evals.py index 80c91943..ad429f0a 100644 --- a/conditional/blueprints/spring_evals.py +++ b/conditional/blueprints/spring_evals.py @@ -112,13 +112,14 @@ def display_spring_evals(internal=False, user_dict=None): member_major_projects = major_projects.get(uid, []) passed_mps = [project for project in member_major_projects if project['status'] == 'Passed'] + cms = req_cm(uid, coop_members) member = { 'name': name, 'uid': uid, 'status': spring_entry.status, 'committee_meetings': cm_attended_count, - 'req_meetings': req_cm(uid, coop_members), + 'req_meetings': cms, 'house_meetings_missed': member_missed_hms, 'major_projects': member_major_projects } diff --git a/conditional/util/member.py b/conditional/util/member.py index 098ba0b9..0c171a31 100644 --- a/conditional/util/member.py +++ b/conditional/util/member.py @@ -143,11 +143,12 @@ def get_hm(member, only_absent=False): return h_meetings -@service_cache(maxsize=128) +# @service_cache(maxsize=128) # Can't hash because members_on_coop is a list 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 + if members_on_coop: on_coop = uid in members_on_coop else: From 12ee144f69224bcce29ca623b014f596ba3bc7c4 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sat, 28 Feb 2026 01:16:26 -0500 Subject: [PATCH 04/16] made lint happy --- conditional/blueprints/intro_evals.py | 2 -- conditional/blueprints/spring_evals.py | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/conditional/blueprints/intro_evals.py b/conditional/blueprints/intro_evals.py index 1085fcdc..262ae18b 100644 --- a/conditional/blueprints/intro_evals.py +++ b/conditional/blueprints/intro_evals.py @@ -1,5 +1,3 @@ -from datetime import datetime - import structlog from flask import Blueprint, request from sqlalchemy import func diff --git a/conditional/blueprints/spring_evals.py b/conditional/blueprints/spring_evals.py index ad429f0a..80c91943 100644 --- a/conditional/blueprints/spring_evals.py +++ b/conditional/blueprints/spring_evals.py @@ -112,14 +112,13 @@ def display_spring_evals(internal=False, user_dict=None): member_major_projects = major_projects.get(uid, []) passed_mps = [project for project in member_major_projects if project['status'] == 'Passed'] - cms = req_cm(uid, coop_members) member = { 'name': name, 'uid': uid, 'status': spring_entry.status, 'committee_meetings': cm_attended_count, - 'req_meetings': cms, + 'req_meetings': req_cm(uid, coop_members), 'house_meetings_missed': member_missed_hms, 'major_projects': member_major_projects } From 684e3e567f0a87f996e9cd4dca6390df2bfcbb1d Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Wed, 4 Mar 2026 09:02:36 -0500 Subject: [PATCH 05/16] Compose watch (#499) * added compose support for this * update readme * made sonarqube happy --- README.md | 5 +++++ docker-compose.yaml | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index 0a245208..d174bdeb 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,11 @@ It is likely easier to use containers like `podman` or `docker` or the correspon With podman, I have been using +```sh +podman compose up --watch +``` + +If you want, you can run without compose support using ```sh podman compose up --force-recreate --build ``` diff --git a/docker-compose.yaml b/docker-compose.yaml index 755ee6df..9ecaf96f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,6 +9,18 @@ services: - "127.0.0.1:8080:8080" volumes: - ./migrations:/opt/conditional/migrations + develop: + watch: + - action: sync+restart + path: ./conditional + target: /opt/conditional/conditional + ignore: + - __pycache__ + - action: sync+restart + path: ./config.py + target: /opt/conditional/config.py + - action: rebuild + path: ./frontend conditional-postgres: image: docker.io/postgres container_name: conditional-postgres From 9a812a2cccc4bd03a5dd8a1860a7caeb53b1fc3c Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Wed, 4 Mar 2026 09:09:17 -0500 Subject: [PATCH 06/16] Update sonarqube to run on its own thing and only if it's not dependabot --- .github/workflows/sonarqube.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 5c157a50..8acafdb0 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -10,8 +10,9 @@ on: jobs: build: - name: Build and analyze + name: Sonarqube Analysis runs-on: ubuntu-latest + if: ${{ !startsWith(github.head_ref, 'dependabot/') }} steps: - uses: actions/checkout@v4 From 9677a8e415238672ca8807b8d5bf59bc5fc4e58f Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Wed, 4 Mar 2026 09:11:30 -0500 Subject: [PATCH 07/16] Delete .github/pull_request_template.md --- .github/pull_request_template.md | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 1cd3021f..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,19 +0,0 @@ -## What - -_what the PR changes_ - -## Why - -_why these changes were made_ - -## Test Plan - -_how did you verify these changes did what you expected_ - -## Env Vars - -_did you add, remove, or rename any environment variables_ - -## Checklist - -- [ ] Tested all changes locally From d515b5da36cd9e64961ed26d5fe069481e028611 Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Wed, 4 Mar 2026 09:12:41 -0500 Subject: [PATCH 08/16] Rename workflow from Build to Sonarqube --- .github/workflows/sonarqube.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 8acafdb0..f339ad7b 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -1,4 +1,4 @@ -name: Build +name: Sonarqube on: push: From 535d469577f75a56acb9db4029f171347f6b86ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:52:00 -0500 Subject: [PATCH 09/16] Bump lazy-object-proxy from 1.4.3 to 1.12.0 (#498) Bumps [lazy-object-proxy](https://github.com/ionelmc/python-lazy-object-proxy) from 1.4.3 to 1.12.0. - [Changelog](https://github.com/ionelmc/python-lazy-object-proxy/blob/master/CHANGELOG.rst) - [Commits](https://github.com/ionelmc/python-lazy-object-proxy/compare/v1.4.3...v1.12.0) --- updated-dependencies: - dependency-name: lazy-object-proxy dependency-version: 1.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Noah Hanford --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index 48c51eb3..831e09de 100644 --- a/requirements.in +++ b/requirements.in @@ -12,7 +12,7 @@ gunicorn~=22.0.0 isort~=6.1.0 itsdangerous~=2.2.0 Jinja2~=3.1.6 -lazy-object-proxy~=1.4.0 +lazy-object-proxy~=1.12.0 Mako~=1.3.10 MarkupSafe~=3.0.2 mccabe~=0.7.0 diff --git a/requirements.txt b/requirements.txt index 7036755e..43c95815 100644 --- a/requirements.txt +++ b/requirements.txt @@ -88,7 +88,7 @@ jinja2==3.1.6 # via # -r requirements.in # flask -lazy-object-proxy==1.4.3 +lazy-object-proxy==1.12.0 # via -r requirements.in mako==1.3.10 # via From f480179655b620b26a9f9393c15b97fe0b6e6f49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:00:46 -0500 Subject: [PATCH 10/16] Bump sqlalchemy from 2.0.45 to 2.0.48 (#497) Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.45 to 2.0.48. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-version: 2.0.48 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Noah Hanford --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index 831e09de..ab2d8918 100644 --- a/requirements.in +++ b/requirements.in @@ -24,7 +24,7 @@ python-dateutil~=2.6.1 python-editor~=1.0.3 sentry-sdk[flask]~=2.24.1 six~=1.17.0 -SQLAlchemy~=2.0.40 +SQLAlchemy~=2.0.48 structlog~=25.5.0 Werkzeug~=3.1.6 wrapt~=1.17.2 diff --git a/requirements.txt b/requirements.txt index 43c95815..76cd96c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -170,7 +170,7 @@ six==1.17.0 # -r requirements.in # pyjwkest # python-dateutil -sqlalchemy==2.0.45 +sqlalchemy==2.0.48 # via # -r requirements.in # alembic From 4df78fae37d5f749949035c344bce38189350b15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:05:50 -0500 Subject: [PATCH 11/16] Bump gunicorn from 22.0.0 to 25.1.0 (#496) Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 22.0.0 to 25.1.0. - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/22.0.0...25.1.0) --- updated-dependencies: - dependency-name: gunicorn dependency-version: 25.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index ab2d8918..2e8f7660 100644 --- a/requirements.in +++ b/requirements.in @@ -8,7 +8,7 @@ Flask-Migrate~=2.1.1 Flask-Gzip~=0.2 Flask-pyoidc~=3.14.3 Flask-SQLAlchemy~=3.1.1 -gunicorn~=22.0.0 +gunicorn~=25.1.0 isort~=6.1.0 itsdangerous~=2.2.0 Jinja2~=3.1.6 diff --git a/requirements.txt b/requirements.txt index 76cd96c2..3269b694 100644 --- a/requirements.txt +++ b/requirements.txt @@ -68,7 +68,7 @@ future==1.0.0 # via pyjwkest greenlet==3.3.0 # via sqlalchemy -gunicorn==22.0.0 +gunicorn==25.1.0 # via -r requirements.in idna==3.11 # via requests From a4f0207233250497e28333b5621d4f9fbe353273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:12:13 -0500 Subject: [PATCH 12/16] Bump serialize-javascript, terser-webpack-plugin and copy-webpack-plugin (#501) Removes [serialize-javascript](https://github.com/yahoo/serialize-javascript). It's no longer used after updating ancestor dependencies [serialize-javascript](https://github.com/yahoo/serialize-javascript), [terser-webpack-plugin](https://github.com/webpack/terser-webpack-plugin) and [copy-webpack-plugin](https://github.com/webpack/copy-webpack-plugin). These dependencies need to be updated together. Removes `serialize-javascript` Updates `terser-webpack-plugin` from 5.3.14 to 5.3.17 - [Release notes](https://github.com/webpack/terser-webpack-plugin/releases) - [Changelog](https://github.com/webpack/terser-webpack-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/terser-webpack-plugin/compare/v5.3.14...v5.3.17) Updates `copy-webpack-plugin` from 13.0.1 to 14.0.0 - [Release notes](https://github.com/webpack/copy-webpack-plugin/releases) - [Changelog](https://github.com/webpack/copy-webpack-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/copy-webpack-plugin/compare/v13.0.1...v14.0.0) --- updated-dependencies: - dependency-name: serialize-javascript dependency-version: dependency-type: indirect - dependency-name: terser-webpack-plugin dependency-version: 5.3.17 dependency-type: indirect - dependency-name: copy-webpack-plugin dependency-version: 14.0.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Noah Hanford --- package-lock.json | 166 +++++++++++++++++++++++++++++----------------- package.json | 2 +- 2 files changed, 105 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6eeeeca2..840f9353 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@babel/preset-env": "^7.28.5", "babel-loader": "^10.0.0", "bootstrap-icons": "^1.13.1", - "copy-webpack-plugin": "^13.0.1", + "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.2", "eslint-config-google": "^0.14.0", "expose-loader": "^5.0.1", @@ -85,7 +85,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1740,6 +1739,7 @@ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -1759,6 +1759,7 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1772,6 +1773,7 @@ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1782,6 +1784,7 @@ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -1797,6 +1800,7 @@ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1814,7 +1818,8 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", @@ -1822,6 +1827,7 @@ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -1835,6 +1841,7 @@ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -1848,6 +1855,7 @@ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1872,6 +1880,7 @@ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1889,7 +1898,8 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@eslint/js": { "version": "9.39.1", @@ -1897,6 +1907,7 @@ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1910,6 +1921,7 @@ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1920,6 +1932,7 @@ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -1934,6 +1947,7 @@ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=18.18.0" } @@ -1944,6 +1958,7 @@ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -1958,6 +1973,7 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=12.22" }, @@ -1972,6 +1988,7 @@ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=18.18" }, @@ -2783,7 +2800,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2797,6 +2813,7 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2807,6 +2824,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2858,14 +2876,16 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "Python-2.0" + "license": "Python-2.0", + "peer": true }, "node_modules/babel-loader": { "version": "10.0.0", @@ -2957,7 +2977,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/baseline-browser-mapping": { "version": "2.8.23", @@ -3049,6 +3070,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3074,7 +3096,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -3101,6 +3122,7 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -3157,6 +3179,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3169,7 +3192,8 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/colorette": { "version": "2.0.20", @@ -3188,23 +3212,24 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "peer": true }, "node_modules/copy-webpack-plugin": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz", - "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz", + "integrity": "sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==", "dev": true, "license": "MIT", "dependencies": { "glob-parent": "^6.0.1", "normalize-path": "^3.0.0", "schema-utils": "^4.2.0", - "serialize-javascript": "^6.0.2", + "serialize-javascript": "^7.0.3", "tinyglobby": "^0.2.12" }, "engines": { - "node": ">= 18.12.0" + "node": ">= 20.9.0" }, "funding": { "type": "opencollective", @@ -3214,6 +3239,16 @@ "webpack": "^5.1.0" } }, + "node_modules/copy-webpack-plugin/node_modules/serialize-javascript": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", + "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/core-js-compat": { "version": "3.46.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", @@ -3323,7 +3358,8 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/detect-libc": { "version": "1.0.3", @@ -3496,6 +3532,7 @@ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3509,6 +3546,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3525,6 +3563,7 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3542,6 +3581,7 @@ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -3560,6 +3600,7 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -3573,6 +3614,7 @@ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -3590,6 +3632,7 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -3600,6 +3643,7 @@ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3616,7 +3660,8 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", @@ -3624,6 +3669,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3637,6 +3683,7 @@ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -3655,6 +3702,7 @@ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -3668,6 +3716,7 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -3750,14 +3799,16 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-uri": { "version": "3.1.0", @@ -3792,6 +3843,7 @@ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -3871,6 +3923,7 @@ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -3884,7 +3937,8 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/function-bind": { "version": "1.1.2", @@ -3932,6 +3986,7 @@ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -3988,6 +4043,7 @@ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } @@ -3998,6 +4054,7 @@ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -4056,6 +4113,7 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.19" } @@ -4174,8 +4232,7 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jquery-ui": { "version": "1.14.1", @@ -4193,6 +4250,7 @@ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -4218,7 +4276,8 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -4239,7 +4298,8 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/keyv": { "version": "4.5.4", @@ -4247,6 +4307,7 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -4267,6 +4328,7 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -4322,7 +4384,8 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/merge-stream": { "version": "2.0.0", @@ -4358,6 +4421,7 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4397,7 +4461,8 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/neo-async": { "version": "2.6.2", @@ -4437,6 +4502,7 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -4497,6 +4563,7 @@ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4584,7 +4651,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -4684,6 +4750,7 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -4694,20 +4761,11 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -4839,6 +4897,7 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -4852,12 +4911,6 @@ "node": ">=18.0.0" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/sass": { "version": "1.93.3", "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.3.tgz", @@ -4983,7 +5036,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5021,16 +5073,6 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -5098,6 +5140,7 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -5169,16 +5212,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "version": "5.3.17", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.17.tgz", + "integrity": "sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -5265,7 +5307,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5279,6 +5320,7 @@ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -5374,6 +5416,7 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -5405,7 +5448,6 @@ "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -5455,7 +5497,6 @@ "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", @@ -5576,6 +5617,7 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index 95438c21..b39cdc23 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@babel/preset-env": "^7.28.5", "babel-loader": "^10.0.0", "bootstrap-icons": "^1.13.1", - "copy-webpack-plugin": "^13.0.1", + "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.2", "eslint-config-google": "^0.14.0", "expose-loader": "^5.0.1", From cbf6a74be7547538e2996df4311079902d02d370 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:18:29 -0500 Subject: [PATCH 13/16] Bump minimatch from 3.1.2 to 3.1.5 (#492) Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.5. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 840f9353..0f69d912 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4416,9 +4416,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "peer": true, From b50024ebade8aaad708dec5779b8ec09f5396b96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:30:37 -0500 Subject: [PATCH 14/16] Bump python-dateutil from 2.6.1 to 2.9.0.post0 (#495) * Bump python-dateutil from 2.6.1 to 2.9.0.post0 Bumps [python-dateutil](https://github.com/dateutil/dateutil) from 2.6.1 to 2.9.0.post0. - [Release notes](https://github.com/dateutil/dateutil/releases) - [Changelog](https://github.com/dateutil/dateutil/blob/master/NEWS) - [Commits](https://github.com/dateutil/dateutil/compare/2.6.1...2.9.0.post0) --- updated-dependencies: - dependency-name: python-dateutil dependency-version: 2.9.0.post0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * remove dependency --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Noah Hanford (spaced) Co-authored-by: Noah Hanford --- requirements.in | 1 - requirements.txt | 33 +++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/requirements.in b/requirements.in index 2e8f7660..c1753f85 100644 --- a/requirements.in +++ b/requirements.in @@ -20,7 +20,6 @@ oic~=1.6.1 pip-tools~=7.4.1 psycopg2-binary~=2.9.3 pylint~=3.3.6 -python-dateutil~=2.6.1 python-editor~=1.0.3 sentry-sdk[flask]~=2.24.1 six~=1.17.0 diff --git a/requirements.txt b/requirements.txt index 3269b694..9a54f1ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile requirements.in -o requirements.txt +# uv pip compile requirements.in alembic==1.15.2 # via # -r requirements.in @@ -19,7 +19,7 @@ build==1.4.0 # via pip-tools bytecode==0.17.0 # via ddtrace -certifi==2026.1.4 +certifi==2026.2.25 # via # requests # sentry-sdk @@ -40,13 +40,13 @@ ddtrace==4.4.0 # via -r requirements.in defusedxml==0.7.1 # via oic -dill==0.4.0 +dill==0.4.1 # via pylint dnspython==2.8.0 # via srvlookup envier==0.6.1 # via ddtrace -flask==3.1.2 +flask==3.1.3 # via # -r requirements.in # flask-gzip @@ -66,7 +66,7 @@ flask-sqlalchemy==3.1.1 # flask-migrate future==1.0.0 # via pyjwkest -greenlet==3.3.0 +greenlet==3.3.2 # via sqlalchemy gunicorn==25.1.0 # via -r requirements.in @@ -111,16 +111,18 @@ oic==1.6.1 # via # -r requirements.in # flask-pyoidc -opentelemetry-api==1.39.1 +opentelemetry-api==1.40.0 # via ddtrace -packaging==25.0 +packaging==26.0 # via # build # gunicorn # wheel +pip==26.0.1 + # via pip-tools pip-tools==7.4.1 # via -r requirements.in -platformdirs==4.5.1 +platformdirs==4.9.2 # via pylint psycopg2-binary==2.9.11 # via -r requirements.in @@ -130,7 +132,7 @@ pyasn1==0.6.2 # python-ldap pyasn1-modules==0.4.2 # via python-ldap -pycparser==2.23 +pycparser==3.0 # via cffi pycryptodomex==3.23.0 # via @@ -140,7 +142,7 @@ pydantic==2.12.5 # via pydantic-settings pydantic-core==2.41.5 # via pydantic -pydantic-settings==2.12.0 +pydantic-settings==2.13.1 # via oic pyjwkest==1.4.4 # via oic @@ -150,9 +152,7 @@ pyproject-hooks==1.2.0 # via # build # pip-tools -python-dateutil==2.6.1 - # via -r requirements.in -python-dotenv==1.2.1 +python-dotenv==1.2.2 # via pydantic-settings python-editor==1.0.4 # via -r requirements.in @@ -163,13 +163,14 @@ requests==2.32.5 # flask-pyoidc # oic # pyjwkest -sentry-sdk[flask]==2.24.1 +sentry-sdk==2.24.1 # via -r requirements.in +setuptools==82.0.0 + # via pip-tools six==1.17.0 # via # -r requirements.in # pyjwkest - # python-dateutil sqlalchemy==2.0.48 # via # -r requirements.in @@ -201,7 +202,7 @@ werkzeug==3.1.6 # via # -r requirements.in # flask -wheel==0.46.2 +wheel==0.46.3 # via pip-tools wrapt==1.17.3 # via From b5a0e7a7a95178ef7d62b13e2f9d4bbe9d9f3a88 Mon Sep 17 00:00:00 2001 From: pikachu0542 <112343747+pikachu0542@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:20:23 -0400 Subject: [PATCH 15/16] New major project form :fire: :fire: :fire: (#503) * Fixed migrations fr this time * Restored all my existing work * Made form look decent * More display changes * More display changes * Rebased with dev and fixed issues that resulted from that * Form is almost done i think * Fixed s3 uploads and file display * Added new MP info to dashboard * Uncommented the slackbot ping * Fixed lint * Renamed db column to make lint happy * Reduced duplicate code byt moving helper funcs to utils * Fix a sonarqube issue * Fixed some more sonarqube issues * Added default values for s3 related env vars * Added S3 creds info to readme * updated readme info * Added message to migrations * Fixed bad mobile styling and also s3 issue * Made the links input and dropzone each be half the full row width * Changed replace to replaceAll * changed loop method to make sonarqube happy * Made form fields required and fixed skill tag error * Fixed dashboard divider issue * Fixed links not having tall enough line height on mobile --- Dockerfile | 4 +- README.md | 14 + conditional/blueprints/dashboard.py | 24 +- .../blueprints/major_project_submission.py | 148 +++- conditional/models/models.py | 22 +- conditional/templates/dashboard.html | 781 +++++++++--------- .../templates/major_project_submission.html | 395 +++++++-- conditional/util/major_project.py | 26 + conditional/util/s3.py | 24 + config.env.py | 7 + frontend/images/photo_video.svg | 6 + .../javascript/modules/majorProjectForm.js | 101 ++- .../javascript/modules/majorProjectStatus.js | 2 +- frontend/stylesheets/pages/_dashboard.scss | 8 + .../stylesheets/pages/_major-project.scss | 136 ++- ...ae578b76143_add_date_to_.mp_submission.py} | 0 .../versions/6c4cf35d7c0c_update_mp_table.py | 43 + .../versions/95538b39976f_mp_time_spent.py | 28 + package-lock.json | 202 ++--- package.json | 2 +- requirements.in | 3 + requirements.txt | 35 +- 22 files changed, 1389 insertions(+), 622 deletions(-) create mode 100644 conditional/util/major_project.py create mode 100644 conditional/util/s3.py create mode 100644 frontend/images/photo_video.svg rename migrations/versions/{6ae578b76143_.py => 6ae578b76143_add_date_to_.mp_submission.py} (100%) create mode 100644 migrations/versions/6c4cf35d7c0c_update_mp_table.py create mode 100644 migrations/versions/95538b39976f_mp_time_spent.py diff --git a/Dockerfile b/Dockerfile index d6b15b39..c7acca14 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,8 +35,8 @@ ENV PORT=${PORT} EXPOSE ${PORT} COPY conditional /opt/conditional/conditional -COPY *.py package.json /opt/conditional -COPY --from=build-frontend /opt/conditional/conditional/static /opt/conditional/conditional/static +COPY *.py package.json /opt/conditional/ +COPY --from=build-frontend /opt/conditional/conditional/static/ /opt/conditional/conditional/static/ RUN ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime diff --git a/README.md b/README.md index d174bdeb..96314d0b 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,20 @@ OIDC_CLIENT_CONFIG = { } ``` +#### Add S3 Config +An S3 bucket is used to store files that users upload (currently just for major project submissions). In order to have this work properly, you need to provide some credentials to the app. + +There are 2 ways that you can get the needed credentials. +1. Reach out to an RTP for creds to the dev bucket +2. Create your own bucket using [DEaDASS](https://deadass.csh.rit.edu/), and the site will give you the credentials you need. + +```py +S3_URI = env.get("S3_URI", "https://s3.csh.rit.edu") +S3_BUCKET_ID = env.get("S3_BUCKET_ID", "major-project-media") +AWS_ACCESS_KEY_ID = env.get("AWS_ACCESS_KEY_ID", "") +AWS_SECRET_ACCESS_KEY = env.get("AWS_SECRET_ACCESS_KEY", "") +``` + #### Database You can either develop using the dev database, or use the local database provided in the docker compose file diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index 32587a77..39cf8265 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -4,7 +4,6 @@ from conditional import start_of_year, auth from conditional.models.models import Conditional from conditional.models.models import HouseMeeting -from conditional.models.models import MajorProject from conditional.models.models import MemberHouseMeetingAttendance from conditional.models.models import MemberSeminarAttendance from conditional.models.models import TechnicalSeminar @@ -12,6 +11,7 @@ from conditional.util.auth import get_user from conditional.util.flask import render_template from conditional.util.housing import get_queue_position +from conditional.util.major_project import get_project_list from conditional.util.member import gatekeep_values, get_active_members, get_freshman_data, get_voting_members, \ get_cm, get_hm, is_gatekeep_active, req_cm from conditional.util.user_dict import user_dict_is_active, user_dict_is_bad_standing, user_dict_is_intromember, \ @@ -82,15 +82,23 @@ def display_dashboard(user_dict=None): data['housing'] = housing + proj_list = get_project_list() + data['major_projects'] = [ { - 'id': p.id, - 'name': p.name, - 'status': p.status, - 'description': p.description - } for p in - MajorProject.query.filter(MajorProject.uid == uid, - MajorProject.date > start_of_year())] + "id": p.id, + "date": p.date, + "name": p.name, + "proj_name": p.name, + "tldr": p.tldr, + "time_spent": p.time_spent, + "skills": p.skills, + "desc": p.description, + "links": list(filter(None, p.links.split("\n"))), + "status": p.status, + } + for p in proj_list + ] data['major_projects_count'] = len(data['major_projects']) diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index aa3ba66c..0bebff28 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -1,30 +1,35 @@ +import collections import json -import requests +import os from flask import Blueprint from flask import request from flask import jsonify from flask import redirect -from sqlalchemy import desc - +import requests +import boto3 import structlog -from conditional.util.context_processors import get_member_name +from werkzeug.utils import secure_filename +from conditional import db, get_user, auth, app from conditional.models.models import MajorProject +from conditional.models.models import MajorProjectSkill -from conditional.util.ldap import ldap_is_eval_director +from conditional.util.context_processors import get_member_name from conditional.util.ldap import ldap_get_member from conditional.util.flask import render_template +from conditional.util.s3 import list_files_in_folder +from conditional.util.user_dict import user_dict_is_eval_director +from conditional.util.major_project import get_project_list -from conditional import db, start_of_year, get_user, auth, app +collections.Callable = collections.abc.Callable logger = structlog.get_logger() major_project_bp = Blueprint("major_project_bp", __name__) - @major_project_bp.route("/major_project/") @auth.oidc_auth("default") @get_user @@ -32,29 +37,58 @@ def display_major_project(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info("Display Major Project Page") - major_projects = [ + # There is probably a better way to do this, but it does work + proj_list: list = get_project_list() + + bucket: str = app.config['S3_BUCKET_ID'] + + major_projects: list[dict] = [ { + "id": p.id, + "date": p.date, "username": p.uid, "name": ldap_get_member(p.uid).cn, "proj_name": p.name, + "tldr": p.tldr, + "time_spent": p.time_spent, + "skills": p.skills, + "desc": p.description, + "links": list(filter(None, p.links.split("\n"))), "status": p.status, - "description": p.description, - "id": p.id, "is_owner": bool(user_dict["username"] == p.uid), + "files": list_files_in_folder(bucket, f"{p.id}/") } - for p in MajorProject.query.filter( - MajorProject.date > start_of_year() - ).order_by(desc(MajorProject.id)) + for p in proj_list ] - major_projects_len = len(major_projects) # return names in 'first last (username)' format return render_template( "major_project_submission.html", major_projects=major_projects, - major_projects_len=major_projects_len, - username=user_dict["username"], - ) + major_projects_len=len(major_projects), + username=user_dict["username"]) + +@major_project_bp.route("/major_project/upload", methods=["POST"]) +@auth.oidc_auth("default") +@get_user +def upload_major_project_files(user_dict=None): + log = logger.new(request=request, auth_dict=user_dict) + log.info('Uploading Major Project File(s)') + + if len(list(request.files.keys())) <1: + return "No file", 400 + + # Temporarily save files to a place, to be uploaded on submit + for _, file in request.files.lists(): + file = file[0] + safe_name: str = secure_filename(file.filename) + filename = f"/tmp/{user_dict['username']}/{safe_name}" + + os.makedirs(os.path.dirname(filename), exist_ok=True) + file.save(filename) + + return jsonify({"success": True}), 200 + @major_project_bp.route("/major_project/submit", methods=["POST"]) @@ -65,27 +99,79 @@ def submit_major_project(user_dict=None): log.info("Submit Major Project") post_data = request.get_json() + name = post_data["projectName"] + tldr = post_data['projectTldr'] + time_spent = post_data['projectTimeSpent'] + skills = post_data['projectSkills'] description = post_data["projectDescription"] + links = post_data['projectLinks'] + + user_id = user_dict['username'] + + log.info(user_id) - if name == "" or description == "": + # All fields are required in order to be able to submit the form + if not name or not tldr or not time_spent or not description: return jsonify({"success": False}), 400 - project = MajorProject(user_dict["username"], name, description) - # Don't you dare try pinging @channel + project: MajorProject = MajorProject(user_id, name, tldr, time_spent, description, links) + + # Save the info to the database + db.session.add(project) + db.session.commit() + + project = MajorProject.query.filter( + MajorProject.name == name, + MajorProject.uid == user_id + ).first() + + skills_list: list = list(filter(lambda x: x != 'None', skills)) + + for skill in skills_list: + skill = skill.strip() + + if skill not in ("", 'None'): + mp_skill = MajorProjectSkill(project.id, skill) + db.session.add(mp_skill) + + db.session.commit() + + # Fail if attempting to retreive non-existent project + if project is None: + return jsonify({"success": False}), 500 + + # Sanitize input so that the Slackbot cannot ping @channel name = name.replace(" *{get_member_name(username)}* ({username})" - f" submitted their major project, *{name}*! Please be sure to reach out" - f" to E-Board members to answer any questions they may have regarding" - f" your project!" + "text": f" *{get_member_name(user_id)}* ({user_id})" + f" submitted their major project, *{name}*!" } ) - db.session.add(project) - db.session.commit() + return jsonify({"success": True}), 200 @@ -95,7 +181,7 @@ def submit_major_project(user_dict=None): def major_project_review(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - if not ldap_is_eval_director(user_dict["account"]): + if not user_dict_is_eval_director(user_dict["account"]): return redirect("/dashboard", code=302) post_data = request.get_json() @@ -106,8 +192,10 @@ def major_project_review(user_dict=None): print(post_data) MajorProject.query.filter(MajorProject.id == pid).update({"status": status}) + db.session.flush() db.session.commit() + return jsonify({"success": True}), 200 @@ -121,10 +209,12 @@ def major_project_delete(pid, user_dict=None): major_project = MajorProject.query.filter(MajorProject.id == pid).first() creator = major_project.uid - if creator == user_dict["username"] or ldap_is_eval_director(user_dict["account"]): + if creator == user_dict["username"] or user_dict_is_eval_director(user_dict["account"]): MajorProject.query.filter(MajorProject.id == pid).delete() + db.session.flush() db.session.commit() + return jsonify({"success": True}), 200 return "Must be project owner to delete!", 401 diff --git a/conditional/models/models.py b/conditional/models/models.py index 2683a599..f3a52072 100644 --- a/conditional/models/models.py +++ b/conditional/models/models.py @@ -127,27 +127,41 @@ def __init__(self, fid, seminar_id): self.fid = fid self.seminar_id = seminar_id - class MajorProject(db.Model): __tablename__ = 'major_projects' id = Column(Integer, primary_key=True) date = Column(Date, nullable=False) uid = Column(String(32), nullable=False, index=True) name = Column(String(64), nullable=False) - description = Column(Text) + tldr = Column(String(128), nullable=True) + time_spent = Column(Text, nullable=True) + description = Column(Text, nullable=False) + links = Column(Text, nullable=True) active = Column(Boolean, nullable=False) status = Column(Enum('Pending', 'Passed', 'Failed', name="major_project_enum"), nullable=False) - def __init__(self, uid, name, desc): + def __init__(self, uid, name, tldr, time_spent, description, links): # pylint: disable=too-many-positional-arguments self.uid = uid self.date = datetime.now() self.name = name - self.description = desc + self.tldr = tldr + self.time_spent = time_spent + self.description = description + self.links = links self.status = 'Pending' self.active = True +class MajorProjectSkill(db.Model): + __tablename__ = "major_project_skills" + project_id = Column(Integer, ForeignKey('major_projects.id', ondelete="cascade"), nullable=False, primary_key=True) + skill = Column(Text, nullable=False, primary_key=True) + + def __init__(self, project_id, skill): + self.project_id = project_id + self.skill = skill + class HouseMeeting(db.Model): __tablename__ = 'house_meetings' diff --git a/conditional/templates/dashboard.html b/conditional/templates/dashboard.html index 397c8ef4..6fba54ef 100644 --- a/conditional/templates/dashboard.html +++ b/conditional/templates/dashboard.html @@ -15,21 +15,21 @@

{{ get_member_name(username) }}

{% if active %} - Active + Active {% else %} - Inactive + Inactive {% endif %} - + {% if onfloor %} - On-floor Status + On-floor Status {% else %} - Off-floor Status + Off-floor Status {% endif %} - + {% if voting %} - Voting + Voting {% else %} - Non-Voting + Non-Voting {% endif %}
@@ -38,7 +38,7 @@
- + {% if freshman %}
@@ -55,16 +55,16 @@

Freshman Evaluations
- - - - - - - + + + + + + @@ -74,378 +74,415 @@

Freshman Evaluations {% if freshman['committee_meetings'] >= 6 %} {% else %} {% endif %} {{freshman['committee_meetings']}} / 6 - - - -

- - - - -
Evaluations Date{{freshman['eval_date']}}
Signatures Missed - {% if freshman['sig_missed'] == 0 %} - None {% else %} - {{freshman['sig_missed']}} {% endif %} +
Evaluations Date{{freshman['eval_date']}}
Signatures Missed + {% if freshman['sig_missed'] == 0 %} + None {% else %} + {{freshman['sig_missed']}} {% endif %}
House Meetings Missed - {% if freshman['hm_missed'] == 0 %} - None {% else %} - {{ freshman['hm_missed'] }} {% endif %} - -
-
- Technical Seminars {% if freshman['ts_total'] == 0 %} -
- -
- {% else %} -
-
    - {% for ts in freshman['ts_list'] %} -
  • {{ts}}
  • - {% endfor %} -
-
- {% endif %} -
-
+ + + + + House Meetings Missed + + {% if freshman['hm_missed'] == 0 %} + None {% else %} + {{ freshman['hm_missed'] }} {% endif %} + + + + + +
+ Technical Seminars {% if freshman['ts_total'] == 0 %} +
+
- {% endif %} - {% if spring['status'] != None %} -
-
-

Membership Evaluations - {% if spring['status'] == "Passed" %} - Passed - {% elif spring['status'] == "Failed" %} - Failed - {% elif active %} - Pending - {% endif %} -

-
-
- Technical Seminars {% if ts_total == 0 %} -
- -
- {% else %} -
-
    - {% for ts in ts_list %} -
  • {{ts}}
  • - {% endfor %} -
-
- {% endif %} -
-
- - - - - - - - - - - - - - - -
Directorship Meetings - {% if spring['committee_meetings'] >= spring['req_meetings'] %} - {% else %} - {% endif %} {{ spring['committee_meetings'] }} / {{ spring['req_meetings'] }} -
House Meetings Missed - {% if spring['hm_missed'] == 0 %} - None {% else %} - {{spring['hm_missed']}} - {% endif %} - -
Major Project - - {% if spring['mp_status'] == "Passed" %} - Passed {% elif spring['mp_status'] == "Pending" %} - Pending {% else %} - None {% endif %} - -
-
+ {% else %} +
+
    + {% for ts in freshman['ts_list'] %} +
  • {{ts}}
  • + {% endfor %} +
{% endif %} - {% if conditionals_len != 0 %} -
-
-

Conditionals

-
-
-
- - - - - - - - - {% for c in conditionals %} - - - - - - - {% endfor %} - -
Date AssignedDate DueDescriptionStatus
{{c['date_created']}}{{c['date_due']}}{{c['description']}} - {% if c['status'] == "Passed" %} - {% elif c['status'] == "Pending" %} - Pending {% else %} - Failed {% endif %} -
- -
-
-
+
+
+

+{% endif %} +{% if spring['status'] != None %} +
+
+

Membership Evaluations + {% if spring['status'] == "Passed" %} + Passed + {% elif spring['status'] == "Failed" %} + Failed + {% elif active %} + Pending {% endif %} +

+
+
+ Technical Seminars {% if ts_total == 0 %} +
+ +
+ {% else %} +
+
    + {% for ts in ts_list %} +
  • {{ts}}
  • + {% endfor %} +
+
+ {% endif %} +
+
+ + + + + + + + + + + + + + + +
Directorship Meetings + {% if spring['committee_meetings'] >= spring['req_meetings'] %} + {% else %} + {% endif %} {{ spring['committee_meetings'] }} / {{ spring['req_meetings'] }} +
House Meetings Missed + {% if spring['hm_missed'] == 0 %} + None {% else %} + {{spring['hm_missed']}} + {% endif %} + +
Major Project + + {% if spring['mp_status'] == "Passed" %} + Passed {% elif spring['mp_status'] == "Pending" %} + Pending {% else %} + None {% endif %} + +
+
+
+{% endif %} +{% if conditionals_len != 0 %} +
+
+

Conditionals

+
+
+
+ + + + + + + + + {% for c in conditionals %} + + + + + + + {% endfor %} + +
Date AssignedDate DueDescriptionStatus
{{c['date_created']}}{{c['date_due']}}{{c['description']}} + {% if c['status'] == "Passed" %} + {% elif c['status'] == "Pending" %} + Pending {% else %} + Failed {% endif %} +
+ +
+
+
+{% endif %} -
-
-

Member Statistics

-
-
-
- - - - - - - {% for title in voting_count %} - - - - - {% endfor %} - -
CategoryMembers
{{ title }}{{ voting_count[title] }}
+
+
+

Member Statistics

+
+
+
+ + + + + + + {% for title in voting_count %} + + + + + {% endfor %} + +
CategoryMembers
{{ title }}{{ voting_count[title] }}
+ +
+
+
-
-
+{% if major_projects_count == 0 and not active%} + +{% elif major_projects_count > 0 %} +
+
+

Major Projects

+
+
+ {% for p in major_projects %} +
+ {% if p['status'] == "Passed" %} +
{{p['name']}}
+ {% elif p['status'] == "Pending" %} +
+
{{p['name']}}
+
- - {% if major_projects_count == 0 and not active%} - - {% elif major_projects_count > 0 %} -
-
-

Major Projects

+ {% else %} +
{{p['name']}}
+ {% endif %} +
+ TLDR + +

{{p['tldr']}}

+
+ +
+ Time Spent + +

{{p['time_spent']}}

+
+ +
+ Skills Applied + +
+ {% for s in p['skills'] %} +

{{s}}

+ {% endfor %}
-
- {% for p in major_projects %} -
- {% if p['status'] == "Passed" %} -
{{p['name']}}
- {% elif p['status'] == "Pending" %} -
- {{p['name']}} - -
- {% else %} -
{{p['name']}}
- {% endif %} -
{{p['description']}}
-
- +
+ +
- {% endif %} - - {% if accepting_dues and check_current_student(username) and not active and not bad_standing %} -
-
-

Become Active

-
-
- Hey there, you're eligible to become an active member! Click the button below if you'd like to become active and pay dues. -
- -
- {% endif %} - +
+ Description + +

{{p['desc']}}

+
+
+ {% endfor %} +
+
+{% endif %} -
- {% if housing %} -
-
-

Housing Status

-
-
+{% if accepting_dues and check_current_student(username) and not active and not bad_standing %} +
+
+

Become Active

+
+
+ Hey there, you're eligible to become an active member! Click the button below if you'd like to become active and pay dues. +
+ +
+{% endif %} - - - - - - + - {% if housing['room'] != None %} - - - - - {% endif %} - {% if housing['queue_pos'][0] != None and housing['room'] == None %} - - - - - {% endif %} - -
Housing Points{{housing['points']}}
Room Number{{housing['room']}}
Housing Queue Position - - {{housing['queue_pos'][0]}} / {{housing['queue_pos'][1]}} - -
+
+ {% if housing %} +
+
+

Housing Status

+
+
+ + + + + + + + + {% if housing['room'] != None %} + + + + {% endif %} - - - - {% if active %} -
-
-

Gatekeep - {% if gatekeep['status'] == "passing" %} - Ok! - {% elif gatekeep['status'] == "disenfranchised" and gatekeep_active%} - Disenfranchised - {% elif active %} - Pending - {% endif %} -

-
-
-
Housing Points{{housing['points']}}
Room Number{{housing['room']}}
- - {% if not gatekeep_active %} - - - - - {% endif %} - - - - - - - - - - - - - -
Status - - Gatekeep Inactive Until 6 Weeks - -
Technical Seminars - - {% if gatekeep['technical_seminars'] >= 2 %} - - {% else %} - - {% endif %} - {{ gatekeep['technical_seminars'] }} / 2 - -
Directorship Meetings - - {% if gatekeep['committee_meetings'] >= 6 %} - - {% else %} - - {% endif %} - {{ gatekeep['committee_meetings']}} / 6 - -
House Meetings Missed - {% if gatekeep['hm_missed'] == 0 %} - None - {% elif gatekeep['hm_missed'] <= 1 %} - {{ gatekeep['hm_missed'] }} Missed - {% else %} - {{gatekeep['hm_missed']}} - {% endif %} - -
-
-
- {% endif %} - - {% if hm_attendance_len == 0 and active%} -
- You haven't missed any house meetings. -
- {% elif hm_attendance_len > 0 %} -
-
-

Missed House Meetings

-
-
- - - - - - - {% for a in hm_attendance %} - - - - - {% endfor %} - -
DateReason
{{a['datetime']}}{{a['reason']}}
-
-
+ {% if housing['queue_pos'][0] != None and housing['room'] == None %} + + Housing Queue Position + + + {{housing['queue_pos'][0]}} / {{housing['queue_pos'][1]}} + + + + {% endif %} + + {% endif %} +
+
+ + {% if active %} +
+
+

Gatekeep + {% if gatekeep['status'] == "passing" %} + Ok! + {% elif gatekeep['status'] == "disenfranchised" and gatekeep_active%} + Disenfranchised + {% elif active %} + Pending + {% endif %} +

+
+
+ + + {% if not gatekeep_active %} + + + + + {% endif %} + + + + + + + + + + + + + +
Status + + Gatekeep Inactive Until 6 Weeks + +
Technical Seminars + + {% if gatekeep['technical_seminars'] >= 2 %} + + {% else %} + + {% endif %} + {{ gatekeep['technical_seminars'] }} / 2 + +
Directorship Meetings + + {% if gatekeep['committee_meetings'] >= 6 %} + + {% else %} + + {% endif %} + {{ gatekeep['committee_meetings']}} / 6 + +
House Meetings Missed + {% if gatekeep['hm_missed'] == 0 %} + None + {% elif gatekeep['hm_missed'] <= 1 %} + {{ gatekeep['hm_missed'] }} Missed + {% else %} + {{gatekeep['hm_missed']}} + {% endif %} + +
+
+
+{% endif %} - {% if cm_attendance_len == 0 and active%} -
You have not attended any directorship meetings.
- {% elif cm_attendance_len > 0 %} -
-
-

Directorship Meeting Attendance

-
+{% if hm_attendance_len == 0 and active%} +
+ You haven't missed any house meetings. +
+{% elif hm_attendance_len > 0 %} +
+
+

Missed House Meetings

+
+
+ + + + + + + {% for a in hm_attendance %} + + + + + {% endfor %} + +
DateReason
{{a['datetime']}}{{a['reason']}}
+
+
+{% endif %} -
- - - - - - - - - {% for meeting in cm_attendance %} - - - - - {% endfor %} - -
EventDate
{{meeting['committee']}}{{meeting['timestamp'].strftime('%Y-%m-%d')}}
- -
-
- {% endif %} -
+{% if cm_attendance_len == 0 and active%} +
You have not attended any directorship meetings.
+{% elif cm_attendance_len > 0 %} +
+
+

Directorship Meeting Attendance

+ +
+ + + + + + + + + {% for meeting in cm_attendance %} + + + + + {% endfor %} + +
EventDate
{{meeting['committee']}}{{meeting['timestamp'].strftime('%Y-%m-%d')}}
+ +
+
+{% endif %} +
+
{% endblock %} diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index 6c5446f2..ba9bcf95 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -1,96 +1,345 @@ {% extends "nav.html" %} + +{% block extraHeader %} + + +{% endblock %} + {% block title %} Major Project Form {% endblock %} + {% block body %} -
-

Major Project Form

+
+ +

Major Project Form

+ +
+
+

+ Welcome to the Major Project submission form! We're excited to read about what you've + been working on. For us (E-Board) to best evaluate your project, please give us as much detail as + possible. Don't feel pressured to write full paragraphs though, good bullet points are plenty! +

+ Generally, a major project is something that you make with the goal of challenging yourself, + learning new things, and doing something you would be proud of. Major projects are most likely to + pass when they meet at least 2 of the 3 + + Major Project Pillars + + - considerable time on your project, benefiting House, and meaningfully applying skills. + And of course, after you submit, please try to talk to E-Board members (in-person or over Slack) + so we are familiar with your project and can ask you questions! +

+
+
+
-
-
-
- - +
+
+ + + + + + + + +
+
+ + + +
+ + + + List what skills you meaningfully used while working on this project (at least 2!) + +
+
+ +
+ + + +
-
-
-
-
-
- - + + + + + +
+
+ + + +
+ +
+
+
+ + +
+ Upload Media +
+ + + Drag files here or click to upload. + +
+
+
- + + -

All Major Projects

{% if major_projects_len - <=0 %}
-
-

No Pending Major Projects

+ +
+ +

All Major Projects

+ + {% if major_projects_len <= 0 %} + +
+
+
+
+ No Pending Major Projects +
+
-
-{% else %} - +
+ + {% else %} + {% for p in major_projects %} -
-
-
-

{{p['proj_name']}}

- - {{p['name']}} ({{p['username']}}) + +
+
+ +
+ +
+ +
+

+ {{p['proj_name']}} +

+ + User profile picture of the submitter + + + {{p['name']}} ({{p['username']}}) + +
+ + +
+ {% if is_eval_director %} + + + {% else %} +
+ + {% if p['status']=='Passed' %} +
+ {% elif p['status']=='Failed' %} +
+ {% else %} +
+ {% endif %} +
+ {% endif %} +
-
- - {% if is_eval_director %} - -
- - + + +
+
+
- {% else %} - {% if p['status'] == 'Passed' %} -
- {% elif p['status'] == 'Failed' %} -
- {% else %} -
- {% endif %} - {% if p.is_owner and p['status'] == 'Pending' %} - - {% endif %} - {% endif %}
- -
- {{p['description']}} + +
+ + +
+
+
+
+
TLDR
+

{{p['tldr']}}

+
+ +
+
Time Commitment
+

{{p['time_spent']}}

+
+
+ +
+
+
Skills
+
+ {% for s in p['skills'] %} + + {{s}} + + {% endfor %} +
+
+ +
+
Links
+ {% for l in p['links'] %} + {% set href = l %} + {% if not href.startswith('http://') and not href.startswith('https://') %} + {% set href = 'https://' + href %} + {% endif %} +

{{ href }}

+ {% endfor %} +
+
+ +
+
+
Description
+

{{p['desc']}}

+
+
+ +
+
Images
+ +
+ {% if p['files']|length == 0 %} +

No images uploaded

+ {% else %} + {% for f in p['files'] %} + User submitted image for the project + {% endfor %} + {% endif %} +
+ +
- {% endfor %} +
+{% endfor %} {% endif %} -
-{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/conditional/util/major_project.py b/conditional/util/major_project.py new file mode 100644 index 00000000..7aa53221 --- /dev/null +++ b/conditional/util/major_project.py @@ -0,0 +1,26 @@ +from sqlalchemy import desc, func + +from conditional import db, start_of_year +from conditional.models.models import MajorProject +from conditional.models.models import MajorProjectSkill + + +def get_project_list(): + proj_list = db.session.query( + MajorProject.id, + MajorProject.date, + MajorProject.uid, + MajorProject.name, + MajorProject.tldr, + MajorProject.time_spent, + MajorProject.description, + MajorProject.links, + MajorProject.status, + func.array_agg(MajorProjectSkill.skill).label("skills") + ).outerjoin(MajorProjectSkill, + MajorProject.id == MajorProjectSkill.project_id + ).group_by(MajorProject.id + ).where(MajorProject.date >= start_of_year() + ).order_by(desc(MajorProject.date), desc(MajorProject.id)) + + return proj_list diff --git a/conditional/util/s3.py b/conditional/util/s3.py new file mode 100644 index 00000000..7ce3a999 --- /dev/null +++ b/conditional/util/s3.py @@ -0,0 +1,24 @@ +import boto3 +import botocore +from conditional import app + + +def list_files_in_folder(bucket_name, folder_prefix): + + s3 = boto3.client( + service_name="s3", + aws_access_key_id=app.config['AWS_ACCESS_KEY_ID'], + aws_secret_access_key=app.config['AWS_SECRET_ACCESS_KEY'], + endpoint_url=app.config['S3_URI'] + ) + + try: + response = s3.list_objects(Bucket=bucket_name, Prefix=folder_prefix) + if 'Contents' in response: + return [obj['Key'] for obj in response['Contents']] + + return [] + + except botocore.exceptions.ClientError as e: + print(f"Error listing files in the folder: {e}") + return [] diff --git a/config.env.py b/config.env.py index 554f8e08..d1a39bb5 100644 --- a/config.env.py +++ b/config.env.py @@ -26,6 +26,13 @@ LDAP_BIND_DN = env.get("CONDITIONAL_LDAP_BIND_DN", "cn=conditional,ou=Apps,dc=csh,dc=rit,dc=edu") LDAP_BIND_PW = env.get("CONDITIONAL_LDAP_BIND_PW", "") +# S3 information +S3_URI = env.get("S3_URI", "https://s3.csh.rit.edu") # URL for where the s3 bucket is hosted +S3_BUCKET_ID = env.get("S3_BUCKET_ID", "major-project-media") # name of the bucket +AWS_ACCESS_KEY_ID = env.get("AWS_ACCESS_KEY_ID", "") +AWS_SECRET_ACCESS_KEY = env.get("AWS_SECRET_ACCESS_KEY", "") + + # Sentry config # Not required for local development, but if you set it, make sure the # SENTRY_ENV is 'local-development' diff --git a/frontend/images/photo_video.svg b/frontend/images/photo_video.svg new file mode 100644 index 00000000..2324ec73 --- /dev/null +++ b/frontend/images/photo_video.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/javascript/modules/majorProjectForm.js b/frontend/javascript/modules/majorProjectForm.js index fc9969c1..a19da72d 100644 --- a/frontend/javascript/modules/majorProjectForm.js +++ b/frontend/javascript/modules/majorProjectForm.js @@ -1,30 +1,77 @@ import FetchUtil from "../utils/fetchUtil"; export default class MajorProjectForm { - constructor(form) { - this.form = form; - this.endpoint = '/major_project/submit'; - this.render(); - } - - render() { - this.form.querySelector('input[type=submit]') - .addEventListener('click', e => this._submitForm(e)); - } - - _submitForm(e) { - e.preventDefault(); - - let payload = { - projectName: this.form.querySelector('input[name=name]').value, - projectDescription: - this.form.querySelector('textarea[name=description]').value - }; - - FetchUtil.postWithWarning(this.endpoint, payload, { - warningText: "You will not be able to edit your " + - "project once it has been submitted.", - successText: "Your project has been submitted." - }); - } -} + + + constructor(form) { + this.form = form; + this.endpoint = '/major_project/submit'; + this.tags_written = false; + this.tag_keys = ["Enter", "Comma", "Tab"]; + this.render(); + } + + render() { + this.form.querySelector('input[type=submit]') + .addEventListener('click', e => this._submitForm(e)); + this.form.querySelector('input[id=skill-input]') + .addEventListener('focusout', e => this.onWriteSkill(e)); + this.form.querySelector('input[id=skill-input]') + .addEventListener('keypress', e => this.onKeyPress(e)); + } + + onKeyPress(e) { + if (this.tag_keys.includes(e.code)) { + e.preventDefault(); + this.onWriteSkill(e); + } + return false; + } + + onWriteSkill(e) { + let input = document.getElementById("skill-input") + if (!this.tags_written) { + this.tags_written = true + + const firstTag = document.getElementsByClassName("skill-tag").item(0); + if (firstTag) firstTag.remove(); + } + + let txt = input.value.replaceAll(/[^a-zA-Z0-9\+\-\.\# ]/g, ''); // allowed characters list + if (txt) input.insertAdjacentHTML("beforebegin", '' + txt + ''); + let skills = this.form.getElementsByClassName("skill-tag") + skills.item(skills.length - 1).addEventListener('click', e => this.onRemoveTag(e)); + input.value = ""; + } + + onRemoveTag(e) { + e.target.remove(); + } + + _submitForm(e) { + e.preventDefault(); + + let skills = []; + + for (const tag of this.form.getElementsByClassName('skill-tag')) { + skills.push(tag.textContent); + } + + let payload = { + projectName: this.form.querySelector('input[name=name]').value, + projectTldr: this.form.querySelector('input[name=tldr]').value, + projectTimeSpent: this.form.querySelector('textarea[name=time-commitment]').value, + projectSkills: skills, + projectDescription: this.form.querySelector('textarea[name=description]').value, + projectLinks: this.form.querySelector('textarea[name=links]').value + }; + + console.log(payload) + + FetchUtil.postWithWarning(this.endpoint, payload, { + warningText: "You will not be able to edit your " + + "project once it has been submitted.", + successText: "Your project has been submitted." + }); + } +} \ No newline at end of file diff --git a/frontend/javascript/modules/majorProjectStatus.js b/frontend/javascript/modules/majorProjectStatus.js index 97a68551..7ca87ad3 100644 --- a/frontend/javascript/modules/majorProjectStatus.js +++ b/frontend/javascript/modules/majorProjectStatus.js @@ -44,7 +44,7 @@ export default class MajorProjectStatus { $(dashboardContainer).hide(); } else { // Major projects page button - $(this.control.closest(".panel")).fadeOut(); + $(this.control.closest(".card")).fadeOut(); } }); } else { diff --git a/frontend/stylesheets/pages/_dashboard.scss b/frontend/stylesheets/pages/_dashboard.scss index debdba53..8cc1bc29 100644 --- a/frontend/stylesheets/pages/_dashboard.scss +++ b/frontend/stylesheets/pages/_dashboard.scss @@ -41,3 +41,11 @@ margin: 5px 0; } } + +.section-header { + font-size: 1.25em; +} + +div.col { + margin-bottom: 1em; +} \ No newline at end of file diff --git a/frontend/stylesheets/pages/_major-project.scss b/frontend/stylesheets/pages/_major-project.scss index 01438510..d9e70f24 100644 --- a/frontend/stylesheets/pages/_major-project.scss +++ b/frontend/stylesheets/pages/_major-project.scss @@ -1,3 +1,135 @@ -.major-project-desc { - white-space: pre-line; +.bg-white { + background-color: #fff; } + +.px-extra { + padding-left: 1em; + padding-right: 1em; +} + +.mp-form-intro { + background-color: #fff; + box-shadow: 1px 1px 2px grey; + padding: 1em; + padding-bottom: 0.25em; + margin-bottom: 2em; +} + +.mp-form { + background-color: #fff; + box-shadow: 1px 1px 2px grey; + margin-bottom: 2em; + padding: 1em; +} + +.form-control { + margin-bottom: 1em; +} +.form-label { + margin-top: 1.25rem; + font-size: 2rem; +} + +.form-label span { + font-size: 1rem; +} + +.form-textarea { + resize: vertical; +} + +.form-skilltags { + box-shadow: inset 0 -1px 0 #ddd; + border: none; + border-radius: 0; + padding: 0 0 10px; + height: fit-content; +} + +.mb-extra { + margin-bottom: 2em; +} + +.proj-listing { + background-color: #fff; + box-shadow: 1px 1px 2px grey; +} + +.rounded-circle { + border-radius: 50%; +} + +.placeholder { + color: #bbb; + font-size: 16px; +} + +.skill-tag { + display: block; + float: left; + background: #b0197e; + padding: 4px 30px 4px 8px; + margin: 2px 3px; + color: #fff; + border-radius: 5px; + transition: .5s all; +} + +.skill-tag:after { + position: absolute; + content: "×"; + border: 1px solid; + border-radius: 10px; + padding: 0 4px; + margin: 3px 0 10px 7px; + font-size: 10px; +} + +.skill-display { + display: block; + float: left; + background: #b0197e; + padding: 4px 8px 4px 8px; + margin: 2px 3px; + color: #fff; + font-weight: 600; + border-radius: 5px; + transition: .5s all; +} + +.skill-tag:hover { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); +} + +.skill-group { + display: flex; + flex-wrap: wrap; +} + +.line-short { + line-height: 0.5em; +} + +.line-med { + line-height: 1.5em; +} + +.proj-img { + max-width: 30%; + margin-left: 0.5em; + margin-right: 0.5em; +} + +.img-container { + display: flex; + flex-wrap: wrap; + margin-bottom: 1em; +} + +.img-header { + margin-left: 1em; +} + +.no-imgs { + margin-left: 0.2em; +} \ No newline at end of file diff --git a/migrations/versions/6ae578b76143_.py b/migrations/versions/6ae578b76143_add_date_to_.mp_submission.py similarity index 100% rename from migrations/versions/6ae578b76143_.py rename to migrations/versions/6ae578b76143_add_date_to_.mp_submission.py diff --git a/migrations/versions/6c4cf35d7c0c_update_mp_table.py b/migrations/versions/6c4cf35d7c0c_update_mp_table.py new file mode 100644 index 00000000..93a45f86 --- /dev/null +++ b/migrations/versions/6c4cf35d7c0c_update_mp_table.py @@ -0,0 +1,43 @@ +"""Add new Major Project data for improved MP form + +Revision ID: 6c4cf35d7c0c +Revises: f1d08673b870 +Create Date: 2026-03-06 15:42:50.323042 + +""" + +# revision identifiers, used by Alembic. +revision = '6c4cf35d7c0c' +down_revision = 'f1d08673b870' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('major_project_skills', + sa.Column('project_id', sa.Integer(), nullable=False), + sa.Column('skill', sa.Text(), nullable=False), + sa.ForeignKeyConstraint(['project_id'], ['major_projects.id'], ondelete='cascade'), + sa.PrimaryKeyConstraint('project_id', 'skill') + ) + op.add_column('major_projects', sa.Column('tldr', sa.String(length=128), nullable=True)) + op.add_column('major_projects', sa.Column('timeSpent', sa.Text(), nullable=True)) + op.add_column('major_projects', sa.Column('links', sa.Text(), nullable=True)) + op.alter_column('major_projects', 'description', + existing_type=sa.TEXT(), + nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('major_projects', 'description', + existing_type=sa.TEXT(), + nullable=True) + op.drop_column('major_projects', 'links') + op.drop_column('major_projects', 'timeSpent') + op.drop_column('major_projects', 'tldr') + op.drop_table('major_project_skills') + # ### end Alembic commands ### diff --git a/migrations/versions/95538b39976f_mp_time_spent.py b/migrations/versions/95538b39976f_mp_time_spent.py new file mode 100644 index 00000000..a2dbf0bb --- /dev/null +++ b/migrations/versions/95538b39976f_mp_time_spent.py @@ -0,0 +1,28 @@ +"""Add time spent to mp table + +Revision ID: 95538b39976f +Revises: 6c4cf35d7c0c +Create Date: 2026-03-09 15:20:23.072283 + +""" + +# revision identifiers, used by Alembic. +revision = '95538b39976f' +down_revision = '6c4cf35d7c0c' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('major_projects', sa.Column('time_spent', sa.Text(), nullable=True)) + op.drop_column('major_projects', 'timeSpent') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('major_projects', sa.Column('timeSpent', sa.TEXT(), autoincrement=False, nullable=True)) + op.drop_column('major_projects', 'time_spent') + # ### end Alembic commands ### diff --git a/package-lock.json b/package-lock.json index 0f69d912..6084610f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "sass": "^1.93.3", "sass-loader": "^16.0.6", "style-loader": "^4.0.0", - "webpack": "^5.102.1", + "webpack": "^5.105.3", "webpack-cli": "^6.0.1" }, "engines": { @@ -2563,13 +2563,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", - "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@webassemblyjs/ast": { @@ -2795,9 +2795,9 @@ "license": "Apache-2.0" }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -2981,13 +2981,16 @@ "peer": true }, "node_modules/baseline-browser-mapping": { - "version": "2.8.23", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.23.tgz", - "integrity": "sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/bootstrap": { @@ -3077,9 +3080,9 @@ } }, "node_modules/browserslist": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", - "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -3097,11 +3100,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", - "update-browserslist-db": "^1.1.4" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -3114,7 +3117,8 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/callsites": { "version": "3.1.0", @@ -3128,9 +3132,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001753", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", - "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", + "version": "1.0.30001775", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz", + "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==", "dev": true, "funding": [ { @@ -3206,7 +3210,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", @@ -3382,21 +3387,21 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.244", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz", - "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==", + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", "dev": true, "license": "ISC" }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -3422,9 +3427,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -3600,7 +3605,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -4212,22 +4216,6 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -5114,6 +5102,16 @@ "resolved": "https://registry.npmjs.org/simple-masonry/-/simple-masonry-1.0.5.tgz", "integrity": "sha1-tU6BpzIniokgnbXuPAkYFzIpHmU=" }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5124,6 +5122,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -5165,6 +5174,22 @@ "webpack": "^5.27.0" } }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -5193,9 +5218,9 @@ } }, "node_modules/terser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", - "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5245,27 +5270,6 @@ } } }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -5329,9 +5333,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, @@ -5380,9 +5384,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -5429,9 +5433,9 @@ "license": "MIT" }, "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", "dependencies": { @@ -5443,9 +5447,9 @@ } }, "node_modules/webpack": { - "version": "5.102.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", - "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", + "version": "5.105.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.3.tgz", + "integrity": "sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==", "dev": true, "license": "MIT", "dependencies": { @@ -5455,25 +5459,25 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.26.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.3", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -5560,9 +5564,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index b39cdc23..7c07ceef 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "sass": "^1.93.3", "sass-loader": "^16.0.6", "style-loader": "^4.0.0", - "webpack": "^5.102.1", + "webpack": "^5.105.3", "webpack-cli": "^6.0.1" } } diff --git a/requirements.in b/requirements.in index c1753f85..0ad7c5f0 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,9 @@ alembic~=1.15.1 astroid~=3.3.9 blinker~=1.4 +boto3==1.35.13 +botocore==1.35.13 +click~=8.1.8 csh_ldap>=2.5.3 ddtrace~=4.4.0 Flask~=3.1.0 diff --git a/requirements.txt b/requirements.txt index 9a54f1ef..b3e1fe19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,9 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile requirements.in +# +# This file is autogenerated by pip-compile with Python 3.14 +# by the following command: +# +# pip-compile --cert=None --client-cert=None --index-url=None --pip-args=None requirements.in +# alembic==1.15.2 # via # -r requirements.in @@ -15,6 +19,13 @@ blinker==1.9.0 # -r requirements.in # flask # sentry-sdk +boto3==1.35.13 + # via -r requirements.in +botocore==1.35.13 + # via + # -r requirements.in + # boto3 + # s3transfer build==1.4.0 # via pip-tools bytecode==0.17.0 @@ -88,7 +99,11 @@ jinja2==3.1.6 # via # -r requirements.in # flask -lazy-object-proxy==1.12.0 +jmespath==1.1.0 + # via + # boto3 + # botocore +lazy-object-proxy==1.4.3 # via -r requirements.in mako==1.3.10 # via @@ -153,6 +168,13 @@ pyproject-hooks==1.2.0 # build # pip-tools python-dotenv==1.2.2 +python-dateutil==2.6.1 + # via + # -r requirements.in + # botocore + # via + # -r requirements.in + # botocore # via pydantic-settings python-editor==1.0.4 # via -r requirements.in @@ -163,7 +185,7 @@ requests==2.32.5 # flask-pyoidc # oic # pyjwkest -sentry-sdk==2.24.1 +sentry-sdk[flask]==2.24.1 # via -r requirements.in setuptools==82.0.0 # via pip-tools @@ -196,6 +218,7 @@ typing-inspection==0.4.2 # pydantic-settings urllib3==2.6.3 # via + # botocore # requests # sentry-sdk werkzeug==3.1.6 @@ -210,3 +233,7 @@ wrapt==1.17.3 # ddtrace zipp==3.23.0 # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools From cb6246e9f572e364a5104eb1003ae662601571ad Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sat, 7 Mar 2026 18:10:18 -0500 Subject: [PATCH 16/16] Fix only evals being checked for cm attendance --- conditional/blueprints/attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conditional/blueprints/attendance.py b/conditional/blueprints/attendance.py index ebc05415..387b5b31 100644 --- a/conditional/blueprints/attendance.py +++ b/conditional/blueprints/attendance.py @@ -175,7 +175,7 @@ def display_attendance_hm(user_dict=None): def submit_committee_attendance(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) - approved = user_dict_is_eval_director(user_dict) + approved = user_dict_is_eboard(user_dict) post_data = request.get_json() committee = post_data['committee']