From 39efe970d6c0c2fdb5fc2e06f0c65df21df3dd62 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Sun, 1 Mar 2026 10:48:58 -0500 Subject: [PATCH 01/26] Fixed migrations fr this time --- conditional/models/models.py | 22 +++++++++++--- migrations/versions/09603d1cfc38_.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 migrations/versions/09603d1cfc38_.py diff --git a/conditional/models/models.py b/conditional/models/models.py index 2683a599..0e675a5e 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) + timeSpent = 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): self.uid = uid self.date = datetime.now() self.name = name - self.description = desc + self.tldr = tldr + self.timeSpent = 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/migrations/versions/09603d1cfc38_.py b/migrations/versions/09603d1cfc38_.py new file mode 100644 index 00000000..38280cb8 --- /dev/null +++ b/migrations/versions/09603d1cfc38_.py @@ -0,0 +1,43 @@ +"""add new fields to major project form + +Revision ID: 09603d1cfc38 +Revises: f1d08673b870 +Create Date: 2026-03-01 10:46:33.652106 + +""" + +# revision identifiers, used by Alembic. +revision = '09603d1cfc38' +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 ### From d4a719b329041c646816760f080cedcec4582cb7 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Sun, 1 Mar 2026 11:10:01 -0500 Subject: [PATCH 02/26] Restored all my existing work --- .../blueprints/major_project_submission.py | 163 +++++++++++++++--- .../templates/major_project_submission.html | 125 +++++++++++--- config.env.py | 4 + .../javascript/modules/majorProjectForm.js | 95 +++++++--- .../stylesheets/pages/_major-project.scss | 52 ++++++ requirements.in | 3 + requirements.txt | 45 ++++- 7 files changed, 415 insertions(+), 72 deletions(-) diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index aa3ba66c..49087ac5 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -1,18 +1,24 @@ import json +import os import requests +import requests +import boto3 + +from config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY from flask import Blueprint from flask import request from flask import jsonify from flask import redirect -from sqlalchemy import desc +from sqlalchemy import func, desc import structlog +from werkzeug.utils import secure_filename from conditional.util.context_processors import get_member_name -from conditional.models.models import MajorProject +from conditional.models.models import MajorProject, MajorProjectSkill from conditional.util.ldap import ldap_is_eval_director from conditional.util.ldap import ldap_get_member @@ -32,19 +38,39 @@ def display_major_project(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info("Display Major Project Page") + # There is probably a better way to do this, but it does work + + proj_list = db.session.query( + MajorProject.id, + MajorProject.date, + MajorProject.uid, + MajorProject.name, + MajorProject.tldr, + MajorProject.timeSpent, + MajorProject.description, + 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)) + major_projects = [ { + "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.timeSpent, + "skills": p.skills, + "desc": p.description, "status": p.status, - "description": p.description, - "id": p.id, - "is_owner": bool(user_dict["username"] == p.uid), + "is_owner": bool(user_dict["username"] == p.uid) } - 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) @@ -53,8 +79,32 @@ def display_major_project(user_dict=None): "major_project_submission.html", major_projects=major_projects, major_projects_len=major_projects_len, - username=user_dict["username"], - ) + 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)') + + log.info(f"user_dict: {user_dict}") + + 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 = 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 +115,88 @@ def submit_major_project(user_dict=None): log.info("Submit Major Project") post_data = request.get_json() + + print(f"Post Data: {post_data}") # TODO: Remove this later + name = post_data["projectName"] + tldr = post_data['projectTldr'] + time_spent = post_data['projectTimeSpent'] + skills = post_data['projectSkills'] description = post_data["projectDescription"] - if name == "" or description == "": + user_id = user_dict['username'] + + log.info(user_id) + + # All fields are required in order to be able to submit the form + # TODO: Do we want any of the fields to have enforced min or max lengths? + if name == "" or tldr == "" or time_spent == "" or skills == "" or description == "": return jsonify({"success": False}), 400 - project = MajorProject(user_dict["username"], name, description) + + # TODO: Ensure all the information is being passed to the object + project = MajorProject(user_id, name, tldr, time_spent, description) - # Don't you dare try pinging @channel + # Save the info to the database + db.session.add(project) + db.session.commit() + + + project = MajorProject.query.filter( + MajorProject.name == name and MajorProject.uid == user_id + ).first() + + skills_list = filter(lambda x: x != 'None', skills) + print(f"Skills: {list(skills_list)}") + + for skill in skills_list: + skill = skill.strip() + + if skill != "" and skill != '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!" - } + # Connect to S3 bucket + s3 = boto3.resource( + "s3", + endpoint_url="https://s3.csh.rit.edu", + aws_access_key_id=AWS_ACCESS_KEY_ID, + aws_secret_access_key=AWS_SECRET_ACCESS_KEY ) - db.session.add(project) - db.session.commit() + + bucket = s3.create_bucket(Bucket="major-project-media") + + # Collect all the locally cached files and put them in the bucket + for file in os.listdir(f"/tmp/{user_id}"): + filepath = f"/tmp/{user_id}/{file}" + + # TODO: Remove this later + print(f"Filepath in S3: {filepath}") + + bucket.upload_file(filepath, f"{project.id}--{file}") + os.remove(filepath) + + # Delete the temp directory once all the files have been stored in S3 + os.rmdir(f"/tmp/{user_id}") + + + # Send the slack ping only after we know that the data was properly saved to the DB + # TODO: Maybe add more info to the slack ping? + # send_slack_ping( + # { + # "text": f" *{get_member_name(user_id)}* ({user_id})" + # f" submitted their major project, *{name}*!" + # } + # ) + return jsonify({"success": True}), 200 @@ -106,8 +217,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 @@ -123,12 +236,14 @@ def major_project_delete(pid, user_dict=None): if creator == user_dict["username"] or ldap_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 def send_slack_ping(payload): - requests.post(app.config["WEBHOOK_URL"], json.dumps(payload), timeout=120) + requests.post(app.config["WEBHOOK_URL"], json.dumps(payload), timeout=120) \ No newline at end of file diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index 6c5446f2..ca0bbcfe 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -1,45 +1,110 @@ {% extends "nav.html" %} +{% block extraHeader %} + +{% endblock %} {% block title %} -Major Project Form + Major Project Form {% endblock %} {% block body %}

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 %}
+ <=0 %} +

No Pending Major Projects

-
-{% else %} +
+ {% else %} {% for p in major_projects %}
-

{{p['proj_name']}}

+ +

{{p['proj_name']}}

{{p['name']}} ({{p['username']}})
@@ -84,7 +149,27 @@
- {{p['description']}} + +
+
+ TLDR: {{p['tldr']}} +
+
+ +
+
Description
+

{{p['desc']}}

+
+ +
+

Skills

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

{{s}}

+ {% endfor %} + + +

chom

