Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .github/workflows/ci.yml
Empty file.
20 changes: 13 additions & 7 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@
from flask_migrate import Migrate
from app.config import Config

application = Flask(__name__)
# application.config['SECRET_KEY'] = 'amber_pearl_latte_is_the_best'
application.config.from_object(Config)
db = SQLAlchemy(application)
migrate = Migrate(application, db)
login = LoginManager(application)
db = SQLAlchemy()
login = LoginManager()
login.login_view = 'login'

from app import routes, models
def create_application(config):
application = Flask(__name__)
application.config.from_object(config)

db.init_app(application)
login.init_app(application)

from app.blueprints import blueprint
application.register_blueprint(blueprint)

return application
6 changes: 6 additions & 0 deletions app/blueprints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

from flask import Blueprint

blueprint = Blueprint('main', __name__)

from app import models, routes
11 changes: 9 additions & 2 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@
default_database_uri = 'sqlite:///' + os.path.join(basedir, 'app.db')

class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'amber_pearl_latte_is_the_best'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or default_database_uri
SECRET_KEY = os.environ.get('SECRET_KEY')

class DeploymentConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or default_database_uri

class TestConfig(Config):
SQLALCHEMY_DATABASE_URI = 'sqlite:///memory'
TESTING = True

5 changes: 2 additions & 3 deletions app/controllers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from app import application
from app.forms import LoginForm, RegisterProjectForm
from flask import session
from app.models import Project, Student
from app import db


def try_to_login_user(student_id, password, registering):
student = Student.query.get(student_id)
student = db.session.get(Student, student_id)
if registering:
if student:
return f"User {student.id} already exists"
Expand Down
18 changes: 9 additions & 9 deletions app/routes.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from flask import flash, redirect, render_template, request, url_for
from flask_login import login_required, login_user, logout_user
from app import application
from app.controllers import try_to_login_user
from app.forms import LoginForm, RegisterProjectForm
from app.models import Project, Student
from app import db
from app.blueprints import blueprint


@application.route('/')
@application.route("/login", methods=['GET', 'POST'])
@blueprint.route('/')
@blueprint.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()

Expand All @@ -20,27 +20,27 @@ def login():
result = try_to_login_user(student_id, password, registering)
if isinstance(result, Student):
login_user(result)
return redirect(url_for('view_projects'))
return redirect(url_for('main.view_projects'))
else:
flash(result, 'error')

return render_template('login.html', form=form)

@application.route("/logout")
@blueprint.route("/logout")
def logout():
logout_user()
return redirect(url_for('login'))
return redirect(url_for('main.login'))


@application.route('/projects')
@blueprint.route('/projects')
@login_required
def view_projects():
projects = Project.query.all()
for p in projects:
print(p)
return render_template('index.html', projects=projects)

@application.route('/register', methods=['GET', 'POST'])
@blueprint.route('/register', methods=['GET', 'POST'])
@login_required
def register_project():
form = RegisterProjectForm()
Expand All @@ -67,6 +67,6 @@ def register_project():

db.session.commit()

return redirect(url_for('view_projects'))
return redirect(url_for('main.view_projects'))

return render_template('register.html', form=form)
6 changes: 3 additions & 3 deletions app/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<script>
window.onload = function() {
document.getElementById("registerProjectBtn").onclick = function() {
window.location.href = "{{ url_for('register_project') }}";
window.location.href = "{{ url_for('main.register_project') }}";
};
};
</script>
Expand All @@ -31,9 +31,9 @@
<td> {{ student.id }} </td>
{% endfor %}
<td>
{{ if current_user.project_id == project.id }}
{% if current_user.project_id == project.id %}
<button> Delete </button>
{{ endif }}
{% endif %}
</td>
</tr>
{% endfor %}
Expand Down
4 changes: 2 additions & 2 deletions app/templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{% block body %}
<div class="login-container">
<h1>Login</h1>
<form method="POST" action="{{ url_for('login') }}">
<form method="POST" action="{{ url_for('main.login') }}">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.student_id.label }}
Expand All @@ -22,7 +22,7 @@ <h1>Login</h1>
{{ form.register }}
</div>
<div class="form-group">
<button type="submit">Login</button>
<button id="submitBtn" type="submit">Login</button>
</div>
</form>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/templates/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<script>
window.onload = function() {
document.getElementById("cancelBtn").onclick = function() {
window.location.href = "{{ url_for('view_projects') }}";
window.location.href = "{{ url_for('main.view_projects') }}";
};
};
</script>
Expand All @@ -15,7 +15,7 @@
{% block body %}
<div id="halfPage">
<h2 class="class1">Group project sign-up</h2>
<form action="{{ url_for('register_project') }}" method="POST">
<form action="{{ url_for('main.register_project') }}" method="POST">

