@@ -1,3 +1,13 @@ | |||
# @name: __init__.py | |||
# @version: 0.1 | |||
# @creation_date: 2021-10-20 | |||
# @license: The MIT License <https://opensource.org/licenses/MIT> | |||
# @author: Simon Bowie <ad7588@coventry.ac.uk> | |||
# @purpose: Initialises the app, SQLAlchemy, and configuration variables | |||
# @acknowledgements: | |||
# https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login | |||
# Config stuff adapted from https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iii-web-forms | |||
from flask import Flask | |||
from flask_sqlalchemy import SQLAlchemy | |||
from flask_login import LoginManager | |||
@@ -9,6 +19,7 @@ db = SQLAlchemy() | |||
def create_app(): | |||
app = Flask(__name__) | |||
# get config variables from OS environment variables: set in env file passed through Docker Compose | |||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY') | |||
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') | |||
@@ -1,3 +1,12 @@ | |||
# @name: auth.py | |||
# @version: 0.1 | |||
# @creation_date: 2021-10-20 | |||
# @license: The MIT License <https://opensource.org/licenses/MIT> | |||
# @author: Simon Bowie <ad7588@coventry.ac.uk> | |||
# @purpose: auth route for authorisation functions like logging in, signing up, and logging out | |||
# @acknowledgements: | |||
# https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login | |||
from flask import Blueprint, render_template, redirect, url_for, request, flash | |||
from werkzeug.security import generate_password_hash, check_password_hash | |||
from flask_login import login_user, logout_user, login_required | |||
@@ -6,6 +15,7 @@ from . import db | |||
auth = Blueprint('auth', __name__) | |||
# routes for login page | |||
@auth.route('/login') | |||
def login(): | |||
return render_template('login.html') | |||
@@ -16,6 +26,7 @@ def login_post(): | |||
password = request.form.get('password') | |||
remember = True if request.form.get('remember') else False | |||
# SQLAlchemy query to get user data based on their email | |||
user = User.query.filter_by(email=email).first() | |||
# check if the user actually exists | |||
@@ -28,6 +39,7 @@ def login_post(): | |||
login_user(user, remember=remember) | |||
return redirect(url_for('main.profile')) | |||
# routes for signup page | |||
@auth.route('/signup') | |||
def signup(): | |||
return render_template('signup.html') | |||
@@ -53,6 +65,7 @@ def signup_post(): | |||
return redirect(url_for('auth.login')) | |||
# route for logout function | |||
@auth.route('/logout') | |||
@login_required | |||
def logout(): |
@@ -1,13 +1,24 @@ | |||
# @name: main.py | |||
# @version: 0.1 | |||
# @creation_date: 2021-10-20 | |||
# @license: The MIT License <https://opensource.org/licenses/MIT> | |||
# @author: Simon Bowie <ad7588@coventry.ac.uk> | |||
# @purpose: Main route for index and other pages | |||
# @acknowledgements: | |||
# https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login | |||
from flask import Blueprint, render_template | |||
from flask_login import login_required, current_user | |||
from . import db | |||
main = Blueprint('main', __name__) | |||
# route for index page | |||
@main.route('/') | |||
def index(): | |||
return render_template('index.html') | |||
# route for profile page | |||
@main.route('/profile') | |||
@login_required | |||
def profile(): |
@@ -1,13 +1,24 @@ | |||
# @name: models.py | |||
# @version: 0.1 | |||
# @creation_date: 2021-10-20 | |||
# @license: The MIT License <https://opensource.org/licenses/MIT> | |||
# @author: Simon Bowie <ad7588@coventry.ac.uk> | |||
# @purpose: Database models for tables in the database | |||
# @acknowledgements: | |||
# https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login | |||
from flask_login import UserMixin | |||
from . import db | |||
from datetime import datetime | |||
# table for users | |||
class User(UserMixin, db.Model): | |||
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy | |||
email = db.Column(db.String(100), unique=True) | |||
password = db.Column(db.String(100)) | |||
name = db.Column(db.String(1000)) | |||
# table for tools | |||
class Tool(db.Model): | |||
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy | |||
created = db.Column(db.DateTime, default=datetime.utcnow) |
@@ -1,3 +1,15 @@ | |||
<!-- | |||
# @name: base.html | |||
# @version: 0.1 | |||
# @creation_date: 2021-10-20 | |||
# @license: The MIT License <https://opensource.org/licenses/MIT> | |||
# @author: Simon Bowie <ad7588@coventry.ac.uk> | |||
# @purpose: Basic layout for all pages | |||
# @acknowledgements: | |||
# https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3 | |||
# Bootstrap 5.1.3: https://getbootstrap.com/ | |||
--> | |||
<!DOCTYPE html> | |||
<html> | |||
@@ -7,7 +19,8 @@ | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title>COPIM online toolkit</title> | |||
<!-- Bootstrap CSS --> | |||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> | |||
<!--<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">--> | |||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> | |||
</head> | |||
<body> |
@@ -2,6 +2,6 @@ | |||
{% block content %} | |||
<h2>{% block title %} {{ tool['name'] }} {% endblock %}</h2> | |||
<span class="badge badge-primary">{{ tool['created'] }}</span> | |||
<span class="badge bg-primary">{{ tool['created'] }}</span> | |||
<p>{{ tool['description'] }}</p> | |||
{% endblock %} |
@@ -6,10 +6,10 @@ | |||
<a href="{{ url_for('tool.show_tool', tool_id=tool['id']) }}"> | |||
<h2>{{ tool['name'] }}</h2> | |||
</a> | |||
<span class="badge badge-primary">{{ tool['created'] }}</span> | |||
<span class="badge bg-primary">{{ tool['created'] }}</span> | |||
{% if current_user.is_authenticated %} | |||
<a href="{{ url_for('tool.edit_tool', tool_id=tool['id']) }}"> | |||
<span class="badge badge-warning">Edit</span> | |||
<span class="badge bg-warning">Edit</span> | |||
</a> | |||
{% endif %} | |||
<hr> |
@@ -1,3 +1,12 @@ | |||
# @name: tool.py | |||
# @version: 0.1 | |||
# @creation_date: 2021-10-20 | |||
# @license: The MIT License <https://opensource.org/licenses/MIT> | |||
# @author: Simon Bowie <ad7588@coventry.ac.uk> | |||
# @purpose: tool route for tool-related functions and pages | |||
# @acknowledgements: | |||
# https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3 | |||
from flask import Blueprint, render_template, request, flash, redirect, url_for | |||
from flask_login import login_required, current_user | |||
from .models import Tool | |||
@@ -6,22 +15,26 @@ from . import db | |||
tool = Blueprint('tool', __name__) | |||
# function to retrieve data about a single tool from the database | |||
def get_tool(tool_id): | |||
tool = Tool.query.filter_by(id=tool_id).first() | |||
if tool is None: | |||
abort(404) | |||
return tool | |||
# route for displaying all tools in database | |||
@tool.route('/tools') | |||
def get_tools(): | |||
tools = Tool.query | |||
return render_template('tools.html', tools=tools) | |||
# route for displaying a single tool based on the ID in the database | |||
@tool.route('/tools/<int:tool_id>') | |||
def show_tool(tool_id): | |||
tool = get_tool(tool_id) | |||
return render_template('tool.html', tool=tool) | |||
# route for editing a single tool based on the ID in the database | |||
@tool.route('/tools/<int:tool_id>/edit', methods=('GET', 'POST')) | |||
@login_required | |||
def edit_tool(tool_id): | |||
@@ -42,6 +55,18 @@ def edit_tool(tool_id): | |||
return render_template('edit.html', tool=tool) | |||
# route for function to delete a single tool from the edit page | |||
@tool.route('/tools/<int:tool_id>/delete', methods=('POST',)) | |||
@login_required | |||
def delete_tool(tool_id): | |||
tool = get_tool(tool_id) | |||
deletion = Tool.query.get(tool_id) | |||
db.session.delete(deletion) | |||
db.session.commit() | |||
flash('Successfully deleted!') | |||
return redirect(url_for('tool.get_tools')) | |||
# route for creating a new tool | |||
@tool.route('/tools/create', methods=('GET', 'POST')) | |||
@login_required | |||
def create_tool(): | |||
@@ -66,13 +91,3 @@ def create_tool(): | |||
db.session.commit() | |||
return render_template('create.html') | |||
@tool.route('/tools/<int:tool_id>/delete', methods=('POST',)) | |||
@login_required | |||
def delete_tool(tool_id): | |||
tool = get_tool(tool_id) | |||
deletion = Tool.query.get(tool_id) | |||
db.session.delete(deletion) | |||
db.session.commit() | |||
flash('Successfully deleted!') | |||
return redirect(url_for('tool.get_tools')) |