+
@@ -93,4 +178,4 @@
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 + document.getElementsByClassName("placeholder").item(0).remove() + } + let txt = input.value.replace(/[^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 = []; + + Array.from(this.form.getElementsByClassName('skill-tag')).forEach(tag => skills.push(tag.firstChild.data)) + + 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 + }; + + 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/stylesheets/pages/_major-project.scss b/frontend/stylesheets/pages/_major-project.scss index 01438510..ca0667ce 100644 --- a/frontend/stylesheets/pages/_major-project.scss +++ b/frontend/stylesheets/pages/_major-project.scss @@ -1,3 +1,55 @@ .major-project-desc { white-space: pre-line; } + +.form-label { + margin-bottom: 0; + 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; +} + +.placeholder { + color: #bbb; + font-size: 16px; +} + +.skill-tag { + display: block; + float: left; + background: #d979e3; + padding: 4px 30px 4px 8px; + margin: 2px 3px; + color: #444; + 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-tag:hover { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); +} \ No newline at end of file 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..e25da049 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,14 @@ +<<<<<<< HEAD # 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 +# +>>>>>>> ed99cfd (Restored all my existing work) alembic==1.15.2 # via # -r requirements.in @@ -15,6 +24,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 @@ -27,7 +43,7 @@ cffi==2.0.0 # via cryptography charset-normalizer==3.4.4 # via requests -click==8.3.1 +click==8.1.8 # via # -r requirements.in # flask @@ -88,7 +104,15 @@ jinja2==3.1.6 # via # -r requirements.in # flask +<<<<<<< HEAD lazy-object-proxy==1.12.0 +======= +jmespath==1.1.0 + # via + # boto3 + # botocore +lazy-object-proxy==1.4.3 +>>>>>>> ed99cfd (Restored all my existing work) # via -r requirements.in mako==1.3.10 # via @@ -152,7 +176,15 @@ pyproject-hooks==1.2.0 # via # build # pip-tools +<<<<<<< HEAD python-dotenv==1.2.2 +======= +python-dateutil==2.6.1 + # via + # -r requirements.in + # botocore +python-dotenv==1.2.1 +>>>>>>> ed99cfd (Restored all my existing work) # via pydantic-settings python-editor==1.0.4 # via -r requirements.in @@ -163,7 +195,13 @@ requests==2.32.5 # flask-pyoidc # oic # pyjwkest +<<<<<<< HEAD sentry-sdk==2.24.1 +======= +s3transfer==0.10.4 + # via boto3 +sentry-sdk[flask]==2.24.1 +>>>>>>> ed99cfd (Restored all my existing work) # via -r requirements.in setuptools==82.0.0 # via pip-tools @@ -196,6 +234,7 @@ typing-inspection==0.4.2 # pydantic-settings urllib3==2.6.3 # via + # botocore # requests # sentry-sdk werkzeug==3.1.6 @@ -210,3 +249,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 821ba2b43610a4c1bffd1d4b8a7e82827fb5954c Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 2 Mar 2026 08:53:09 -0500 Subject: [PATCH 03/26] Made form look decent --- .../blueprints/major_project_submission.py | 5 +- .../templates/major_project_submission.html | 396 ++++++++++++------ frontend/images/photo_video.svg | 6 + .../javascript/modules/majorProjectForm.js | 3 +- .../stylesheets/pages/_major-project.scss | 23 +- migrations/versions/217b5842448c_.py | 26 ++ migrations/versions/72444bd2437e_.py | 41 ++ migrations/versions/8182939744c8_.py | 39 ++ migrations/versions/82cdb94fc915_.py | 26 ++ migrations/versions/e2673b9e9ea8_.py | 34 ++ package-lock.json | 279 +++++++----- package.json | 4 +- requirements.txt | 5 - 13 files changed, 651 insertions(+), 236 deletions(-) create mode 100644 frontend/images/photo_video.svg create mode 100644 migrations/versions/217b5842448c_.py create mode 100644 migrations/versions/72444bd2437e_.py create mode 100644 migrations/versions/8182939744c8_.py create mode 100644 migrations/versions/82cdb94fc915_.py create mode 100644 migrations/versions/e2673b9e9ea8_.py diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index 49087ac5..68e1f352 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -48,6 +48,7 @@ def display_major_project(user_dict=None): MajorProject.tldr, MajorProject.timeSpent, MajorProject.description, + MajorProject.links, MajorProject.status, func.array_agg(MajorProjectSkill.skill).label("skills") ).outerjoin(MajorProjectSkill, @@ -67,6 +68,7 @@ def display_major_project(user_dict=None): "time_spent": p.timeSpent, "skills": p.skills, "desc": p.description, + "links": p.links, "status": p.status, "is_owner": bool(user_dict["username"] == p.uid) } @@ -123,6 +125,7 @@ def submit_major_project(user_dict=None): time_spent = post_data['projectTimeSpent'] skills = post_data['projectSkills'] description = post_data["projectDescription"] + links = post_data['projectLinks'] user_id = user_dict['username'] @@ -134,7 +137,7 @@ def submit_major_project(user_dict=None): return jsonify({"success": False}), 400 # TODO: Ensure all the information is being passed to the object - project = MajorProject(user_id, name, tldr, time_spent, description) + project = MajorProject(user_id, name, tldr, time_spent, description, links) # Save the info to the database db.session.add(project) diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index ca0bbcfe..43574c0b 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -1,181 +1,325 @@ {% extends "nav.html" %} + {% block extraHeader %} - + {% endblock %} + {% block title %} - Major Project Form +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, +

+ 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! + + 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!) + + + +
+ + + List what skills you meaningfully used while working on this project (at least 2!) +
+
- - + + +
- - -
+ + + + +
- - + + +
-
-
-
- -
Upload Media
-
Drag files here or click to upload.
+ +
+
+ + +
+ 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 %} {% for p in major_projects %} -
-
-
- -

{{p['proj_name']}}

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

{{p['proj_name']}}

+ + + + {{p['name']}} ({{p['username']}}) +
- {% else %} - {% if p['status'] == 'Passed' %} -
- {% elif p['status'] == 'Failed' %} -
- {% else %} -
- {% endif %} - {% if p.is_owner and p['status'] == 'Pending' %} - - {% endif %} - {% endif %} + +
+ + {% if is_eval_director %} + +
+ + + + + +
+ + {% else %} + + + {{p['status']}} + + + {% endif %} + +
+
- -
- -
-
- TLDR: {{p['tldr']}} -
-
- -
-
Description
-

{{p['desc']}}

-
- -
-

Skills

+ + + +
+ +

+ TLDR: {{p['tldr']}} +

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

{{s}}

+ + {{s}} + {% endfor %} - -

chom

-
+
+ +

+ Time Commitment: + {{p['time_spent']}} +

+ +

+ Links: + {{p['links']}} +

+ +

+ Description:
+ {{p['desc']}} +

