summaryrefslogtreecommitdiff
path: root/application.py
diff options
context:
space:
mode:
Diffstat (limited to 'application.py')
-rw-r--r--application.py261
1 files changed, 261 insertions, 0 deletions
diff --git a/application.py b/application.py
new file mode 100644
index 0000000..54c7e6e
--- /dev/null
+++ b/application.py
@@ -0,0 +1,261 @@
+import os
+
+from cs50 import SQL
+from flask import Flask, flash, redirect, render_template, request, session
+from flask_session import Session
+from tempfile import mkdtemp
+from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
+from werkzeug.security import check_password_hash, generate_password_hash
+
+from helpers import apology, deal_finder, login_required, passw_requirements, usd
+
+# Configure application
+app = Flask(__name__)
+
+# Ensure templates are auto-reloaded
+app.config["TEMPLATES_AUTO_RELOAD"] = True
+
+# Ensure responses aren't cached, allows smoother implementation of development-changes
+@app.after_request
+def after_request(response):
+ response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
+ response.headers["Expires"] = 0
+ response.headers["Pragma"] = "no-cache"
+ return response
+
+# Custom filter
+app.jinja_env.filters["usd"] = usd
+
+# Configure session to use filesystem (instead of signed cookies, to avoid multiple cookies in one browser)
+app.config["SESSION_FILE_DIR"] = mkdtemp()
+app.config["SESSION_PERMANENT"] = False
+app.config["SESSION_TYPE"] = "filesystem"
+Session(app)
+
+# Configure the SQLite database
+db = SQL("sqlite:///spending.db")
+
+
+@app.route("/")
+def home():
+ """Home page with different versions for visiting guests & logged-in users """
+
+ # When a non-registered guest visits the website:
+ if not session:
+ return render_template("welcome.html")
+
+ # For a logged-in user:
+ else:
+ user_id = session["user_id"]
+
+ # Getting username & budget
+ user_info = db.execute("SELECT * FROM users WHERE id = ?", user_id)
+ username = user_info[0]["username"]
+ cash = user_info[0]["cash"]
+
+ # Getting total amount of money spent in each category
+ amounts = []
+ categories = ['%Rent', '%Maintenance', '%Food', '%Family', '%Pets', '%Clothes', '%Hobbies', '%Investing', '%Other']
+
+ for element in categories:
+ result = db.execute("SELECT SUM(amount) FROM expenses WHERE user_id = ? AND category LIKE ?", user_id, element)[0]["SUM(amount)"]
+ if result == None:
+ result = 0
+ amounts.append(result)
+
+ return render_template("home.html", name=username, cash=cash, rent=amounts[0], maintain=amounts[1], food=amounts[2], family=amounts[3],
+ pets=amounts[4], clothes=amounts[5], hobbies=amounts[6], invest=amounts[7], other=amounts[8])
+
+
+@app.route("/account", methods=["GET", "POST"])
+@login_required
+def account():
+ """ View user data & replace passsword """
+
+ user_id = session["user_id"]
+ user_info = db.execute("SELECT * FROM users WHERE id = ?", user_id)
+ username = user_info[0]["username"]
+ cash = user_info[0]["cash"]
+
+ if request.method == "GET":
+ return render_template("account.html", name=username, budget=cash)
+
+ else:
+ # allowing the user to set a new password for their account
+ pw_new = request.form.get("pw_new")
+ if not check_password_hash(user_info[0]["hash"], request.form.get("pw_old")):
+ return apology("Your old password was not correct", 400)
+ elif pw_new != request.form.get("confirmation"):
+ return apology("New Password and Confirm New Password did not match", 400)
+ elif passw_requirements(pw_new) != "valid":
+ return apology("Password must be between 6 and 25 characters long and contain at least one number and one letter", 400)
+
+ # replace password in the db if all requirements were met
+ db.execute("UPDATE users SET hash = ? WHERE username = ?", generate_password_hash(pw_new), username)
+ flash("You successfully changed your password!")
+ return render_template("/account.html", name=username, budget=cash)
+
+
+@app.route("/bargain", methods=["GET", "POST"])
+@login_required
+def bargain():
+ """Search for & display cheapest deals for a user-specified item """
+
+ # display search form
+ if request.method == "GET":
+ return render_template("bargain.html")
+ # display results collected & sorted by web scraping helper function
+ else:
+ item = request.form.get("product-search")
+ deals = deal_finder(item)
+
+ return render_template("bargain.html", deals=deals)
+
+
+@app.route("/budget", methods=["GET", "POST"])
+@login_required
+def budget():
+ """ View & set budget """
+
+ user_id = session["user_id"]
+ budget = db.execute("SELECT cash FROM users WHERE id = ?", user_id)[0]["cash"]
+
+ # view current budget
+ if request.method == "GET":
+ return render_template("budget.html", budget=budget)
+
+ # update budget
+ else:
+ added_budget = request.form.get("add_budget")
+ new_budget = float(budget) + float(added_budget)
+ db.execute("UPDATE users SET cash = ? WHERE id = ?", new_budget, user_id)
+
+ flash("Budget updated!")
+ return render_template("budget.html", budget=new_budget)
+
+
+@app.route("/history")
+@login_required
+def history():
+ """ Show record of all previous expenses """
+
+ user_id = session["user_id"]
+ record = db.execute("SELECT * FROM expenses WHERE user_id = ? ORDER BY date", user_id)
+ return render_template("history.html", record=record)
+
+
+@app.route("/login", methods=["GET", "POST"])
+def login():
+ """Log user in"""
+
+ # Forget any user_id
+ session.clear()
+
+ # User reached the route via GET:
+ if request.method == "GET":
+ return render_template("login.html")
+
+ # User submitted via POST to log in:
+ else:
+ # Ensure username was submitted
+ if not request.form.get("username"):
+ return apology("must provide username", 403)
+
+ # Ensure password was submitted
+ elif not request.form.get("password"):
+ return apology("must provide password", 403)
+
+ # Query database for username
+ rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
+
+ # Ensure username exists and password is correct
+ if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
+ return apology("invalid username and/or password", 403)
+
+ # Remember which user has logged in
+ session["user_id"] = rows[0]["id"]
+
+ # Redirect user to home page
+ return redirect("/")
+
+
+@app.route("/logout")
+def logout():
+ """Log user out"""
+
+ # Forget any user_id
+ session.clear()
+
+ # Redirect user to login form
+ return redirect("/")
+
+
+@app.route("/register", methods=["GET", "POST"])
+def register():
+ """ Register a new user account """
+
+ # If the user visits this route, display the form to sign up for a new account
+ if request.method == "GET":
+ return render_template("register.html")
+
+ # If the user submits their registration with POST
+ else:
+ # Get the user's name & password from the form
+ reg_name = request.form.get("username")
+ reg_passw = request.form.get("password")
+
+ # Checking if the user failed to provide a username & password, if the name is already taken or if the confirmed password was different
+ if not reg_name:
+ return apology("Please enter a username", 400)
+ elif len(db.execute("SELECT * FROM users WHERE username = ?", reg_name)) != 0:
+ return apology("Username is already taken", 400)
+ elif not reg_passw:
+ return apology("Please choose a unique password", 400)
+ elif reg_passw != request.form.get("confirmation"):
+ return apology("Password and Confirm Password did not match", 400)
+ # Special check with a helper function for password safety
+ elif passw_requirements(reg_passw) != "valid":
+ return apology("Password must be between 6 and 25 characters long and contain at least one number and one letter", 400)
+
+ # Otherwise register the new user into the database (with a hashed password for extra security) and redirect them to the main page
+ db.execute("INSERT INTO users (username, hash) VALUES (?, ?)", reg_name, generate_password_hash(reg_passw))
+ return redirect("/login")
+
+
+@app.route("/tracker", methods=["POST", "GET"])
+@login_required
+def tracker():
+ """ Add new expenses """
+
+ # Display form to add a new expense
+ if request.method == "GET":
+ return render_template("tracker.html")
+ # When submitting a new expense:
+ else:
+ user_id = session["user_id"]
+ budget = db.execute("SELECT cash FROM users WHERE id = ?", user_id)[0]["cash"]
+
+ # ternary condition if the user does not give an optional comment on the expense
+ comment = "Unspecified" if not request.form.get("comment") else request.form.get("comment")
+ category, amount, date = request.form.get("category"), request.form.get("amount", type=float), request.form.get("date")
+
+ # subtract expense from budget
+ updated_budget = float(budget) - amount
+ db.execute("UPDATE users SET cash = ? WHERE id = ?", updated_budget, user_id)
+
+ # update table "expenses" in database for use in history.html
+ db.execute("INSERT INTO expenses (user_id, comment, category, amount, date) VALUES (?, ?, ?, ?, ?)",
+ user_id, comment, category, amount, date)
+ return redirect("/")
+
+
+# Handle errors
+def errorhandler(e):
+ if not isinstance(e, HTTPException):
+ e = InternalServerError()
+ return apology(e.name, e.code)
+
+
+# Listen for errors
+for code in default_exceptions:
+ app.errorhandler(code)(errorhandler)