Skip to content
Permalink
Browse files
feature 5 complete
  • Loading branch information
leesj4 committed Nov 23, 2021
1 parent 725f8be commit 8d04f1bb8d6d21b5e3cc12d56896f047c966a3f3
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 58 deletions.
@@ -7,33 +7,34 @@ db = SQLAlchemy() # defines a new database
DB_NAME = "database.db"

def app(): # creates and initialises a flask app
application = Flask(__name__) # initializes the app
application.secret_key = "password" # encrypts cookies and session data for the website

app = Flask(__name__) # initializes the app
app.secret_key = "password" # encrypts cookies and session data for the website

# stores sqlite database in the current directory
application.config["SQLALCHEMY_DATABASE_URI"] = f'sqlite:///{DB_NAME}'
db.init_app(application) # initialises database with the application
app.config["SQLALCHEMY_DATABASE_URI"] = f'sqlite:///{DB_NAME}'
db.init_app(app) # initialises database with the app

# sets where uploaded images are saved
application.config["IMAGE_UPLOADS"] = "/home/codio/workspace/auction/static/images"
app.config["IMAGE_UPLOADS"] = "/home/codio/workspace/auction/static/images"

from .routes import routes # imports the routes from the route file
application.register_blueprint(routes, url_prefix="/")
app.register_blueprint(routes, url_prefix="/")

from .database_models import User, Item # imports the database classes from the database_models file
create_db(application)
from .database_models import Item, User # imports the database classes from the database_models file
create_db(app)

login_manager = LoginManager()
login_manager.login_view = "routes.login"
login_manager.init_app(application)
login_manager.init_app(app)

@login_manager.user_loader
def load_user(id): # tells user how to find a user
return User.query.get(int(id)) # looks for primary key, checking against the id passed

return application
return app

def create_db(application):
def create_db(app):
# checks if the database exists already, creating it if not
if not path.exists("auction/" + DB_NAME): # determines if database.db is a path in the directory
db.create_all(app=application) # creates the database
#if not path.exists("auction/" + DB_NAME): # determines if database.db is a path in the directory
db.create_all(app=app) # creates the database
BIN +0 Bytes (100%) auction/database.db
Binary file not shown.
@@ -6,13 +6,15 @@ class Item(db.Model): # class that stores info about each item on the system
item_id = db.Column(db.Integer, primary_key=True) # primary key id number
item_name = db.Column(db.String(150))
description = db.Column(db.String(500))
start_bid = db.Column(db.Integer)
price = db.Column(db.Integer)
current_bid = db.Column(db.Integer, default=0)
item_image = db.Column(db.Text())

date_only = db.Column(db.Date, server_default=func.date()) # just the date the item was made
date = db.Column(db.DateTime(timezone=True), server_default=func.now()) # defaults the datetime to now
date = db.Column(db.DateTime, server_default=func.now()) # defaults the datetime to now
auction_end = db.Column(db.DateTime)

sold = db.Column(db.Boolean(), default=False)
sold_to = db.Column(db.Integer, default=0)
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # must pass a valid id of an existing user to this object, 1 to many

class User(db.Model, UserMixin): # creates each user in the database
@@ -21,6 +23,4 @@ class User(db.Model, UserMixin): # creates each user in the database
phone = db.Column(db.Integer)
password = db.Column(db.String(150))
username = db.Column(db.String(150))
items = db.relationship('Item') # a list of all the items a user owns

#current_bid = db.Column(db.Integer)
items = db.relationship('Item') # a list of all the items a user owns
@@ -6,6 +6,8 @@ from werkzeug.utils import secure_filename # allows me to change the uploaded fi
from . import db
from flask_login import login_user, login_required, logout_user, current_user # login management modules
import os
from datetime import datetime, timedelta
import smtplib


routes = Blueprint("routes", __name__) # blueprint allows splitting up routes over several files
@@ -14,17 +16,20 @@ routes = Blueprint("routes", __name__) # blueprint allows splitting up routes ov
UPLOAD_FOLDER = "/home/codio/workspace/auction/static/images"
ALLOWED_EXTENSIONS = set(['jpg'])


