Work / coursework

Research Position Portal

Flask portal where faculty post research positions and students apply, with email-confirmed accounts and a Dockerised AWS deployment.

Kind
coursework
Status
complete
Role
team of 4: team liaison, top contributor
Period
Nov 2025 – Dec 2025
Affiliation
WPI CS3733 Software Engineering
domains
web-systems databases
methods
session-auth relational-modeling agile containerization
stack
python flask flask-login flask-migrate flask-wtf flask-mail sqlalchemy sqlite werkzeug jinja2 docker aws bootstrap html css

Problem

Undergraduate research positions at a university are matched almost entirely through informal channels. A professor mentions an opening in a lecture, or a student emails around hoping to catch one. Faculty have no clean way to advertise what they need. Students have no way to see what exists or to present the coursework that would qualify them.

The portal gives both sides a structured surface: faculty post positions with concrete requirements, students maintain a profile of majors, courses taken, programming languages and interests. Applications carry references.

Data

The interesting part is the relational model, because the matching problem is really a modelling problem. Users are polymorphic: Student and Faculty both inherit a shared User. The qualification surface is normalised across Major, Course, TakenCourse, ProgrammingLang and TopicOfInterest. Position, Application and Reference carry the workflow itself.

  • Store: SQLAlchemy models over SQLite, with Flask-Migrate handling schema migrations
  • Scale: a course project, so the data is seeded rather than organic, with no real user volume behind it

Approach

Flask with blueprint separation (auth, student, faculty, main, errors) keeps the two user roles isolated rather than tangled behind conditionals.

Authentication is session-based: Flask-Login holds the session. Passwords are hashed with Werkzeug’s generate_password_hash rather than stored. Faculty accounts additionally require email confirmation before they can post: a signed, expiring token generated with itsdangerous and delivered by Flask-Mail, redeemed at a /confirm/<token> route. The gate exists because a faculty account is the one that can create positions, so it is the account worth verifying.

Deployment is containerised with Docker and runs on AWS EC2. The image self-signs a TLS certificate with OpenSSL at build time and starts by applying migrations (flask db upgrade) before serving over HTTPS on port 3001. The team’s AWS runbook also provisions an RDS PostgreSQL instance, though the pinned dependencies ship no PostgreSQL driver. The container as built runs on SQLite.

Process was Agile across four people over six weeks, with the team liaison role carrying communication with the course staff and the largest share of the implementation on the merged branch.

Experiments

What didn’t work

pytest is pinned and the suite is empty. requirements.txt carries pytest==8.4.2 and there is a tests/ package, but it contains nothing except an empty __init__.py: no tests at all. On a six-week course project with a fixed demo date, testing was the thing that kept losing to features. The dependency was added in anticipation of a suite that never got written. Naming it here is more useful than quietly leaving the dependency to imply coverage that does not exist.

Auth0 was specified and never built. The requirements document offers SSO through Auth0 as an alternative to email-and-password login. No Auth0 SDK, no OAuth flow and no token handling ever landed. What shipped is the Flask-Login and Werkzeug path described above. The spec was written optimistically at the start of the term and never revised once the scope became clear.

The PostgreSQL story is a runbook, not a build. The AWS guide walks through provisioning RDS PostgreSQL, but no driver was ever added to the image, so the deployed container ran the same SQLite file the laptops did. The gap went unnoticed because nothing in the demo needed the data to survive a redeploy.

The graded deployment is gone. The EC2 instance backing the demo no longer answers, which is the ordinary fate of a free-tier box after a term ends, but it does mean the only remaining evidence is the source.

Artifacts