+
+
+
- {% endfor %} -{% endif %} + {% endfor %} + {% endif %}
+ {% endblock %} \ No newline at end of file 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 d2944adf..7412213b 100644 --- a/frontend/javascript/modules/majorProjectForm.js +++ b/frontend/javascript/modules/majorProjectForm.js @@ -57,7 +57,8 @@ export default class MajorProjectForm { 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 + projectDescription: this.form.querySelector('textarea[name=description]').value, + projectLinks: this.form.querySelector('textarea[name=links]').value }; console.log(payload) diff --git a/frontend/stylesheets/pages/_major-project.scss b/frontend/stylesheets/pages/_major-project.scss index ca0667ce..4d903666 100644 --- a/frontend/stylesheets/pages/_major-project.scss +++ b/frontend/stylesheets/pages/_major-project.scss @@ -1,9 +1,22 @@ -.major-project-desc { - white-space: pre-line; +.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-bottom: 0; margin-top: 1.25rem; font-size: 2rem; } @@ -24,6 +37,10 @@ height: fit-content; } +.mb-extra { + margin-bottom: 2em; +} + .placeholder { color: #bbb; font-size: 16px; diff --git a/migrations/versions/217b5842448c_.py b/migrations/versions/217b5842448c_.py new file mode 100644 index 00000000..40c7a8b0 --- /dev/null +++ b/migrations/versions/217b5842448c_.py @@ -0,0 +1,26 @@ +"""add time spent to major projects + +Revision ID: 217b5842448c +Revises: e2673b9e9ea8 +Create Date: 2026-02-21 16:45:10.780115 + +""" + +# revision identifiers, used by Alembic. +revision = '217b5842448c' +down_revision = 'e2673b9e9ea8' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('major_projects', sa.Column('timeSpent', sa.Text(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('major_projects', 'timeSpent') + # ### end Alembic commands ### diff --git a/migrations/versions/72444bd2437e_.py b/migrations/versions/72444bd2437e_.py new file mode 100644 index 00000000..5bed108b --- /dev/null +++ b/migrations/versions/72444bd2437e_.py @@ -0,0 +1,41 @@ +"""empty message + +Revision ID: 72444bd2437e +Revises: 217b5842448c +Create Date: 2026-02-28 17:15:54.028901 + +""" + +# revision identifiers, used by Alembic. +revision = '72444bd2437e' +down_revision = '217b5842448c' + +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.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', 'timeSpent') + op.drop_column('major_projects', 'tldr') + op.drop_table('major_project_skills') + # ### end Alembic commands ### diff --git a/migrations/versions/8182939744c8_.py b/migrations/versions/8182939744c8_.py new file mode 100644 index 00000000..00eeec1d --- /dev/null +++ b/migrations/versions/8182939744c8_.py @@ -0,0 +1,39 @@ +"""empty message + +Revision ID: 8182939744c8 +Revises: f1d08673b870 +Create Date: 2026-03-01 10:04:50.175588 + +""" + +# revision identifiers, used by Alembic. +revision = '8182939744c8' +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.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', 'tldr') + op.drop_table('major_project_skills') + # ### end Alembic commands ### diff --git a/migrations/versions/82cdb94fc915_.py b/migrations/versions/82cdb94fc915_.py new file mode 100644 index 00000000..88de2ac8 --- /dev/null +++ b/migrations/versions/82cdb94fc915_.py @@ -0,0 +1,26 @@ +"""empty message + +Revision ID: 82cdb94fc915 +Revises: f1d08673b870 +Create Date: 2026-03-01 10:19:20.002845 + +""" + +# revision identifiers, used by Alembic. +revision = '82cdb94fc915' +down_revision = 'f1d08673b870' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('major_projects', sa.Column('links', sa.Text(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('major_projects', 'links') + # ### end Alembic commands ### diff --git a/migrations/versions/e2673b9e9ea8_.py b/migrations/versions/e2673b9e9ea8_.py new file mode 100644 index 00000000..9c7b3755 --- /dev/null +++ b/migrations/versions/e2673b9e9ea8_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: e2673b9e9ea8 +Revises: 7a3904cac24b +Create Date: 2026-02-21 13:10:18.957944 + +""" + +# revision identifiers, used by Alembic. +revision = 'e2673b9e9ea8' +down_revision = '7a3904cac24b' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('project_id_fk', 'major_project_skills', type_='foreignkey') + op.create_foreign_key(None, 'major_project_skills', 'major_projects', ['project_id'], ['id'], ondelete='cascade') + op.alter_column('major_projects', 'tldr', + existing_type=sa.VARCHAR(length=128), + nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('major_projects', 'tldr', + existing_type=sa.VARCHAR(length=128), + nullable=False) + op.drop_constraint(None, 'major_project_skills', type_='foreignkey') + op.create_foreign_key('project_id_fk', 'major_project_skills', 'major_projects', ['project_id'], ['id']) + # ### end Alembic commands ### diff --git a/package-lock.json b/package-lock.json index 0f69d912..636df461 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": { @@ -1774,6 +1774,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1785,6 +1786,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -1801,6 +1803,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1820,6 +1823,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", @@ -1828,6 +1833,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -1842,6 +1848,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -1856,6 +1863,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1881,6 +1889,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1900,6 +1909,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/@eslint/js": { "version": "9.39.1", @@ -1908,6 +1919,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1922,6 +1934,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1933,6 +1946,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -1948,6 +1962,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "engines": { "node": ">=18.18.0" } @@ -1959,6 +1974,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -1974,6 +1990,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "engines": { "node": ">=12.22" }, @@ -1989,6 +2006,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "engines": { "node": ">=18.18" }, @@ -2563,13 +2581,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 +2813,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": { @@ -2814,6 +2832,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2825,6 +2844,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2878,6 +2898,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/argparse": { "version": "2.0.1", @@ -2886,6 +2908,8 @@ "dev": true, "license": "Python-2.0", "peer": true + "license": "Python-2.0", + "peer": true }, "node_modules/babel-loader": { "version": "10.0.0", @@ -2979,15 +3003,20 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "peer": true + "dev": true, + "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": { @@ -3071,15 +3100,16 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "peer": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "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 +3127,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 +3144,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", @@ -3123,14 +3154,15 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=6" } }, "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": [ { @@ -3180,6 +3212,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3194,6 +3227,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/colorette": { "version": "2.0.20", @@ -3206,7 +3241,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", @@ -3214,6 +3250,8 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "peer": true + "dev": true, + "peer": true }, "node_modules/copy-webpack-plugin": { "version": "14.0.0", @@ -3360,6 +3398,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/detect-libc": { "version": "1.0.3", @@ -3382,21 +3422,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 +3462,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" }, @@ -3533,6 +3573,7 @@ "dev": true, "license": "Apache-2.0", "peer": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3547,6 +3588,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3564,6 +3606,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3582,6 +3625,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -3601,6 +3645,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=10" }, @@ -3615,6 +3660,7 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -3633,6 +3679,7 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -3644,6 +3691,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3662,6 +3710,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", @@ -3670,6 +3720,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3684,6 +3735,7 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, + "peer": true, "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -3703,6 +3755,7 @@ "dev": true, "license": "BSD-3-Clause", "peer": true, + "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -3801,6 +3854,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -3809,6 +3864,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/fast-uri": { "version": "3.1.0", @@ -3844,6 +3901,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -3987,6 +4045,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=18" }, @@ -4044,6 +4103,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">= 4" } @@ -4114,6 +4174,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=0.8.19" } @@ -4212,27 +4273,12 @@ "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", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", "license": "MIT" + "license": "MIT" }, "node_modules/jquery-ui": { "version": "1.14.1", @@ -4251,6 +4297,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -4278,6 +4325,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -4300,6 +4349,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/keyv": { "version": "4.5.4", @@ -4308,6 +4359,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -4329,6 +4381,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -4386,6 +4439,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/merge-stream": { "version": "2.0.0", @@ -4422,6 +4477,7 @@ "dev": true, "license": "ISC", "peer": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4463,6 +4519,8 @@ "dev": true, "license": "MIT", "peer": true + "license": "MIT", + "peer": true }, "node_modules/neo-async": { "version": "2.6.2", @@ -4503,6 +4561,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -4564,6 +4623,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4751,6 +4811,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -4762,6 +4823,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=6" } @@ -4898,6 +4960,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=4" } @@ -5114,6 +5177,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 +5197,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", @@ -5141,6 +5225,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=8" }, @@ -5165,6 +5250,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 +5294,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 +5346,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", @@ -5321,6 +5401,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -5329,9 +5410,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 +5461,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": [ { @@ -5417,6 +5498,7 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, + "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -5429,9 +5511,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 +5525,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 +5537,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 +5642,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": { @@ -5618,6 +5700,7 @@ "dev": true, "license": "MIT", "peer": true, + "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index b39cdc23..68bb8610 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "npm": ">=6.14.8" }, "scripts": { - "webpack": "webpack-cli", + "webpack": "webpack-cli -w", "build": "npm run webpack && npm run scss && cp -r node_modules/bootstrap-icons/font/fonts conditional/static/css/fonts" }, "dependencies": { @@ -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.txt b/requirements.txt index e25da049..24887492 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,9 @@ -<<<<<<< HEAD -# 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 # ->>>>>>> ed99cfd (Restored all my existing work) alembic==1.15.2 # via # -r requirements.in From fc1d5c6119dd62038e9846db30c601fedd1e64c8 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Tue, 3 Mar 2026 20:49:04 -0500 Subject: [PATCH 04/26] More display changes --- .../templates/major_project_submission.html | 202 +++++++++--------- .../stylesheets/pages/_major-project.scss | 29 +++ package.json | 2 +- 3 files changed, 126 insertions(+), 107 deletions(-) diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index 43574c0b..0b5d3822 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -130,7 +130,7 @@

Major Project Form

>
-
+
@@ -164,162 +164,152 @@

Major Project Form

- +
-

All Major Projects

+

All Major Projects

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

{{p['proj_name']}}

+ +
+ +
+

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

- {{p['name']}} ({{p['username']}}) - + + {{p['name']}} ({{p['username']}}) +
-
- + +
{% if is_eval_director %} - {% else %} - - + {% if p['status']=='Passed' %} - badge-success +
{% elif p['status']=='Failed' %} - badge-danger +
{% else %} - badge-warning +
{% endif %} - "> - {{p['status']}} -
- +
{% endif %} -
-
- - - + +
+
+ +
+
- +
-
+ +
-

- TLDR: {{p['tldr']}} -

+
-
+
- Skills: +
+
TL;DR
+

{{p['tldr']}}

+
- {% for s in p['skills'] %} - - {{s}} - - {% endfor %} +
+ Time Commitment +

{{p['time_spent']}}

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

- Time Commitment: - {{p['time_spent']}} -

+
+ Links +

{{p['links']}}

+
-

- Links: - {{p['links']}} -

+
+ Description +

{{p['desc']}}

+
-

- Description:
- {{p['desc']}} -

+
+
- {% endfor %} - {% endif %} - +{% endfor %} +{% endif %}
{% endblock %} \ No newline at end of file diff --git a/frontend/stylesheets/pages/_major-project.scss b/frontend/stylesheets/pages/_major-project.scss index 4d903666..448f6f84 100644 --- a/frontend/stylesheets/pages/_major-project.scss +++ b/frontend/stylesheets/pages/_major-project.scss @@ -1,3 +1,12 @@ +.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; @@ -41,6 +50,15 @@ margin-bottom: 2em; } +.proj-listing { + background-color: #fff; + padding: 0.5em; +} + +.rounded-circle { + border-radius: 50%; +} + .placeholder { color: #bbb; font-size: 16px; @@ -67,6 +85,17 @@ font-size: 10px; } +.skill-display { + display: block; + float: left; + background: #d979e3; + padding: 4px 8px 4px 8px; + margin: 2px 3px; + color: #444; + 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); } \ No newline at end of file diff --git a/package.json b/package.json index 68bb8610..7c07ceef 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "npm": ">=6.14.8" }, "scripts": { - "webpack": "webpack-cli -w", + "webpack": "webpack-cli", "build": "npm run webpack && npm run scss && cp -r node_modules/bootstrap-icons/font/fonts conditional/static/css/fonts" }, "dependencies": { From 0dbf574785d08e2ed1d54319a8e00dfb05badc22 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Tue, 3 Mar 2026 22:21:36 -0500 Subject: [PATCH 05/26] More display changes --- .../templates/major_project_submission.html | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index 0b5d3822..aff2c2f6 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -265,25 +265,24 @@

-
-
- -
-
TL;DR
-

{{p['tldr']}}

+
+
TLDR
+

{{p['tldr']}}

- Time Commitment -

{{p['time_spent']}}

+
Time Commitment
+

{{p['time_spent']}}

+
+
- Skills
+
Skills
{% for s in p['skills'] %} - + {{s}} {% endfor %} @@ -293,18 +292,16 @@
TL;DR
Links

{{p['links']}}

+
+
Description

{{p['desc']}}

-
-
-
-

From 30ff53e1998fec82290db141fd0d85308464ad04 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Sat, 7 Mar 2026 15:22:47 -0500 Subject: [PATCH 06/26] Rebased with dev and fixed issues that resulted from that --- Dockerfile | 4 +- .../blueprints/major_project_submission.py | 15 ++-- .../javascript/modules/majorProjectForm.js | 2 + migrations/versions/217b5842448c_.py | 26 ------ .../{09603d1cfc38_.py => 6c4cf35d7c0c_.py} | 8 +- migrations/versions/72444bd2437e_.py | 41 ---------- migrations/versions/8182939744c8_.py | 39 --------- migrations/versions/82cdb94fc915_.py | 26 ------ migrations/versions/e2673b9e9ea8_.py | 34 -------- package-lock.json | 79 ------------------- requirements.txt | 19 +---- 11 files changed, 19 insertions(+), 274 deletions(-) delete mode 100644 migrations/versions/217b5842448c_.py rename migrations/versions/{09603d1cfc38_.py => 6c4cf35d7c0c_.py} (91%) delete mode 100644 migrations/versions/72444bd2437e_.py delete mode 100644 migrations/versions/8182939744c8_.py delete mode 100644 migrations/versions/82cdb94fc915_.py delete mode 100644 migrations/versions/e2673b9e9ea8_.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/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index 68e1f352..87408cbb 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -1,10 +1,9 @@ import json import os -import requests - import requests import boto3 +from conditional.models.models import MajorProject, MajorProjectSkill from config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY from flask import Blueprint from flask import request @@ -18,8 +17,6 @@ from conditional.util.context_processors import get_member_name -from conditional.models.models import MajorProject, MajorProjectSkill - from conditional.util.ldap import ldap_is_eval_director from conditional.util.ldap import ldap_get_member from conditional.util.flask import render_template @@ -55,7 +52,7 @@ def display_major_project(user_dict=None): MajorProject.id == MajorProjectSkill.project_id ).group_by(MajorProject.id ).where(MajorProject.date >= start_of_year() - ).order_by(desc(MajorProject.date)) + ).order_by(MajorProject.date) major_projects = [ { @@ -133,7 +130,7 @@ def submit_major_project(user_dict=None): # All fields are required in order to be able to submit the form # TODO: Do we want any of the fields to have enforced min or max lengths? - if name == "" or tldr == "" or time_spent == "" or skills == "" or description == "": + if not name or not tldr or not time_spent or not description: return jsonify({"success": False}), 400 # TODO: Ensure all the information is being passed to the object @@ -144,11 +141,13 @@ def submit_major_project(user_dict=None): db.session.commit() + # project_id = project.id project = MajorProject.query.filter( - MajorProject.name == name and MajorProject.uid == user_id + MajorProject.name == name, + MajorProject.uid == user_id ).first() - skills_list = filter(lambda x: x != 'None', skills) + skills_list = list(filter(lambda x: x != 'None', skills)) print(f"Skills: {list(skills_list)}") for skill in skills_list: diff --git a/frontend/javascript/modules/majorProjectForm.js b/frontend/javascript/modules/majorProjectForm.js index 7412213b..fb339307 100644 --- a/frontend/javascript/modules/majorProjectForm.js +++ b/frontend/javascript/modules/majorProjectForm.js @@ -52,6 +52,8 @@ export default class MajorProjectForm { Array.from(this.form.getElementsByClassName('skill-tag')).forEach(tag => skills.push(tag.firstChild.data)) + console.log("Links: " + this.form.querySelector('textarea[name=links]').value); + let payload = { projectName: this.form.querySelector('input[name=name]').value, projectTldr: this.form.querySelector('input[name=tldr]').value, diff --git a/migrations/versions/217b5842448c_.py b/migrations/versions/217b5842448c_.py deleted file mode 100644 index 40c7a8b0..00000000 --- a/migrations/versions/217b5842448c_.py +++ /dev/null @@ -1,26 +0,0 @@ -"""add time spent to major projects - -Revision ID: 217b5842448c -Revises: e2673b9e9ea8 -Create Date: 2026-02-21 16:45:10.780115 - -""" - -# revision identifiers, used by Alembic. -revision = '217b5842448c' -down_revision = 'e2673b9e9ea8' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('major_projects', sa.Column('timeSpent', sa.Text(), nullable=True)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('major_projects', 'timeSpent') - # ### end Alembic commands ### diff --git a/migrations/versions/09603d1cfc38_.py b/migrations/versions/6c4cf35d7c0c_.py similarity index 91% rename from migrations/versions/09603d1cfc38_.py rename to migrations/versions/6c4cf35d7c0c_.py index 38280cb8..5cd8ad9e 100644 --- a/migrations/versions/09603d1cfc38_.py +++ b/migrations/versions/6c4cf35d7c0c_.py @@ -1,13 +1,13 @@ -"""add new fields to major project form +"""empty message -Revision ID: 09603d1cfc38 +Revision ID: 6c4cf35d7c0c Revises: f1d08673b870 -Create Date: 2026-03-01 10:46:33.652106 +Create Date: 2026-03-06 15:42:50.323042 """ # revision identifiers, used by Alembic. -revision = '09603d1cfc38' +revision = '6c4cf35d7c0c' down_revision = 'f1d08673b870' from alembic import op diff --git a/migrations/versions/72444bd2437e_.py b/migrations/versions/72444bd2437e_.py deleted file mode 100644 index 5bed108b..00000000 --- a/migrations/versions/72444bd2437e_.py +++ /dev/null @@ -1,41 +0,0 @@ -"""empty message - -Revision ID: 72444bd2437e -Revises: 217b5842448c -Create Date: 2026-02-28 17:15:54.028901 - -""" - -# revision identifiers, used by Alembic. -revision = '72444bd2437e' -down_revision = '217b5842448c' - -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.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', 'timeSpent') - op.drop_column('major_projects', 'tldr') - op.drop_table('major_project_skills') - # ### end Alembic commands ### diff --git a/migrations/versions/8182939744c8_.py b/migrations/versions/8182939744c8_.py deleted file mode 100644 index 00eeec1d..00000000 --- a/migrations/versions/8182939744c8_.py +++ /dev/null @@ -1,39 +0,0 @@ -"""empty message - -Revision ID: 8182939744c8 -Revises: f1d08673b870 -Create Date: 2026-03-01 10:04:50.175588 - -""" - -# revision identifiers, used by Alembic. -revision = '8182939744c8' -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.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', 'tldr') - op.drop_table('major_project_skills') - # ### end Alembic commands ### diff --git a/migrations/versions/82cdb94fc915_.py b/migrations/versions/82cdb94fc915_.py deleted file mode 100644 index 88de2ac8..00000000 --- a/migrations/versions/82cdb94fc915_.py +++ /dev/null @@ -1,26 +0,0 @@ -"""empty message - -Revision ID: 82cdb94fc915 -Revises: f1d08673b870 -Create Date: 2026-03-01 10:19:20.002845 - -""" - -# revision identifiers, used by Alembic. -revision = '82cdb94fc915' -down_revision = 'f1d08673b870' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('major_projects', sa.Column('links', sa.Text(), nullable=True)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('major_projects', 'links') - # ### end Alembic commands ### diff --git a/migrations/versions/e2673b9e9ea8_.py b/migrations/versions/e2673b9e9ea8_.py deleted file mode 100644 index 9c7b3755..00000000 --- a/migrations/versions/e2673b9e9ea8_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""empty message - -Revision ID: e2673b9e9ea8 -Revises: 7a3904cac24b -Create Date: 2026-02-21 13:10:18.957944 - -""" - -# revision identifiers, used by Alembic. -revision = 'e2673b9e9ea8' -down_revision = '7a3904cac24b' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('project_id_fk', 'major_project_skills', type_='foreignkey') - op.create_foreign_key(None, 'major_project_skills', 'major_projects', ['project_id'], ['id'], ondelete='cascade') - op.alter_column('major_projects', 'tldr', - existing_type=sa.VARCHAR(length=128), - nullable=True) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('major_projects', 'tldr', - existing_type=sa.VARCHAR(length=128), - nullable=False) - op.drop_constraint(None, 'major_project_skills', type_='foreignkey') - op.create_foreign_key('project_id_fk', 'major_project_skills', 'major_projects', ['project_id'], ['id']) - # ### end Alembic commands ### diff --git a/package-lock.json b/package-lock.json index 636df461..6084610f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1774,7 +1774,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1786,7 +1785,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -1803,7 +1801,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1823,8 +1820,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", @@ -1833,7 +1828,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -1848,7 +1842,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -1863,7 +1856,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1889,7 +1881,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1909,8 +1900,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/@eslint/js": { "version": "9.39.1", @@ -1919,7 +1908,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1934,7 +1922,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1946,7 +1933,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -1962,7 +1948,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "engines": { "node": ">=18.18.0" } @@ -1974,7 +1959,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -1990,7 +1974,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "engines": { "node": ">=12.22" }, @@ -2006,7 +1989,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "engines": { "node": ">=18.18" }, @@ -2832,7 +2814,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2844,7 +2825,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2898,8 +2878,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/argparse": { "version": "2.0.1", @@ -2908,8 +2886,6 @@ "dev": true, "license": "Python-2.0", "peer": true - "license": "Python-2.0", - "peer": true }, "node_modules/babel-loader": { "version": "10.0.0", @@ -3003,8 +2979,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "peer": true - "dev": true, - "peer": true }, "node_modules/baseline-browser-mapping": { "version": "2.10.0", @@ -3100,7 +3074,6 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "peer": true, - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3154,7 +3127,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=6" } @@ -3212,7 +3184,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3227,8 +3198,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/colorette": { "version": "2.0.20", @@ -3250,8 +3219,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "peer": true - "dev": true, - "peer": true }, "node_modules/copy-webpack-plugin": { "version": "14.0.0", @@ -3398,8 +3365,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/detect-libc": { "version": "1.0.3", @@ -3573,7 +3538,6 @@ "dev": true, "license": "Apache-2.0", "peer": true, - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3588,7 +3552,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3606,7 +3569,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3625,7 +3587,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -3644,8 +3605,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "peer": true, - "peer": true, "engines": { "node": ">=10" }, @@ -3660,7 +3619,6 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -3679,7 +3637,6 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, - "peer": true, "engines": { "node": ">=4.0" } @@ -3691,7 +3648,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3710,8 +3666,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", @@ -3720,7 +3674,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3735,7 +3688,6 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, - "peer": true, "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -3755,7 +3707,6 @@ "dev": true, "license": "BSD-3-Clause", "peer": true, - "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -3854,8 +3805,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -3864,8 +3813,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/fast-uri": { "version": "3.1.0", @@ -3901,7 +3848,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -4045,7 +3991,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=18" }, @@ -4103,7 +4048,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">= 4" } @@ -4174,7 +4118,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=0.8.19" } @@ -4278,7 +4221,6 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", "license": "MIT" - "license": "MIT" }, "node_modules/jquery-ui": { "version": "1.14.1", @@ -4297,7 +4239,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -4325,8 +4266,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -4349,8 +4288,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/keyv": { "version": "4.5.4", @@ -4359,7 +4296,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -4381,7 +4317,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -4439,8 +4374,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/merge-stream": { "version": "2.0.0", @@ -4477,7 +4410,6 @@ "dev": true, "license": "ISC", "peer": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4519,8 +4451,6 @@ "dev": true, "license": "MIT", "peer": true - "license": "MIT", - "peer": true }, "node_modules/neo-async": { "version": "2.6.2", @@ -4561,7 +4491,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -4623,7 +4552,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4811,7 +4739,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -4823,7 +4750,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=6" } @@ -4960,7 +4886,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=4" } @@ -5225,7 +5150,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=8" }, @@ -5401,7 +5325,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -5498,7 +5421,6 @@ "dev": true, "license": "BSD-2-Clause", "peer": true, - "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -5700,7 +5622,6 @@ "dev": true, "license": "MIT", "peer": true, - "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/requirements.txt b/requirements.txt index 24887492..b3e1fe19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,7 +38,7 @@ cffi==2.0.0 # via cryptography charset-normalizer==3.4.4 # via requests -click==8.1.8 +click==8.3.1 # via # -r requirements.in # flask @@ -99,15 +99,11 @@ jinja2==3.1.6 # via # -r requirements.in # flask -<<<<<<< HEAD -lazy-object-proxy==1.12.0 -======= jmespath==1.1.0 # via # boto3 # botocore lazy-object-proxy==1.4.3 ->>>>>>> ed99cfd (Restored all my existing work) # via -r requirements.in mako==1.3.10 # via @@ -171,15 +167,14 @@ pyproject-hooks==1.2.0 # via # build # pip-tools -<<<<<<< HEAD python-dotenv==1.2.2 -======= python-dateutil==2.6.1 # via # -r requirements.in # botocore -python-dotenv==1.2.1 ->>>>>>> ed99cfd (Restored all my existing work) + # via + # -r requirements.in + # botocore # via pydantic-settings python-editor==1.0.4 # via -r requirements.in @@ -190,13 +185,7 @@ requests==2.32.5 # flask-pyoidc # oic # pyjwkest -<<<<<<< HEAD -sentry-sdk==2.24.1 -======= -s3transfer==0.10.4 - # via boto3 sentry-sdk[flask]==2.24.1 ->>>>>>> ed99cfd (Restored all my existing work) # via -r requirements.in setuptools==82.0.0 # via pip-tools From ef1aaf9c4d55ce472f220bc99233db3ae7480019 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Sun, 8 Mar 2026 00:34:48 -0500 Subject: [PATCH 07/26] Form is almost done i think --- .../blueprints/major_project_submission.py | 7 ++-- .../templates/major_project_submission.html | 36 +++++++++++-------- .../javascript/modules/majorProjectForm.js | 4 +-- .../javascript/modules/majorProjectStatus.js | 2 +- .../stylesheets/pages/_major-project.scss | 24 ++++++++++--- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index 87408cbb..32ed3e12 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -4,6 +4,7 @@ import boto3 from conditional.models.models import MajorProject, MajorProjectSkill +from conditional.util.user_dict import user_dict_is_eval_director from config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY from flask import Blueprint from flask import request @@ -65,7 +66,7 @@ def display_major_project(user_dict=None): "time_spent": p.timeSpent, "skills": p.skills, "desc": p.description, - "links": p.links, + "links": list(filter(None, p.links.split("\n"))), "status": p.status, "is_owner": bool(user_dict["username"] == p.uid) } @@ -208,7 +209,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() @@ -236,7 +237,7 @@ 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() diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index aff2c2f6..78772951 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -125,7 +125,7 @@

Major Project Form

id="links" name="links" rows="2" - class="form-control" + class="form-control form-textarea" placeholder="If you have links (GitHub, YouTube, etc.), put them here!" >
@@ -184,7 +184,7 @@

All Major Projects

{% for p in major_projects %} -
+
@@ -192,7 +192,7 @@

All Major Projects

-
+

{{p['proj_name']}}

@@ -209,10 +209,10 @@

-
+
{% if is_eval_director %}
@@ -281,23 +281,31 @@
Time Commitment
Skills
- {% for s in p['skills'] %} - - {{s}} - - {% endfor %} +
+ {% for s in p['skills'] %} + + {{s}} + + {% endfor %} +
- Links -

{{p['links']}}

+
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']}}

+
Description
+

{{p['desc']}}

diff --git a/frontend/javascript/modules/majorProjectForm.js b/frontend/javascript/modules/majorProjectForm.js index fb339307..0fe9151b 100644 --- a/frontend/javascript/modules/majorProjectForm.js +++ b/frontend/javascript/modules/majorProjectForm.js @@ -51,9 +51,7 @@ export default class MajorProjectForm { let skills = []; Array.from(this.form.getElementsByClassName('skill-tag')).forEach(tag => skills.push(tag.firstChild.data)) - - console.log("Links: " + this.form.querySelector('textarea[name=links]').value); - + let payload = { projectName: this.form.querySelector('input[name=name]').value, projectTldr: this.form.querySelector('input[name=tldr]').value, 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/_major-project.scss b/frontend/stylesheets/pages/_major-project.scss index 448f6f84..cdd7f836 100644 --- a/frontend/stylesheets/pages/_major-project.scss +++ b/frontend/stylesheets/pages/_major-project.scss @@ -52,7 +52,7 @@ .proj-listing { background-color: #fff; - padding: 0.5em; + box-shadow: 1px 1px 2px grey; } .rounded-circle { @@ -67,10 +67,10 @@ .skill-tag { display: block; float: left; - background: #d979e3; + background: #b0197e; padding: 4px 30px 4px 8px; margin: 2px 3px; - color: #444; + color: #fff; border-radius: 5px; transition: .5s all; } @@ -88,14 +88,28 @@ .skill-display { display: block; float: left; - background: #d979e3; + background: #b0197e; padding: 4px 8px 4px 8px; margin: 2px 3px; - color: #444; + 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; } \ No newline at end of file From 80e2cf8946fee7ac584081e7b60752533503297b Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Sun, 8 Mar 2026 21:47:32 -0400 Subject: [PATCH 08/26] Fixed s3 uploads and file display --- .../blueprints/major_project_submission.py | 72 ++-- .../templates/major_project_submission.html | 313 +++++++++--------- config.env.py | 3 + .../stylesheets/pages/_major-project.scss | 20 ++ 4 files changed, 236 insertions(+), 172 deletions(-) diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index 32ed3e12..c9b71166 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -1,11 +1,11 @@ import json import os +import botocore import requests import boto3 from conditional.models.models import MajorProject, MajorProjectSkill from conditional.util.user_dict import user_dict_is_eval_director -from config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY from flask import Blueprint from flask import request from flask import jsonify @@ -24,10 +24,32 @@ from conditional import db, start_of_year, get_user, auth, app +import collections +collections.Callable = collections.abc.Callable + logger = structlog.get_logger() major_project_bp = Blueprint("major_project_bp", __name__) +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']] + else: + return [] + + except botocore.exceptions.ClientError as e: + print(f"Error listing files in the folder: {e}") + return [] @major_project_bp.route("/major_project/") @auth.oidc_auth("default") @@ -53,7 +75,14 @@ def display_major_project(user_dict=None): MajorProject.id == MajorProjectSkill.project_id ).group_by(MajorProject.id ).where(MajorProject.date >= start_of_year() - ).order_by(MajorProject.date) + ).order_by(desc(MajorProject.date), desc(MajorProject.id)) + + 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'] + ) major_projects = [ { @@ -68,7 +97,8 @@ def display_major_project(user_dict=None): "desc": p.description, "links": list(filter(None, p.links.split("\n"))), "status": p.status, - "is_owner": bool(user_dict["username"] == p.uid) + "is_owner": bool(user_dict["username"] == p.uid), + "files": list_files_in_folder("major-project-media", f"{p.id}/") } for p in proj_list ] @@ -88,7 +118,7 @@ def upload_major_project_files(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Uploading Major Project File(s)') - log.info(f"user_dict: {user_dict}") + # log.info(f"user_dict: {user_dict}") if len(list(request.files.keys())) <1: return "No file", 400 @@ -168,27 +198,23 @@ def submit_major_project(user_dict=None): name = name.replace(" + {% endblock %} {% block title %} @@ -11,9 +12,9 @@ {% block body %}
- +

Major Project Form

- +

@@ -33,226 +34,225 @@

Major Project Form

- +
- + - + - + - +
- + - +
List what skills you meaningfully used while working on this project (at least 2!)
- +
- +
- + - + - +
- +
- -
-
- - -
- Upload Media + +
+
+
+ + +
+ Upload Media +
+ + + Drag files here or click to upload. +
- - - 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 %} - -{% for p in major_projects %} - -
-
- -
- - -
- -
-

- {{p['proj_name']}} -

- - +
+ +
+ +
+ +
+

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

+ + - - - {{p['name']}} ({{p['username']}}) - -
- - -
- {% if is_eval_director %} -
+ + + {{p['name']}} ({{p['username']}}) + +
+ + +
+ {% if is_eval_director %} +
- - + +
  • + Passed +
  • +
  • + Pending +
  • +
  • + Failed +
  • +
  • + Delete +
  • +
    - + {% else %}
    {% if p['status']=='Passed' %} -
    +
    {% elif p['status']=='Failed' %} -
    +
    {% else %} -
    +
    {% endif %}
    {% endif %}
    - - + +
    - +
    - - + +
    @@ -271,25 +271,25 @@

    TLDR

    {{p['tldr']}}

    - +
    Time Commitment

    {{p['time_spent']}}

    - +
    Skills
    {% for s in p['skills'] %} - - {{s}} - + + {{s}} + {% endfor %}
    - +
    Links
    {% for l in p['links'] %} @@ -301,13 +301,28 @@
    Links
    {% endfor %}
    - +
    -
    +
    Description

    {{p['desc']}}

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

    No images uploaded

    + {% else %} + {% for f in p['files'] %} + + {% endfor %} + {% endif %} +
    + +
    diff --git a/config.env.py b/config.env.py index d4cf36c1..6d4913b8 100644 --- a/config.env.py +++ b/config.env.py @@ -27,9 +27,12 @@ LDAP_BIND_PW = env.get("CONDITIONAL_LDAP_BIND_PW", "") # S3 information +S3_URI = env.get("S3_URI", "") +S3_BUCKET_ID = env.get("S3_BUCKET_ID", "") 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/stylesheets/pages/_major-project.scss b/frontend/stylesheets/pages/_major-project.scss index cdd7f836..d9e70f24 100644 --- a/frontend/stylesheets/pages/_major-project.scss +++ b/frontend/stylesheets/pages/_major-project.scss @@ -112,4 +112,24 @@ .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 From 86a20609cba8abbeafc760f0c5d4c714909a98c6 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 9 Mar 2026 00:41:58 -0400 Subject: [PATCH 09/26] Added new MP info to dashboard --- conditional/blueprints/dashboard.py | 42 +- conditional/templates/dashboard.html | 781 +++++++++++---------- frontend/stylesheets/pages/_dashboard.scss | 8 + 3 files changed, 450 insertions(+), 381 deletions(-) diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index 32587a77..063836b5 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -1,8 +1,9 @@ +from sqlalchemy import desc, func import structlog from flask import Blueprint, request -from conditional import start_of_year, auth -from conditional.models.models import Conditional +from conditional import db, start_of_year, auth +from conditional.models.models import Conditional, MajorProjectSkill from conditional.models.models import HouseMeeting from conditional.models.models import MajorProject from conditional.models.models import MemberHouseMeetingAttendance @@ -82,15 +83,38 @@ def display_dashboard(user_dict=None): data['housing'] = housing + proj_list = db.session.query( + MajorProject.id, + MajorProject.date, + MajorProject.uid, + MajorProject.name, + MajorProject.tldr, + MajorProject.timeSpent, + 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)) + 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.timeSpent, + "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/templates/dashboard.html b/conditional/templates/dashboard.html index 397c8ef4..f7d8636d 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/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 From c603cfefaf4be42547394dac386d1bc6123c1651 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 9 Mar 2026 12:28:27 -0400 Subject: [PATCH 10/26] Uncommented the slackbot ping --- .../blueprints/major_project_submission.py | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index c9b71166..bc05ce20 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -83,6 +83,8 @@ def display_major_project(user_dict=None): aws_secret_access_key=app.config['AWS_SECRET_ACCESS_KEY'], endpoint_url=app.config['S3_URI'] ) + + bucket = app.config['S3_BUCKET_ID'] major_projects = [ { @@ -98,7 +100,7 @@ def display_major_project(user_dict=None): "links": list(filter(None, p.links.split("\n"))), "status": p.status, "is_owner": bool(user_dict["username"] == p.uid), - "files": list_files_in_folder("major-project-media", f"{p.id}/") + "files": list_files_in_folder(bucket, f"{p.id}/") } for p in proj_list ] @@ -118,8 +120,6 @@ def upload_major_project_files(user_dict=None): log = logger.new(request=request, auth_dict=user_dict) log.info('Uploading Major Project File(s)') - # log.info(f"user_dict: {user_dict}") - if len(list(request.files.keys())) <1: return "No file", 400 @@ -146,8 +146,6 @@ def submit_major_project(user_dict=None): post_data = request.get_json() - print(f"Post Data: {post_data}") # TODO: Remove this later - name = post_data["projectName"] tldr = post_data['projectTldr'] time_spent = post_data['projectTimeSpent'] @@ -164,8 +162,7 @@ def submit_major_project(user_dict=None): if not name or not tldr or not time_spent or not description: return jsonify({"success": False}), 400 - # TODO: Ensure all the information is being passed to the object - project = MajorProject(user_id, name, tldr, time_spent, description, links) + project: MajorProject = MajorProject(user_id, name, tldr, time_spent, description, links) # Save the info to the database db.session.add(project) @@ -178,8 +175,7 @@ def submit_major_project(user_dict=None): MajorProject.uid == user_id ).first() - skills_list = list(filter(lambda x: x != 'None', skills)) - print(f"Skills: {list(skills_list)}") + skills_list: list = list(filter(lambda x: x != 'None', skills)) for skill in skills_list: skill = skill.strip() @@ -219,12 +215,12 @@ def submit_major_project(user_dict=None): # Send the slack ping only after we know that the data was properly saved to the DB # TODO: Maybe add more info to the slack ping? - # send_slack_ping( - # { - # "text": f" *{get_member_name(user_id)}* ({user_id})" - # f" submitted their major project, *{name}*!" - # } - # ) + send_slack_ping( + { + "text": f" *{get_member_name(user_id)}* ({user_id})" + f" submitted their major project, *{name}*!" + } + ) return jsonify({"success": True}), 200 From 8fa3f15d2c6c8f4acd55ff4be4fed09807f070a5 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 9 Mar 2026 13:14:42 -0400 Subject: [PATCH 11/26] Fixed lint --- conditional/blueprints/dashboard.py | 2 +- .../blueprints/major_project_submission.py | 75 ++++++++----------- conditional/models/models.py | 2 +- 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index 063836b5..b8dde69f 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -99,7 +99,7 @@ def display_dashboard(user_dict=None): ).group_by(MajorProject.id ).where(MajorProject.date >= start_of_year() ).order_by(desc(MajorProject.date), desc(MajorProject.id)) - + data['major_projects'] = [ { "id": p.id, diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index bc05ce20..e14ee849 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -1,30 +1,29 @@ +import collections import json import os -import botocore -import requests -import boto3 -from conditional.models.models import MajorProject, MajorProjectSkill -from conditional.util.user_dict import user_dict_is_eval_director from flask import Blueprint from flask import request from flask import jsonify from flask import redirect -from sqlalchemy import func, desc - +import botocore +import requests +import boto3 import structlog + +from sqlalchemy import func, desc from werkzeug.utils import secure_filename -from conditional.util.context_processors import get_member_name +from conditional import db, start_of_year, 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.user_dict import user_dict_is_eval_director -from conditional import db, start_of_year, get_user, auth, app - -import collections collections.Callable = collections.abc.Callable logger = structlog.get_logger() @@ -33,20 +32,20 @@ def list_files_in_folder(bucket_name, folder_prefix): - s3 = boto3.client( + s3 = boto3.client( service_name="s3", - aws_access_key_id=app.config['AWS_ACCESS_KEY_ID'], + 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']] - else: - return [] - + + return [] + except botocore.exceptions.ClientError as e: print(f"Error listing files in the folder: {e}") return [] @@ -77,13 +76,6 @@ def display_major_project(user_dict=None): ).where(MajorProject.date >= start_of_year() ).order_by(desc(MajorProject.date), desc(MajorProject.id)) - 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'] - ) - bucket = app.config['S3_BUCKET_ID'] major_projects = [ @@ -112,7 +104,6 @@ def display_major_project(user_dict=None): major_projects=major_projects, major_projects_len=major_projects_len, username=user_dict["username"]) - @major_project_bp.route("/major_project/upload", methods=["POST"]) @auth.oidc_auth("default") @get_user @@ -122,7 +113,7 @@ def upload_major_project_files(user_dict=None): 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(): @@ -132,7 +123,7 @@ def upload_major_project_files(user_dict=None): os.makedirs(os.path.dirname(filename), exist_ok=True) file.save(filename) - + return jsonify({"success": True}), 200 @@ -161,7 +152,7 @@ def submit_major_project(user_dict=None): # TODO: Do we want any of the fields to have enforced min or max lengths? if not name or not tldr or not time_spent or not description: return jsonify({"success": False}), 400 - + project: MajorProject = MajorProject(user_id, name, tldr, time_spent, description, links) # Save the info to the database @@ -174,31 +165,31 @@ def submit_major_project(user_dict=None): 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 != "" and skill != 'None': + + 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(" Date: Mon, 9 Mar 2026 15:24:36 -0400 Subject: [PATCH 12/26] Renamed db column to make lint happy --- conditional/blueprints/dashboard.py | 4 +-- .../blueprints/major_project_submission.py | 4 +-- conditional/models/models.py | 4 +-- migrations/versions/95538b39976f_.py | 28 +++++++++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 migrations/versions/95538b39976f_.py diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index b8dde69f..420c7d4a 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -89,7 +89,7 @@ def display_dashboard(user_dict=None): MajorProject.uid, MajorProject.name, MajorProject.tldr, - MajorProject.timeSpent, + MajorProject.time_spent, MajorProject.description, MajorProject.links, MajorProject.status, @@ -107,7 +107,7 @@ def display_dashboard(user_dict=None): "name": p.name, "proj_name": p.name, "tldr": p.tldr, - "time_spent": p.timeSpent, + "time_spent": p.time_spent, "skills": p.skills, "desc": p.description, "links": list(filter(None, p.links.split("\n"))), diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index e14ee849..d7e303eb 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -65,7 +65,7 @@ def display_major_project(user_dict=None): MajorProject.uid, MajorProject.name, MajorProject.tldr, - MajorProject.timeSpent, + MajorProject.time_spent, MajorProject.description, MajorProject.links, MajorProject.status, @@ -86,7 +86,7 @@ def display_major_project(user_dict=None): "name": ldap_get_member(p.uid).cn, "proj_name": p.name, "tldr": p.tldr, - "time_spent": p.timeSpent, + "time_spent": p.time_spent, "skills": p.skills, "desc": p.description, "links": list(filter(None, p.links.split("\n"))), diff --git a/conditional/models/models.py b/conditional/models/models.py index 4858c5f1..f3a52072 100644 --- a/conditional/models/models.py +++ b/conditional/models/models.py @@ -134,7 +134,7 @@ class MajorProject(db.Model): uid = Column(String(32), nullable=False, index=True) name = Column(String(64), nullable=False) tldr = Column(String(128), nullable=True) - timeSpent = Column(Text, nullable=True) + time_spent = Column(Text, nullable=True) description = Column(Text, nullable=False) links = Column(Text, nullable=True) active = Column(Boolean, nullable=False) @@ -147,7 +147,7 @@ def __init__(self, uid, name, tldr, time_spent, description, links): # pylint: d self.date = datetime.now() self.name = name self.tldr = tldr - self.timeSpent = time_spent + self.time_spent = time_spent self.description = description self.links = links self.status = 'Pending' diff --git a/migrations/versions/95538b39976f_.py b/migrations/versions/95538b39976f_.py new file mode 100644 index 00000000..dc4c7ea0 --- /dev/null +++ b/migrations/versions/95538b39976f_.py @@ -0,0 +1,28 @@ +"""empty message + +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 ### From 992fd00eb21b793bdfa0490f152e8f38e191a6e4 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 9 Mar 2026 15:54:22 -0400 Subject: [PATCH 13/26] Reduced duplicate code byt moving helper funcs to utils --- conditional/blueprints/dashboard.py | 24 ++------- .../blueprints/major_project_submission.py | 50 ++----------------- conditional/util/major_project.py | 26 ++++++++++ conditional/util/s3.py | 24 +++++++++ 4 files changed, 59 insertions(+), 65 deletions(-) create mode 100644 conditional/util/major_project.py create mode 100644 conditional/util/s3.py diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py index 420c7d4a..39cf8265 100644 --- a/conditional/blueprints/dashboard.py +++ b/conditional/blueprints/dashboard.py @@ -1,11 +1,9 @@ -from sqlalchemy import desc, func import structlog from flask import Blueprint, request -from conditional import db, start_of_year, auth -from conditional.models.models import Conditional, MajorProjectSkill +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 @@ -13,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, \ @@ -83,22 +82,7 @@ def display_dashboard(user_dict=None): data['housing'] = housing - 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)) + proj_list = get_project_list() data['major_projects'] = [ { diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index d7e303eb..be93f006 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -7,22 +7,22 @@ from flask import jsonify from flask import redirect -import botocore import requests import boto3 import structlog -from sqlalchemy import func, desc from werkzeug.utils import secure_filename -from conditional import db, start_of_year, get_user, auth, app +from conditional import db, get_user, auth, app from conditional.models.models import MajorProject from conditional.models.models import MajorProjectSkill 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 collections.Callable = collections.abc.Callable @@ -30,26 +30,6 @@ major_project_bp = Blueprint("major_project_bp", __name__) -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 [] - @major_project_bp.route("/major_project/") @auth.oidc_auth("default") @get_user @@ -58,23 +38,7 @@ def display_major_project(user_dict=None): log.info("Display Major Project Page") # There is probably a better way to do this, but it does work - - 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)) + proj_list = get_project_list() bucket = app.config['S3_BUCKET_ID'] @@ -104,6 +68,7 @@ def display_major_project(user_dict=None): major_projects=major_projects, major_projects_len=major_projects_len, username=user_dict["username"]) + @major_project_bp.route("/major_project/upload", methods=["POST"]) @auth.oidc_auth("default") @get_user @@ -115,7 +80,6 @@ def upload_major_project_files(user_dict=None): 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 = secure_filename(file.filename) @@ -149,7 +113,6 @@ def submit_major_project(user_dict=None): log.info(user_id) # All fields are required in order to be able to submit the form - # TODO: Do we want any of the fields to have enforced min or max lengths? if not name or not tldr or not time_spent or not description: return jsonify({"success": False}), 400 @@ -159,8 +122,6 @@ def submit_major_project(user_dict=None): db.session.add(project) db.session.commit() - - # project_id = project.id project = MajorProject.query.filter( MajorProject.name == name, MajorProject.uid == user_id @@ -205,7 +166,6 @@ def submit_major_project(user_dict=None): # Send the slack ping only after we know that the data was properly saved to the DB - # TODO: Maybe add more info to the slack ping? send_slack_ping( { "text": f" *{get_member_name(user_id)}* ({user_id})" 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..5a56c82b --- /dev/null +++ b/conditional/util/s3.py @@ -0,0 +1,24 @@ +import boto3 +import botocore +from flask 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 [] From e4111a45129e19975040da806e160b8353e14518 Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 9 Mar 2026 16:55:21 -0400 Subject: [PATCH 14/26] Fix a sonarqube issue --- .../blueprints/major_project_submission.py | 13 +++--- .../templates/major_project_submission.html | 44 +++++++++---------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/conditional/blueprints/major_project_submission.py b/conditional/blueprints/major_project_submission.py index be93f006..0bebff28 100644 --- a/conditional/blueprints/major_project_submission.py +++ b/conditional/blueprints/major_project_submission.py @@ -38,11 +38,11 @@ def display_major_project(user_dict=None): log.info("Display Major Project Page") # There is probably a better way to do this, but it does work - proj_list = get_project_list() + proj_list: list = get_project_list() - bucket = app.config['S3_BUCKET_ID'] + bucket: str = app.config['S3_BUCKET_ID'] - major_projects = [ + major_projects: list[dict] = [ { "id": p.id, "date": p.date, @@ -61,12 +61,11 @@ def display_major_project(user_dict=None): 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, + major_projects_len=len(major_projects), username=user_dict["username"]) @major_project_bp.route("/major_project/upload", methods=["POST"]) @@ -82,7 +81,7 @@ def upload_major_project_files(user_dict=None): # Temporarily save files to a place, to be uploaded on submit for _, file in request.files.lists(): file = file[0] - safe_name = secure_filename(file.filename) + safe_name: str = secure_filename(file.filename) filename = f"/tmp/{user_dict['username']}/{safe_name}" os.makedirs(os.path.dirname(filename), exist_ok=True) @@ -152,7 +151,7 @@ def submit_major_project(user_dict=None): endpoint_url=app.config['S3_URI']) # Collect all the locally cached files and put them in the bucket - temp_dir = f"/tmp/{user_id}" + temp_dir: str = f"/tmp/{user_id}" if os.path.exists(temp_dir): for file in os.listdir(temp_dir): filepath = f"{temp_dir}/{file}" diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index 7be8ccd3..07044d3d 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -67,7 +67,7 @@

    Major Project Form

    -
    @@ -109,11 +109,11 @@

    Major Project Form

    @@ -123,11 +123,11 @@

    Major Project Form

    From 7286a027a84d3d5dd6789c18d42092d3a1a5882a Mon Sep 17 00:00:00 2001 From: pikachu0542 Date: Mon, 9 Mar 2026 17:03:55 -0400 Subject: [PATCH 15/26] Fixed some more sonarqube issues --- .../templates/major_project_submission.html | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/conditional/templates/major_project_submission.html b/conditional/templates/major_project_submission.html index 07044d3d..2daa7c3a 100644 --- a/conditional/templates/major_project_submission.html +++ b/conditional/templates/major_project_submission.html @@ -43,12 +43,12 @@

    Major Project Form

    @@ -118,7 +118,7 @@

    Major Project Form

    -