@routes.route("/", methods=['GET', 'POST'])
@login_required # requires the user to be logged in to access
#@login_required # requires the user to be logged in to access
def home_page():
items = Item.query.all() # gets all rows of items
users = User.query.all() # gets all rows of items
return render_template("home_page.html", items=items, user=current_user, users=users, extensions=ALLOWED_EXTENSIONS) # sends items to the html and keeps track of login status
dt = datetime.now()
return render_template("home_page.html", items=items, user=current_user, users=users, dt=dt) # sends items to the html and keeps track of login status


@routes.route("/login", methods=['GET', 'POST'])
def login(): # users can login to the website using an existing account
if request.method == 'POST':
email = request.form.get("email")
email = request.form.get("email") # gets values from form
password = request.form.get("password")

user = User.query.filter_by(email = email).first() # checks all the users with the email entered
@@ -34,12 +39,13 @@ def login(): # users can login to the website using an existing account
login_user(user, remember=True) # remembers the user is logged while the web server is using
return redirect(url_for('routes.home_page')) # redirects the user to the home page
else:
flash("Password incorrect", category="warning")
flash("Password incorrect", category="warning") # shows a warning
else:
flash("Account does not exist", category="warning")

return render_template("login.html", user=current_user)


@routes.route("/register", methods=['GET', 'POST'])
def register(): # users can register for a new account, saves new user to the database
if request.method == 'POST':
@@ -71,18 +77,20 @@ def register(): # users can register for a new account, saves new user to the da

return render_template("register.html", user=current_user)


@routes.route("/logout")
@login_required
def logout():
def logout(): # allows users to logout
logout_user() # remembers that the user has logged out
flash("You are now logged out.", category="success")
return redirect(url_for("routes.login")) # redirects user back to the login page
return redirect(url_for("routes.home_page")) # redirects user back to the login page


def allowed_file(filename):
def allowed_file(filename): # checks the image uploaded is of the write extension
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

# ********adds to the database even with the wrong extension, since it needs to commit to get user id*************

@routes.route("/sell", methods=['GET', 'POST'])
@login_required
def sell(): # user can enter information about an item they would like to add to the home page in order to sell
@@ -91,10 +99,14 @@ def sell(): # user can enter information about an item they would like to add to
item_name = item_name.capitalize() # capitalizes name
description = request.form.get("description")
description = description.capitalize()
start_bid = request.form.get("start_bid")

price = request.form.get("price")

dt = datetime.now()
td = timedelta(days=3)
auction_end = dt + td # gets the auction end time, set to 3 days in the future

#creates a new Item object
new_item = Item(item_name=item_name, description=description, start_bid=start_bid, user_id=current_user.id)
new_item = Item(item_name=item_name, description=description, price=price, auction_end=auction_end, user_id=current_user.id)
db.session.add(new_item)
db.session.commit() # commits the new item to the database

@@ -120,13 +132,16 @@ def sell(): # user can enter information about an item they would like to add to

return render_template("sell.html", user=current_user)


@routes.route("/myitems", methods=['GET', 'POST'])
@login_required
def my_items(): # shows items the user has for put sale
length = len(current_user.items) # how many items the user has for sale
dt = datetime.now() # gets the current date and time

if length > 0:
return render_template("my_items.html", user=current_user) # shows the user their items
else:
return render_template("my_items.html", user=current_user, dt=dt) # shows the user their items
else: # if the user has no items for sale
return render_template("no_items.html", user=current_user) # tells the user they have nothing for sale


@@ -136,25 +151,38 @@ def item_info(item_id): # route shows users a dedicated page for each item, with
user = current_user
users = User.query.all() # gets all users
item = Item.query.filter_by(item_id=item_id).first() # finds the item the user clicked ons information
return render_template("item.html", user=current_user, users=users, item=item)
dt = datetime.now()