<input type="number" name="grpMembers" id="groupMembers" placeholder="Enter number of group members" min="1"
max="4"><br><br>
Expand Down
7 changes: 6 additions & 1 deletion project-signup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from app import application
from flask_migrate import Migrate
from app import create_application, db
from app.config import DeploymentConfig

if __name__ == "__main__":
application = create_application(DeploymentConfig)
migrate = Migrate(application, db)

application.run(debug=True, use_debugger=False, use_reloader=False)
32 changes: 32 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
alembic==1.15.2
attrs==25.3.0
blinker==1.9.0
certifi==2025.4.26
click==8.1.8
exceptiongroup==1.3.0
Flask==3.1.0
Flask-Login==0.6.3
Flask-Migrate==4.1.0
Flask-SQLAlchemy==3.1.1
Flask-WTF==1.2.2
greenlet==3.2.0
h11==0.16.0
idna==3.10
itsdangerous==2.2.0
Jinja2==3.1.6
Mako==1.3.10
MarkupSafe==3.0.2
outcome==1.3.0.post0
PySocks==1.7.1
selenium==4.32.0
sniffio==1.3.1
sortedcontainers==2.4.0
SQLAlchemy==2.0.40
trio==0.30.0
trio-websocket==0.12.2
typing_extensions==4.13.2
urllib3==2.4.0
websocket-client==1.8.0
Werkzeug==3.1.3
wsproto==1.2.0
WTForms==3.2.1
Empty file added test/__init__.py
Empty file.
62 changes: 62 additions & 0 deletions test/systemTests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

import multiprocessing
import time
import unittest
from flask import url_for
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions

from app.config import TestConfig
from app.controllers import try_to_login_user
from app.models import Student
from app import create_application, db

localHost = "http://localhost:5000/"

class SystemTests(unittest.TestCase):

def setUp(self):
self.testApp = create_application(TestConfig)
self.app_context = self.testApp.app_context()
self.app_context.push()
db.create_all()

self.server_thread = multiprocessing.Process(target=self.testApp.run)
self.server_thread.start()


options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
self.driver = webdriver.Chrome(options=options)

def test_successful_login(self):
student = Student(id=1)
student.set_password("a")
db.session.add(student)
db.session.commit()

self.driver.get(localHost)
id_input = self.driver.find_element(By.ID, 'student_id')
id_input.send_keys("1")

password_input = self.driver.find_element(By.ID, 'password')
password_input.send_keys("a")

login_btn = self.driver.find_element(By.ID, 'submitBtn')
login_btn.click()

WebDriverWait(self.driver, 5).until(expected_conditions.url_changes(self.driver.current_url))

self.assertEqual(self.driver.current_url, localHost + 'projects')

def tearDown(self):
self.server_thread.terminate()
self.driver.close()
db.session.remove()
db.drop_all()
self.app_context.pop()

if __name__ == '__main__':
unittest.main()
19 changes: 17 additions & 2 deletions test/unitTests.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@

import unittest

from app.config import TestConfig
from app.controllers import try_to_login_user
from app.models import Student
from app import db
from app import create_application, db

class UnitTests(unittest.TestCase):

def setUp(self):
self.testApp = create_application(TestConfig)
self.app_context = self.testApp.app_context()
self.app_context.push()
db.create_all()

def test_successful_login(self):
student = Student(id=1)
student.set_password("a")
db.session.add(student)
db.session.commit()

self.assertEqual(student, try_to_login_user(1,"a",False))
self.assertEqual('Wrong password', try_to_login_user(1,"b",False))

def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()

self.assertThat(try_to_login_user(1,"a",False))
if __name__ == '__main__':
unittest.main()