From b74ea26cfc1ff5389ebdb0b7d1cf9eaad34d0e63 Mon Sep 17 00:00:00 2001 From: b4-sharp <Bradley2.Sharp@live.uwe.ac.uk> Date: Mon, 13 Mar 2023 11:54:49 +0000 Subject: [PATCH] Fix merge --- store/forms.py | 2 +- store/models.py | 94 ++++- store/routes.py | 336 +++++++++++++++++- store/static/main.css | 18 +- store/static/oldmain.css | 69 ++++ store/templates/base.html | 65 ++-- store/templates/login.html | 5 - store/templates/userContent/ChangeEmail.html | 28 ++ .../templates/userContent/ChangePhNumber.html | 28 ++ .../templates/userContent/ChangeUsername.html | 28 ++ store/templates/userContent/account.html | 9 + .../templates/userContent/accountDetails.html | 40 +++ store/templates/userContent/admin.html | 9 + store/templates/userContent/login.html | 28 ++ store/templates/userContent/register.html | 38 ++ .../templates/userContent/reset_password.html | 21 ++ .../userContent/reset_password_confirm.html | 26 ++ .../userContent/reset_security1.html | 21 ++ store/templates/userContent/user.html | 9 + store/templates/userContent/verify_code.html | 21 ++ .../userContent/verify_code_security1.html | 21 ++ .../verify_code_security1_confirm.html | 26 ++ store/utility.py | 50 ++- unit_tests.py | 18 +- 24 files changed, 965 insertions(+), 45 deletions(-) create mode 100644 store/static/oldmain.css delete mode 100644 store/templates/login.html create mode 100644 store/templates/userContent/ChangeEmail.html create mode 100644 store/templates/userContent/ChangePhNumber.html create mode 100644 store/templates/userContent/ChangeUsername.html create mode 100644 store/templates/userContent/account.html create mode 100644 store/templates/userContent/accountDetails.html create mode 100644 store/templates/userContent/admin.html create mode 100644 store/templates/userContent/login.html create mode 100644 store/templates/userContent/register.html create mode 100644 store/templates/userContent/reset_password.html create mode 100644 store/templates/userContent/reset_password_confirm.html create mode 100644 store/templates/userContent/reset_security1.html create mode 100644 store/templates/userContent/user.html create mode 100644 store/templates/userContent/verify_code.html create mode 100644 store/templates/userContent/verify_code_security1.html create mode 100644 store/templates/userContent/verify_code_security1_confirm.html diff --git a/store/forms.py b/store/forms.py index ecef864..712ad60 100644 --- a/store/forms.py +++ b/store/forms.py @@ -1,4 +1,4 @@ import wtforms from flask_wtf import FlaskForm from wtforms import StringField # Basic example. -from wtforms.validators import DataRequired # Basic example. \ No newline at end of file +from wtforms.validators import DataRequired # Basic example. diff --git a/store/models.py b/store/models.py index d291958..b88e0e3 100644 --- a/store/models.py +++ b/store/models.py @@ -1,5 +1,10 @@ # https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/ + +#from flask import current_app from store import db +from flask_login import UserMixin +from werkzeug.security import generate_password_hash, check_password_hash + # Helper tables for many to many relationships, see https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/#many-to-many-relationships. items = db.Table('Items', @@ -40,4 +45,91 @@ class ItemSet(db.Model): backref=db.backref('ItemSets', lazy=True)) def __repr__(self): - return f"id: {self.id}, description: {self.description}, items: {self.items}" \ No newline at end of file + return f"id: {self.id}, description: {self.description}, items: {self.items}" + +# database for user register it will be added more cullomns in the future +# that is a basic implementation to check if it works +# Freddy implemented that +#UserMixin is to inherit flask-login implementetion +class User( db.Model, UserMixin): + user_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + username = db.Column(db.String(256), nullable=False, unique=True) + email = db.Column(db.String(256), nullable=False) + phone_number = db.Column(db.String(20), nullable=False) + password_hash = db.Column(db.String(256), nullable=False) + securityQ1 = db.Column(db.String(30), nullable=False) + userType = db.Column(db.String(20), default="standard") + + + @classmethod + def create_user(cls, username: str, password: str,email: str , phone_number: str, securityQ1 : str ) : + password_hash = generate_password_hash(password) + user = cls(username=username, password_hash= password_hash, email=email, phone_number=phone_number, securityQ1 = securityQ1) + db.session.add(user) + db.session.commit() + return user + + @classmethod + def update_passwords(cls, user_id: int, password: str): + user = cls.query.get(user_id) + password_hash = generate_password_hash(password) + user.password_hash = password_hash + db.session.commit() + + @classmethod + def update_security_q1(cls, user_id: int, securityQ1: str): + user = cls.query.get(user_id) + user.securityQ1 = securityQ1 + db.session.commit() + + @classmethod + def update_username(cls, user_id: int, username: str): + user = cls.query.get(user_id) + user.username = username + db.session.commit() + + + @classmethod + def update_email(cls, user_id: int, email: str): + user = cls.query.get(user_id) + user.email = email + db.session.commit() + + @classmethod + def update_Number(cls, user_id: int, phone_number: str): + user = cls.query.get(user_id) + user.email = phone_number + db.session.commit() + + def is_active(self) -> bool: + return True + + + def get_userDetails(self): + lis = [self.username, self.email, self.phone_number] + return lis + + + def get_id(self): + return str(self.user_id) + + def check_password(self, password: str) -> bool: + return check_password_hash(self.password_hash, password) + + def securityverification(self, securityQ1: str) -> bool: + if self.securityQ1 == securityQ1: + return True + else: + return False + + def checkIfUserExist(self, username: str) -> bool: + if self.username == username: + return True + else: + return False + + + + + def __repr__(self) -> str: + return f"User(user_id={self.user_id}, username='{self.username}')" \ No newline at end of file diff --git a/store/routes.py b/store/routes.py index f68c1b4..5525a60 100644 --- a/store/routes.py +++ b/store/routes.py @@ -1,13 +1,53 @@ from store import app, db -from flask import render_template +from flask import render_template, request, flash, redirect, url_for, Flask, session from store.utility import * +# Official flask-login doc free liecense +#https://flask-login.readthedocs.io/en/latest/ +from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user + +#Official flask-mail docum +#https://pythonhosted.org/Flask-Mail/ +from flask_mail import Mail, Message + + + +#Email configuration +app.config['MAIL_SERVER']='smtp.gmail.com' +app.config['MAIL_PORT'] = 465 +app.config['MAIL_USERNAME'] = 'antique.store7111@gmail.com' +app.config['MAIL_PASSWORD'] = 'nbouhrtaurcefgvj' +app.config['MAIL_USE_TLS'] = False +app.config['MAIL_USE_SSL'] = True + +mail = Mail(app) + + +#Create an instance of the LoginManager class and initialise +#it with your Flask application to begin using Flask-Login. +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = 'login' + + + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + +def get_user_id(): + if current_user.is_authenticated: + return current_user.user_id + else: + return None + @app.route("/") @app.route("/index") def index(): return render_template('index.html', title='Home') # If the title is unspecified then a default is used that is set in base.html @app.route("/basket") +@login_required def basket(): return render_template('basket.html', title='Basket') @@ -19,6 +59,296 @@ def items(): def itemSets(): return render_template('itemSets.html', title='Item Sets') -@app.route("/login") + + + +@app.route("/register", methods=['POST', 'GET']) +def register(): + if request.method == "POST": + error = None # errors + username = request.form['username'] + password = request.form['password'] + securityQ1 = request.form['securityQ1'] + email = request.form['email'] + phone_number = request.form['phone_number'] + #user_exists = User.query.filter_by(username=username).first() + if User.checkIfUserExist(User, username): + error = 'The username you chose is already taken.' + return render_template('userContent/register.html', title='Register', error=error) + else: + new_user = User.create_user(username=username, password=password, email = email, phone_number = phone_number,securityQ1 = securityQ1) + flash('Your account has been created! You are now able to log in.', 'success') + return redirect(url_for('index')) + return render_template('userContent/register.html', title='Register') + + +@app.route("/login", methods=['POST', 'GET']) def login(): - return render_template('login.html', title='Login') \ No newline at end of file + if request.method == "POST": + username = request.form['username'] + password = request.form['password'] + securityQ1 = request.form['securityQ1'] + + user = User.query.filter_by(username=username).first() + if user and user.check_password(password): + if user.securityverification(securityQ1) == True: + login_user(user) + print(current_user.userType) + return redirect(url_for('index')) + else: + flash('Invalid security answer') + else: + flash('Login unsuccessful. Please check username and password.') + return render_template('userContent/login.html', title='Login') + + +@app.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('index')) + + + + + + + + + + + +@app.route('/reset_password', methods=['GET', 'POST']) +def reset_password(): + if request.method == 'POST': + email = request.form['email'] + print("the email was :", email) + user = User.query.filter_by(email=email).first() + if user: + verification_code = getcode() + session['verification_code'] = verification_code + session['user_id'] = user.user_id + msg = Message('Password Reset Verification Code', sender='your-email@gmail.com', recipients=[email]) + msg.body = f"Hello, Your password reset verification code is {verification_code}" + mail.send(msg) + flash('A verification code has been sent to your email') + return redirect(url_for('verify_code')) + else: + flash('Invalid email address') + return render_template('userContent/reset_password.html') + +@app.route('/verify_code', methods=['GET', 'POST']) +def verify_code(): + if 'user_id' not in session or 'verification_code' not in session: + return redirect(url_for('reset_password')) + if request.method == 'POST': + verification_code = request.form['verification_code'] + if str(verification_code) == session['verification_code']: + session.pop('verification_code', None) + return redirect(url_for('reset_password_confirm')) + else: + flash('Invalid verification code') + return render_template('userContent/verify_code.html') + + + + +@app.route('/reset_password_confirm', methods=['GET', 'POST']) +def reset_password_confirm(): + if 'user_id' not in session: + return redirect(url_for('reset_password')) + user_id = session['user_id'] + if request.method == 'POST': + password = request.form['password'] + confirm_password = request.form['confirm_password'] + if password == confirm_password: + User.update_passwords(user_id, password) + session.pop('user_id', None) + flash('Your password has been successfully reset') + return redirect(url_for('login')) + else: + flash('Passwords do not match') + return render_template('userContent/reset_password_confirm.html') + + + + +def getcode(): + randomchar = [str(random.choice(string.ascii_letters)) + str(random.randint(10,99)) for _ in range(4)] + randomdString = "" + for i in range(0, 4): + randomdString += randomchar[i % 4] + + return randomdString + + + + +@app.route('/reset_security1', methods=['GET', 'POST']) +def reset_security1(): + if request.method == 'POST': + email = request.form['email'] + print("the email was :", email) + user = User.query.filter_by(email=email).first() + if user: + verification_code = getcode() + session['verification_code'] = verification_code + session['user_id'] = user.user_id + msg = Message('security answer, Reset Verification Code', sender='your-email@gmail.com', recipients=[email]) + msg.body = f"Hello, Your security answer reset verification code is {verification_code}" + mail.send(msg) + flash('A verification code has been sent to your email') + return redirect(url_for('verify_code_security1')) + else: + flash('Invalid email address') + return render_template('userContent/reset_security1.html') + +@app.route('/verify_code_security1', methods=['GET', 'POST']) +def verify_code_security1(): + if 'user_id' not in session or 'verification_code' not in session: + return redirect(url_for('verify_code_security1')) + if request.method == 'POST': + verification_code = request.form['verification_code'] + if str(verification_code) == session['verification_code']: + session.pop('verification_code', None) + return redirect(url_for('verify_code_security1_confirm')) + else: + flash('Invalid verification code') + return render_template('userContent/verify_code_security1.html') + + + + +@app.route('/verify_code_security1_confirm', methods=['GET', 'POST']) +def verify_code_security1_confirm(): + if 'user_id' not in session: + return redirect(url_for('reset_security1')) + user_id = session['user_id'] + if request.method == 'POST': + securityQ1 = request.form['securityQ1'] + confirm_securityQ1 = request.form['confirm_securityQ1'] + if securityQ1 == confirm_securityQ1: + User.update_security_q1(user_id, securityQ1) + session.pop('user_id', None) + flash('Your security question has been successfully reset') + return redirect(url_for('login')) + else: + flash('security question do not match') + return render_template('userContent/verify_code_security1_confirm.html') + + + +@app.route("/account") +@login_required +def account(): + if current_user.userType == 'standard': + return redirect(url_for('standardUser')) + elif current_user.userType == 'admin': + return redirect(url_for('admin')) + else: + flash('Unauthorized access please contact us on antique.store7111@gmail.com') + return redirect(url_for('index')) + + + + +@app.route("/accountDetails") +@login_required +def accountDetails(): + a = get_user_id() + user = User.query.filter_by(user_id = a).first() + return render_template('userContent/accountDetails.html', user=user) + + + +@app.route("/standardUser") +@login_required +def standardUser(): + if current_user.userType != 'standard': + flash('Unauthorized access no admins allowed please create a standard account') + return redirect(url_for('home')) + else: + return render_template('userContent/user.html') + + + + +@app.route("/admin") +@login_required +def admin(): + if current_user.userType != 'admin': + flash('Unauthorized access') + return redirect(url_for('home')) + else: + return render_template('userContent/admin.html') + +@app.route("/ChangeUsername/", methods=['GET', 'POST']) +@login_required +def ChangeUsername(): + + if request.method == 'POST': + user_id = current_user.user_id + password = request.form['password'] + securityQ1 = request.form['securityQ1'] + NewUsername = request.form['NewUsername'] + user = User.query.get(user_id) + if user and user.check_password(password): + #if User.check_password(user_id, password): + if user.securityverification(securityQ1) == True: + User.update_username(user_id, NewUsername) + flash('username successfully changed.') + + else: + flash('Invalid security answer') + else: + flash('Wrong password.') + + return render_template('userContent/ChangeUsername.html') + + + +@app.route("/ChangeEmail/", methods=['GET', 'POST']) +@login_required +def ChangeEmail(): + + if request.method == 'POST': + user_id = current_user.user_id + password = request.form['password'] + securityQ1 = request.form['securityQ1'] + NewEmail = request.form['NewEmail'] + user = User.query.get(user_id) + if user and user.check_password(password): + #if User.check_password(user_id, password): + if user.securityverification(securityQ1) == True: + User.update_email(user_id, NewEmail) + flash('email successfully changed.') + + else: + flash('Invalid security answer') + else: + flash('Wrong password.') + + return render_template('userContent/ChangeEmail.html') + +@app.route("/ChangePhNumber/", methods=['GET', 'POST']) +@login_required +def ChangePhNumber(): + + if request.method == 'POST': + user_id = current_user.user_id + password = request.form['password'] + securityQ1 = request.form['securityQ1'] + NewNumber = request.form['NewNumber'] + user = User.query.get(user_id) + if user and user.check_password(password): + #if User.check_password(user_id, password): + if user.securityverification(securityQ1) == True: + User.update_Number(user_id, NewNumber) + flash('New Number successfully changed.') + + else: + flash('Invalid security answer') + else: + flash('Wrong password.') + + return render_template('userContent/ChangePhNumber.html') \ No newline at end of file diff --git a/store/static/main.css b/store/static/main.css index e2369d1..6782b0d 100644 --- a/store/static/main.css +++ b/store/static/main.css @@ -72,7 +72,7 @@ div{ width:100%; } -li a { +.navbar li a { color:white; display: block; text-align: center; @@ -80,7 +80,7 @@ li a { padding: 14px 16px; } -ul { +.navbar ul { list-style-type: none; margin: 0; padding: 0; @@ -98,11 +98,11 @@ input[type=text] { font-size: 17px; } -li { +.navbar li { float:left; } -li a:hover { +.navbar li a:hover { background-color: white; color:black; } @@ -114,4 +114,14 @@ li a:hover { h1{ color: black; + font-size: 30px; } + +.error{ + text-decoration: none; + color:red; + font-size: 30px; +} + + + diff --git a/store/static/oldmain.css b/store/static/oldmain.css new file mode 100644 index 0000000..5a6d56f --- /dev/null +++ b/store/static/oldmain.css @@ -0,0 +1,69 @@ +body { + background-color: white; +} + +footer { + background-color: black; + color: white; + position: fixed; + bottom:0; + left:0; + width:100%; +} + +div{ + position:fixed; + left:0; + width:100%; +} + +.navbar li a { + color:white; + display: block; + text-align: center; + text-decoration: none; + padding: 14px 16px; +} + +.navbar ul { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: black; +} + + +input[type=text] { + float: left; + padding: 6px; + border: none; + margin-top: 8px; + margin-right: 16px; + font-size: 17px; +} + +.navbar li { + float:left; +} + +.navbar li a:hover { + background-color: white; + color:black; +} + +*{ + text-align: center; + font-family: sans-serif; +} + +h1{ + color: black; + font-size: 30px; +} + +.error{ + text-decoration: none; + color:red; + font-size: 30px; +} \ No newline at end of file diff --git a/store/templates/base.html b/store/templates/base.html index ad2955a..fd45e13 100644 --- a/store/templates/base.html +++ b/store/templates/base.html @@ -3,33 +3,58 @@ <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>{% block title %}{% endblock%}</title> - <link rel="stylesheet" type="text/css" href="../static/main.css"> <!-- Load the CSS. --> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}"> <!-- Load the CSS. --> </head> <body> - <header> - <h1>ANTIQUES ONLINE</h1> + <header class="header"> + <a class="logo" href="{{ url_for('index')}}">ANTIQUES ONLINE</a> </header> - <div> - <ul> - <li><a href="{{ url_for('basket')}}">Basket</a></li> - <li><a href="{{ url_for('items')}}">Items</a></li> - <li><a href="{{ url_for('itemSets')}}">Item Sets</a></li> - <li><a href="{{ url_for('login')}}">Login</a></li> + <nav class="navbar"> + <ul class="navbar-list"> + <li><a href="{{ url_for('basket')}}">Basket</a></li> + <li><a href="{{ url_for('items')}}">Items</a></li> + <li><a href="{{ url_for('itemSets')}}">Item Sets</a></li> + + {% if current_user.is_authenticated %} + <li><a href="{{ url_for('account')}}">Account</a></li> + <li><a href="{{ url_for('logout')}}">Logout</a></li> + <li><a class="welcome">Welcome, {{ current_user.username }}!</a></li> + {% else %} + <li><a href="{{ url_for('login')}}">Login</a></li> + <li><a href="{{ url_for('register')}}">Register</a></li> + {% endif %} + + <li class="search"> + <form> + <button type="submit"><i class="fa fa-search"></i></button> + <input type="text" placeholder="Search..."> + </form> + </li> + </ul> + </nav> + + <div class="container"> + {% with messages = get_flashed_messages() %} + {% if messages %} + <ul class="flashes"> + {% for message in messages %} + <li class="error">{{ message }}</li> + {% endfor %} + </ul> + {% endif %} + {% endwith %} - <input type="text" placeholder="Search..."> - </ul> - </div> - - {% block content %} - - {% endblock %} <!-- Where HTML will go when extended. --> - - <footer> - <p>Contact Info: mail@mail.com + {% block content %} + + {% endblock %} <!-- Where HTML will go when extended. --> + </div> + + <footer class="footer"> + <p>Contact Info: mail@mail.com <br> Phone Number 0000 000 0000</p> - </footer> + </footer> </body> </html> diff --git a/store/templates/login.html b/store/templates/login.html deleted file mode 100644 index ecb8f69..0000000 --- a/store/templates/login.html +++ /dev/null @@ -1,5 +0,0 @@ -{%extends 'base.html' %} - -{% block content %} - {% block title %} Login | Antiques Online {% endblock %} -{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/ChangeEmail.html b/store/templates/userContent/ChangeEmail.html new file mode 100644 index 0000000..db4e042 --- /dev/null +++ b/store/templates/userContent/ChangeEmail.html @@ -0,0 +1,28 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Reset code {% endblock %} + + +<form method="POST" action="{{ url_for('ChangeEmail') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>Change E-mail</h1> + + <label for="password"><b>Password verification</b></label> + <input type="password" placeholder="Enter Password" name="password" id="password" required> + + + <label for="securityQ1"><b>what is your favourite colour, for verification porpuses/b></label> + <input type="securityQ1" placeholder="Enter a colour" name="securityQ1" id="securityQ1" required> + + <label for="NewEmail"><b>New E-mail</b></label> + <input type="NewEmail" placeholder="Enter new E-mail" name="NewEmail" id="NewEmail" required> + + <input class="button" type="submit" value="Change E-mail" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/ChangePhNumber.html b/store/templates/userContent/ChangePhNumber.html new file mode 100644 index 0000000..ec7cf88 --- /dev/null +++ b/store/templates/userContent/ChangePhNumber.html @@ -0,0 +1,28 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Reset code {% endblock %} + + +<form method="POST" action="{{ url_for('ChangePhNumber') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>Change Number</h1> + + <label for="password"><b>Password verification</b></label> + <input type="password" placeholder="Enter Password" name="password" id="password" required> + + + <label for="securityQ1"><b>what is your favourite colour, for verification porpuses/b></label> + <input type="securityQ1" placeholder="Enter a colour" name="securityQ1" id="securityQ1" required> + + <label for="NewNumber"><b> Enter New Number</b></label> + <input type="NewNumber" placeholder="Enter new Number" name="NewNumber" id="NewNumber" required> + + <input class="button" type="submit" value="Change Number" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/ChangeUsername.html b/store/templates/userContent/ChangeUsername.html new file mode 100644 index 0000000..b2799c2 --- /dev/null +++ b/store/templates/userContent/ChangeUsername.html @@ -0,0 +1,28 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Reset code {% endblock %} + + +<form method="POST" action="{{ url_for('ChangeUsername') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>Change Username</h1> + + <label for="password"><b>Password verification</b></label> + <input type="password" placeholder="Enter Password" name="password" id="password" required> + + + <label for="securityQ1"><b>what is your favourite colour, for verification porpuses/b></label> + <input type="securityQ1" placeholder="Enter a colour" name="securityQ1" id="securityQ1" required> + + <label for="NewUsername"><b>New username</b></label> + <input type="NewUsername" placeholder="Enter new Username" name="NewUsername" id="NewUsername" required> + + <input class="button" type="submit" value="Change username" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/account.html b/store/templates/userContent/account.html new file mode 100644 index 0000000..f341255 --- /dev/null +++ b/store/templates/userContent/account.html @@ -0,0 +1,9 @@ +{%extends 'base.html' %} + +{% block content %} + {% block title %} Account{% endblock %} + <h1>account Information</h1> + <ul> + <li><a href="{{ url_for('accountDetails', user_id=current_user.id)}}">View my Account details</a></li> + </ul> +{% endblock %} diff --git a/store/templates/userContent/accountDetails.html b/store/templates/userContent/accountDetails.html new file mode 100644 index 0000000..b0a4082 --- /dev/null +++ b/store/templates/userContent/accountDetails.html @@ -0,0 +1,40 @@ +{%extends 'base.html' %} + +{% block content %} + + +<h1>Account Details</h1> +<h1>Welcome, {{ user.username }}</h1> +<table style="width:100%"> + <tr> + <th>Your username is:</th> + <th>{{ user.username }}</th> + <th><a href="{{ url_for('ChangeUsername')}}">change your username</a></th> + + </tr> + <tr> + <td>Your email address is:</td> + <td>{{ user.email }}</td> + <td><a href="{{ url_for('ChangeEmail')}}">Change email address</a></td> + </tr> + <tr> + <th>Your Phone Number is:</th> + <td>{{ user.phone_number }}</td> + <td><a href="{{ url_for('ChangePhNumber')}}">change phone number</a></td> + </tr> + </table> + +{% endblock %} + + + + +<!-- + {% block title %} Account{% endblock %} + <h1>Account Details</h1> + <h1>Welcome, {{ user.username }}</h1> + <p>Your email address is: {{ user.email }}</p> + <p>Your Phone Number is: {{ user.phone_number }}</p> + + + --> \ No newline at end of file diff --git a/store/templates/userContent/admin.html b/store/templates/userContent/admin.html new file mode 100644 index 0000000..7b4ed58 --- /dev/null +++ b/store/templates/userContent/admin.html @@ -0,0 +1,9 @@ +{%extends 'base.html' %} + +{% block content %} + {% block title %} Account{% endblock %} + <h1>Admin usertype account Information</h1> + <ul> + <li><a href="{{ url_for('accountDetails', user_id=current_user.id)}}">View my Account details</a></li> + </ul> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/login.html b/store/templates/userContent/login.html new file mode 100644 index 0000000..1513de5 --- /dev/null +++ b/store/templates/userContent/login.html @@ -0,0 +1,28 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} login | Antiques Online {% endblock %} + + +<form method="POST" action="{{ url_for('login') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>Login</h1> + + <label for="username"><b>username</b></label> + <input type="username" placeholder="Enter username" name="username" id="username" required> + + <label for="password"><b>Password</b></label> + <input type="password" placeholder="Enter Password" name="password" id="password" required> + <label for="securityQ1"><b>what is your favourite colour</b></label> + <input type="securityQ1" placeholder="what is your favourite colour" name="securityQ1" id="securityQ1" required> + + <a href="{{ url_for('reset_password')}}">Forgot my Password</a> + <a href="{{ url_for('reset_security1')}}">Forgot my favourite color</a> + <input class="button" type="submit" value="Login" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/register.html b/store/templates/userContent/register.html new file mode 100644 index 0000000..c813b3c --- /dev/null +++ b/store/templates/userContent/register.html @@ -0,0 +1,38 @@ + + + {%extends 'base.html' %} +{% block content %} + {% block title %} Register | Antiques Online {% endblock %} + + +<form method="POST" action="{{ url_for('register') }}" style="float: center; text-align: center;"> + + <div class="container"> + + <h1>Register</h1> + + {% if error %} + <p class=error><strong>Error:</strong> {{ error }} + {% endif %} + + <label for="username"><b>username</b></label> + <input type="username" placeholder="Enter username" name="username" id="username" required> + + <label for="email"><b>E-mail</b></label> + <input type="email" placeholder="Enter E-mail" name="email" id="email" required> + + <label for="phone_number"><b>Phone Number</b></label> + <input type="phone_number" placeholder="Enter Number" name="phone_number" id="phone_number" required> + + <label for="securityQ1"><b>what is your favourite colour</b></label> + <input type="securityQ1" placeholder="Enter a colour" name="securityQ1" id="securityQ1" required> + + <label for="password"><b>Password</b></label> + <input type="password" placeholder="Enter Password" name="password" id="password" required> + + <input class="button" type="submit" value="Register" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/reset_password.html b/store/templates/userContent/reset_password.html new file mode 100644 index 0000000..74f003d --- /dev/null +++ b/store/templates/userContent/reset_password.html @@ -0,0 +1,21 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Reset code {% endblock %} + + +<form method="POST" action="{{ url_for('reset_password') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>email verify</h1> + + <label for="email"><b>Enter your email</b></label> + <input type="email" placeholder="Enter your email" name="email" id="email" required> + + <input class="button" type="submit" value="Reset Password" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/reset_password_confirm.html b/store/templates/userContent/reset_password_confirm.html new file mode 100644 index 0000000..72ece7e --- /dev/null +++ b/store/templates/userContent/reset_password_confirm.html @@ -0,0 +1,26 @@ + +{%extends 'base.html' %} +{% block content %} + {% block title %} Verify reset Password {% endblock %} + + + <form method="POST" action="{{ url_for('reset_password_confirm') }}" style="float: center; text-align: center;"> + + <div class="container"> + + <h1>Reset Password</h1> + + + <label for="password"><b>Password</b></label> + <input type="password" placeholder="Enter Password" name="password" id="password" required> + + + <label for="confirm_password"><b>confirm password</b></label> + <input type="confirm_password" placeholder="confirm password" name="confirm_password" id="confirm_password" required> + + <input class="button" type="submit" value="confirm password" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/reset_security1.html b/store/templates/userContent/reset_security1.html new file mode 100644 index 0000000..0e723fe --- /dev/null +++ b/store/templates/userContent/reset_security1.html @@ -0,0 +1,21 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Reset code {% endblock %} + + +<form method="POST" action="{{ url_for('reset_security1') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>email verify</h1> + + <label for="email"><b>Enter your email</b></label> + <input type="email" placeholder="Enter your email" name="email" id="email" required> + + <input class="button" type="submit" value="Reset security answer" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/user.html b/store/templates/userContent/user.html new file mode 100644 index 0000000..d0bdc29 --- /dev/null +++ b/store/templates/userContent/user.html @@ -0,0 +1,9 @@ +{%extends 'base.html' %} + +{% block content %} + {% block title %} Account{% endblock %} + <h1>Standard usertype account Information</h1> + <ul> + <li><a href="{{ url_for('accountDetails', user_id=current_user.id)}}">View my Account details</a></li> + </ul> +{% endblock %} diff --git a/store/templates/userContent/verify_code.html b/store/templates/userContent/verify_code.html new file mode 100644 index 0000000..8022e30 --- /dev/null +++ b/store/templates/userContent/verify_code.html @@ -0,0 +1,21 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Verify reset Password {% endblock %} + + +<form method="POST" action="{{ url_for('verify_code') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>Login</h1> + + <label for="verification_code"><b>Enter your code </b></label> + <input type="verification_code" placeholder="Enter your code " name="verification_code" id="verification_code" required> + + <input class="button" type="submit" value="Reset Password" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/verify_code_security1.html b/store/templates/userContent/verify_code_security1.html new file mode 100644 index 0000000..094a2fc --- /dev/null +++ b/store/templates/userContent/verify_code_security1.html @@ -0,0 +1,21 @@ + + +{%extends 'base.html' %} +{% block content %} + {% block title %} Verify reset Password {% endblock %} + + +<form method="POST" action="{{ url_for('verify_code_security1') }}" style="float: center; text-align: center;"> + + <div class="container"> + <h1>Reset security answer</h1> + + <label for="verification_code"><b>Enter your code </b></label> + <input type="verification_code" placeholder="Enter your code " name="verification_code" id="verification_code" required> + + <input class="button" type="submit" value="Reset security answer" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/templates/userContent/verify_code_security1_confirm.html b/store/templates/userContent/verify_code_security1_confirm.html new file mode 100644 index 0000000..dc526cd --- /dev/null +++ b/store/templates/userContent/verify_code_security1_confirm.html @@ -0,0 +1,26 @@ + +{%extends 'base.html' %} +{% block content %} + {% block title %} Verify reset Password {% endblock %} + + + <form method="POST" action="{{ url_for('verify_code_security1_confirm') }}" style="float: center; text-align: center;"> + + <div class="container"> + + <h1>Reset security question</h1> + + + <label for="securityQ1"><b>what is your favourite colour</b></label> + <input type="securityQ1" placeholder="Enter a colour" name="securityQ1" id="securityQ1" required> + + + <label for="confirm_securityQ1"><b>confirm answer</b></label> + <input type="confirm_securityQ1" placeholder="confirm answer" name="confirm_securityQ1" id="confirm_securityQ1" required> + + <input class="button" type="submit" value="confirm password" > + + </div> + + </form> +{% endblock %} \ No newline at end of file diff --git a/store/utility.py b/store/utility.py index 27029b6..6d97f9d 100644 --- a/store/utility.py +++ b/store/utility.py @@ -1,7 +1,11 @@ # TODO: Search for similiarly named item to replace removed item from itemset. + from store.models import * from sqlalchemy import select -from store import db +from store import * +import random +import string # for autegenarating letters + # Difference between filter and filter_by: https://stackoverflow.com/a/31560897 def get_unsold_items(): return Item.query.filter_by(date_sold=None).all() @@ -9,14 +13,28 @@ def get_unsold_items(): def get_sold_items(): return Item.query.filter(Item.date_sold.isnot(None)).all() -def get_all_items(): - return Item.query().all() +def get_items(): + return Item.query.all() def get_item_sets(): return ItemSet.query.all() -def add_similar_item_to_set(item): - pass +from difflib import SequenceMatcher + +# Search the description of all items to find similar. +# This is intended for use in replacing items in an items, +# and works best when description is close to the target. +# As a result it does not return sold items. +def get_similar_items(description): + to_search = get_items() + found = [] + for descriptable in to_search: + # This relies on the fact both Item and Items sets have a description column. + if (SequenceMatcher(None, description.lower(), descriptable.description.lower()).ratio() > 0.4 # This ratio comparison needs to be fine tuned. + and descriptable.date_sold == None): + found.append(descriptable) + + return found def remove_item(item): add_similar_item_to_set(item) @@ -25,9 +43,19 @@ def remove_item(item): def remove_item_by_id(item_id): - add_similar_item_to_set()# TODO: Need to get Item object to pass to add similar item to set + add_similar_item_to_set() # TODO: Need to get Item object to pass to add similar item to set db.session.Item.query() +# Search desciption for matching substring. +# This is most useful for a user keyword search. +def get_by_keyword(substring): + to_search = get_items() + get_item_sets() + found = [] + for descriptable in to_search: + if substring.lower() in descriptable.description.lower(): + found.append(descriptable) + return found + def get_itemsets_by_item_id(item_id): item_sets = get_item_sets() item_set_contained_id = [] @@ -37,6 +65,12 @@ def get_itemsets_by_item_id(item_id): item_set_contained_id.append(item_set) return item_set_contained_id + # Alternate approach where items_sets are retrieved from queries, but are not in the desired format. + # item_sets = select(ItemSet).where(ItemSet.items.any(Item.id==item_id)) + # flask_item_sets = db.session.execute(item_sets).all() + # flask_item_set = flask_item_sets[0][0] - - \ No newline at end of file +def add_item(description, price, date_sold=None): + item = Item(description=description,price=price,date_sold=date_sold) + db.session.add(item) + db.session.commit() \ No newline at end of file diff --git a/unit_tests.py b/unit_tests.py index 04d006c..35245b1 100644 --- a/unit_tests.py +++ b/unit_tests.py @@ -34,10 +34,22 @@ class TestUtility(unittest.TestCase): self.assertEqual(contains_itemset == True, True) self.assertEqual(contains_itemset2 == True, True) - - + def test_get_by_keyword(self): + results = get_by_keyword("vase") + self.assertEqual(len(results) > 0, True) + def test_get_similar_items(self): + results = get_similar_items("Chinese Porcelaine Vases set") + is_search_too_wide = any("chinese" not in descriptable.description.lower() for descriptable in results) + + self.assertEqual(len(results) > 0 and not is_search_too_wide, True) + + def add_item_to_database(self): + item = ["Vase with golden trimming", 12] + add_item(item[0],item[1]) + added_item = Item.query.filter_by(description = "Vase with golden trimming").all() + self.assertEqual(item[0], added_item.description) if __name__ == "__main__": with app.app_context(): - unittest.main() \ No newline at end of file + unittest.main() -- GitLab