# if statements show different html depending on if the item is marked as sold, expired, or still for sale
if item.sold == True:
return render_template("item_sold.html", user=current_user, users=users, item=item)

elif dt > item.auction_end:
return render_template("item_expired.html", user=current_user, users=users, item=item)

elif item.sold == False:
return render_template("item.html", user=current_user, users=users, item=item)


@routes.route("/delete/<int:item_id>", methods=['GET', 'POST'])
@login_required
def item_delete(item_id): # allows a user to delete their item from the website
user=current_user
user = current_user
item = Item.query.filter_by(item_id=item_id).first() # finds the item they clicked on

# prevents another user deleting items they don't own
if item.user_id == user.id: # if the current user is the items owner
Item.query.filter_by(item_id=item_id).delete() # deletes the item
db.session.commit()
flash("Item deleted", category="success")
return redirect(url_for('routes.my_items'))

else:
flash("Unauthorised user", category="warning") # tells the user if they try to delete someone elses item

return redirect(url_for('routes.home_page')) # redirects to the home page


@routes.route("/sold/<int:item_id>", methods=['GET', 'POST'])
@login_required
def sold(item_id): # allows a user to mark an item as sold
@@ -167,6 +195,54 @@ def sold(item_id): # allows a user to mark an item as sold
db.session.commit()

else:
flash("Unauthorised user", category="warning") # tells the user if they try to delete someone elses item
flash("Unauthorised user", category="warning") # tells the user if they try to mark someone elses item
return redirect(url_for('routes.home_page')) # redirects to the home page

winner = User.query.filter_by(id=item.sold_to).first()
if item.sold_to > 0:
notify_winner(winner, item) # sends an email to the winner
return redirect(url_for('routes.my_items')) # redirects to the home page


@routes.route("/bid/<int:item_id>", methods=['GET', 'POST'])
@login_required
def bid(item_id): # allows a user to bid on an item
user = current_user
item = Item.query.filter_by(item_id=item_id).first()

if item.sold_to == 0: # if this is the first bid
new_bid = request.form.get("new_bid") # gets the bid from the form
item.current_bid = new_bid # sets the values in the database = to the new values
item.sold_to = user.id
db.session.commit() # updates the database

else: # if the item has been bid on before
outbid_user = User.query.filter_by(id=item.sold_to).first()
notify_outbid(outbid_user, item) # emails the person being outbid
new_bid = request.form.get("new_bid") # gets the bid from the form
item.current_bid = new_bid # sets the values in the database = to the new values
item.sold_to = user.id
db.session.commit()

return redirect(url_for('routes.item_info', item_id=item.item_id)) # redirects to the home page


def notify_winner(winner, item): # emails a user if they win an auction
message = "you have won the "+item.item_name+" auction!!" # not allowing me to send an email with a link in it, coming in blank
winner_email = winner.email
send_email("pythonflaskemail@gmail.com", "josephlees79@gmail.com", message) # changed reciever to reduce spam when testing


def notify_outbid(user, item): # emails a user if they are outbid
message = "you have been outbid on the "+item.item_name+" auction!" # wont allow me to put a link in the email
outbid_email = user.email
send_email("pythonflaskemail@gmail.com", "josephlees79@gmail.com", message)


return redirect(url_for('routes.my_items')) # redirects to the home page
def send_email(sender, reciever, message): # sends the email using smtplib module
password = "Python150*"

server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(sender, password)
server.sendmail(sender, "josephlees79@gmail.com", message)
BIN -616 KB (2.2%) auction/static/images/2.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN +37.7 KB (370%) auction/static/images/4.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -8,28 +8,38 @@
<!-- displays a list of all the items for sale on the website -->
<div class="row">
{% for item in items %} <!-- for loop prints information about each item -->
{% if item.sold == False %}
<div class="col-12 col-md-6 col-lg-4 mb-4"> <!-- moves cards underneath eachother when tab is small -->
{% if item.sold == False %} <!-- only shows items that are not sold or expired -->
{% if dt < item.auction_end %}
<div class="col-12 col-md-6 col-lg-4 mb-4"> <!-- moves cards underneath eachother when tab is small -->

