| @@ -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')) | |||