<!-- creates a card that displays a picture and info about the item-->
<div class="card" style="width: 18rem;">
<!-- creates a card that displays a picture and info about the item-->
<div class="card" style="width: 18rem;">

<!-- gets the relavent image from the static/images folder under the item_id.jpg -->
<img class="card-img-top border-bottom" src="/static/images/{{item.item_id}}.jpg">
<div class="card-body">
<h5 class="card-title">{{item.item_name}}</h5>
<main class="card-text">£{{item.start_bid}}</main>
<!-- gets the relavent image from the static/images folder under the item_id.jpg -->
<img class="card-img-top border-bottom" src="/static/images/{{item.item_id}}.jpg">
<div class="card-body">
<h5 class="card-title">{{item.item_name}}</h5>

{% for user in users %}
{% if user.id == item.user_id%}
<p><small class="card-text text-muted">Added by {{user.username}} on {{item.date_only}}</small></p>
<!-- shows different variables depending on if the item has been bid on before -->
{% if item.current_bid == 0 %}
<main class="card-text">£{{item.price}}</main>
{% else%}
<main class="card-text">Current bid: £{{item.current_bid}}</main>
{% endif %}
{% endfor %}

<a href="{{ url_for('routes.item_info', item_id=item.item_id) }}">More information</a>
<!-- shows who the seller is -->
{% for user in users %}
{% if user.id == item.user_id%}
<p><small class="card-text text-muted">Added by {{user.username}} on {{item.date.strftime("%d/%m/%Y")}}</small></p>
{% endif %}
{% endfor %}

<!-- link to the specific item page -->
<a href="{{ url_for('routes.item_info', item_id=item.item_id) }}">More information</a>
</div>
</div>
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
</div>
@@ -24,8 +24,8 @@ but just displays the items the current user has up for sale -->

<!-- shows a sold badge if the item has been marked as sold -->
<h5 class="card-title">{{item.item_name}} <span class="badge bg-success text-light">Sold</span></h5>
<main class="card-text">£{{item.start_bid}}</main>
<p><small class="card-text text-muted">Added on {{item.date_only}}</small></p>
<main class="card-text">Sold for: £{{item.current_bid}}</main>
<p><small class="card-text text-muted">Added on {{item.date.strftime("%d/%m/%Y")}}</small></p>

<!-- dropdown with user actions regarding the item -->
<div class="dropdown">
@@ -38,12 +38,30 @@ but just displays the items the current user has up for sale -->
<li><a class="dropdown-item text-danger" href="{{ url_for('routes.item_delete', item_id=item.item_id)}}">Delete item</a></li>
</ul>
</div>

{% elif dt > item.auction_end %}
<!-- shows an expired badge if the auction has expired -->
<h5 class="card-title">{{item.item_name}} <span class="badge bg-danger text-light">Expired</span></h5>
{% if item.current_bid > 0 %}
<p class="card-text">Mark as sold to continue</p>
<a class="btn btn-danger" href="{{ url_for('routes.sold', item_id=item.item_id)}}" role="button">Mark as sold</a>

{% elif item.current_bid == 0 %}
<p class="card-text">Item did not sell</p>
<a class="btn btn-danger" href="{{ url_for('routes.item_delete', item_id=item.item_id)}}" role="button">Delete item</a>
{% endif %}

{% else %}

<h5 class="card-title">{{item.item_name}}</h5>
<main class="card-text">£{{item.start_bid}}</main>
<p><small class="card-text text-muted">Added on {{item.date_only}}</small></p>

{% if item.current_bid == 0 %}
<main class="card-text">£{{item.price}}</main>
{% else%}
<main class="card-text">Current bid: £{{item.current_bid}}</main>
{% endif %}

<p><small class="card-text text-muted">Added on {{item.date.strftime("%d/%m/%Y")}}</small></p>

<!-- dropdown with user actions regarding the item -->
<div class="dropdown">

0 comments on commit 8d04f1b

Please sign in to comment.