From 7d73e7e230f677697998d5cb77e0d0c6c3afb90f Mon Sep 17 00:00:00 2001 From: jal601 Date: Sun, 14 Jun 2015 19:46:47 -0700 Subject: [PATCH] Desserts app - end of week 6 --- README.md | 6 ++ desserts.db | Bin 0 -> 16384 bytes models.py | 196 +++++++++++++++++++++++++++++++++++++++-- templates/add.html | 87 ++++++++++++++---- templates/details.html | 62 +++++++++++++ templates/edit.html | 72 +++++++++++++++ templates/header.html | 21 +++++ templates/index.html | 56 ++++++++++-- templates/menu.html | 42 +++++++++ views.py | 126 ++++++++++++++++++++++++-- 10 files changed, 630 insertions(+), 38 deletions(-) create mode 100644 desserts.db create mode 100644 templates/details.html create mode 100644 templates/edit.html create mode 100644 templates/header.html create mode 100644 templates/menu.html diff --git a/README.md b/README.md index cc852f0..4d17f1a 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,9 @@ $ python app.py ``` Visit [http://localhost:5000/](http://localhost:5000/) in your browser to see the results. + +### Running Tests + +``` +python test_models.py +``` diff --git a/desserts.db b/desserts.db new file mode 100644 index 0000000000000000000000000000000000000000..b960a6d11247725a58f39ab0447a9b2e95a5a347 GIT binary patch literal 16384 zcmeI(&uSAv90%~3-DX1y+bKebP(sH;X$nzVL+jDT5^FWK8dIScWwM!=ZL`@pyRmqZ z6g(C@RXlm{C42<&0la(h2|ReynV48hVori!zYD|c{Qu2oH-~Jp5AK)!Sn@`w0ul29 znI)8xbM^NH;q6Zd%L`^@Tu-e`gV1Rwwb2tWV= z5P$##AaGs;dJ8m}nVX|Wmts+C$-0apsp3IqZB$CdYKd2iH_9bGsN}inlwapt+tt!$ zslvB+s(gF5T+Z|9R9gg+KP*-@ZWSxJmF49nQ`u3zC;83tPO)l=o@j;2m(g&7Jn!li zAD_->I^C;m-7Qx3_?^-o&-wMG>80MLoy?@u^yq0nPBX@&sWTCpnhD{V=J6qlC^`&P zeI#}n2*35K_Uh`-9gUqsJQJ}{BYj7A(zN;;=@t4L31qu#l6__)re?xM#s)CDlD>yu zn`_&9@z1X9xJ&7>H^gCe!d|k^>?3>6>g+AsWrg$dk;6VA009U<00Izz00bZa0SG_< z0)G(D-XiLSU-m0o|1t~kPkL94U5!Dwe<;0rz{%FD5iOPKIxEadVtpqy|5+p z&+T5*?{F^+4}59UNA?Bkka<1OJe$$ei6BAqdT}R1GbxA6C9PDfx{XFlUjH_T>0~yc z8}yJsctVBkDRPpw9iqcZ_2c0+)on@X&{tW@A*;GNij_F5Nu{3is2Qr*69+O$zq)3^ z*}<&HAOHafKmY;|fB*y_009U<00I#BF9lroj##Wi*eBM}84d_Q00Izz00bZa0SG_< z0uX=z1pb1+ik&3c>`K<%m+iLSkg79y-Crc#U4p+P|xIO5hS1srfr$a`& f4ksB7$x*Y)V%jzTKrnAw3_F 3000: + raise Exception("That's too many calories!") + + # get dessert from database + dessert = Dessert.query.filter_by(name=new_name).first() + + # users can only edit their own desserts + if user.id != dessert.user_id: + raise Exception("That's not your dessert!") + + dessert.price = new_price + dessert.calories = new_calories + db.session.commit() + + +def create_dessert(new_name, new_price, new_calories, new_user_id): # Create a dessert with the provided input. - # At first, we will trust the user. - # This line maps to line 16 above (the Dessert.__init__ method) - dessert = Dessert(new_name, new_price, new_calories) + # We need every piece of input to be provided. + + # Can you think of other ways to write this following check? + if new_name is None or new_price is None or new_calories is None or new_user_id is None: + raise Exception("Need name, price, calories, and user!") + + # They can also be empty strings if submitted from a form + if new_name == '' or new_price == '' or new_calories == '' or new_user_id == '': + raise Exception("Need name, price, calories, and user!") + + # check for duplicate items + name_check = Dessert.query.filter_by(name=new_name).first() + if name_check: + if new_name == name_check.name: + raise Exception("That dessert already exists!") + # check reasonable number of calories + if int(new_calories) < 100: + raise Exception("That's not enough calories!") + elif int(new_calories) > 3000: + raise Exception("That's too many calories!") + + # This line maps to line 16 above (the Dessert.__init__ method) + dessert = Dessert(new_name, new_price, new_calories, new_user_id) # Actually add this dessert to the database db.session.add(dessert) # Save all pending changes to the database - db.session.commit() - return dessert + try: + db.session.commit() + return dessert + except: + # If something went wrong, explicitly roll back the database + db.session.rollback() + + +def delete_dessert(user, id): + + # get dessert from database + dessert = Dessert.query.get(id) + + # users can only delete desserts they created + if user.id != dessert.user_id: + raise Exception("That's not your dessert!") + + if dessert: + # We store the name before deleting it, because we can't access it + # afterwards. + dessert_name = dessert.name + db.session.delete(dessert) + + try: + db.session.commit() + return "Dessert {} deleted".format(dessert_name) + except: + # If something went wrong, explicitly roll back the database + db.session.rollback() + return "Something went wrong" + else: + return "Dessert not found" if __name__ == "__main__": diff --git a/templates/add.html b/templates/add.html index e2d174a..7dd1883 100644 --- a/templates/add.html +++ b/templates/add.html @@ -1,27 +1,82 @@ -

Add Dessert

+{% include 'header.html' %} -{% if dessert %} + -

{{dessert.name}} successfully created! Add another below.

-

Back to Dessert List

+
-{% endif %} +
+ {% if user %} + Welcome {{ user }} | Logout | Home - -
+ {% else %} - - + Login | Home - - + {% endif %} +
- - +

Add Dessert

+ {% if dessert %} - - + - \ No newline at end of file + + + {% endif %} + + {% if error %} + + {% endif %} + + + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ + + + + + + +
+ + +
+ + \ No newline at end of file diff --git a/templates/details.html b/templates/details.html new file mode 100644 index 0000000..70e4023 --- /dev/null +++ b/templates/details.html @@ -0,0 +1,62 @@ +{% include 'header.html' %} + + + + + +
+ +
+ {% if user %} + + Welcome {{ user }} | Logout | Home + + {% else %} + + Login | Home + + {% endif %} +
+ +

Dessert Details

+ + {% if error %} + + {% endif %} + +

{{ dessert.name }}

+ +

Price: ${{ dessert.price }}

+ +

Calories: {{ dessert.calories }}

+ +

Calories per dollar: {{ dessert.calories_per_dollar() }}

+ + Edit {{ dessert.name }} + + + Delete {{ dessert.name }} + + + + + + \ No newline at end of file diff --git a/templates/edit.html b/templates/edit.html new file mode 100644 index 0000000..2d5664a --- /dev/null +++ b/templates/edit.html @@ -0,0 +1,72 @@ +{% include 'header.html' %} + + + +
+ +
+ {% if user %} + + Welcome {{ user }} | Logout | Home + + {% else %} + + Login | Home + + {% endif %} +
+ +

Edit Dessert

+ + + {% if error %} + + {% endif %} + + + + + +
+ +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + +
+ + + + + + + +
+ + +
+ + \ No newline at end of file diff --git a/templates/header.html b/templates/header.html new file mode 100644 index 0000000..2f60415 --- /dev/null +++ b/templates/header.html @@ -0,0 +1,21 @@ + + + Desserts + + + + + + + + + + + + + + + + + + diff --git a/templates/index.html b/templates/index.html index 8220939..aadea61 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,9 +1,51 @@ -

Dessert Menu

+{% include 'header.html' %} -
    -{% for dessert in desserts %} -
  • {{ dessert.name }} - ${{ dessert.price }}, {{ dessert.calories }} calories -{% endfor %} -
+ -Add Item \ No newline at end of file +
+ +
+ {% if user %} + + Welcome {{ user }} | Logout | Home + + {% else %} + + Login | Home + + {% endif %} +
+ + +

Log In

+ + {% if error %} + + {% endif %} + + +
+ +
+ + + + +
+ +
+ + + + +
+ + + +
+ + \ No newline at end of file diff --git a/templates/menu.html b/templates/menu.html new file mode 100644 index 0000000..e15c62b --- /dev/null +++ b/templates/menu.html @@ -0,0 +1,42 @@ +{% include 'header.html' %} + + + +
+ + + {% if user %} +
+ Welcome {{ user }} | Logout | Home +
+

{{ user }}'s Dessert Menu

+ + {% else %} +
+ Login | Home +
+

Dessert Menu

+ + {% endif %} + + {% if message %} + + {% endif %} + + +
    + {% for dessert in desserts %} +
  • {{ dessert.name }} - ${{ dessert.price }}, {{ dessert.calories }} calories
  • + {% endfor %} +
+ + Add Item + + +
+ + \ No newline at end of file diff --git a/views.py b/views.py index 4e42ae5..0878751 100644 --- a/views.py +++ b/views.py @@ -1,22 +1,95 @@ -from flask import render_template, request +from flask import render_template, request, session -from models import Dessert, create_dessert +from models import Dessert, create_dessert, delete_dessert, edit_dessert, login, get_logged_in_user from app import app -@app.route('/') +@app.route('/', methods=["GET", "POST"]) def index(): - desserts = Dessert.query.all() + if request.method == 'GET': + # if user is logged in, show only their desserts + if "username" in session: + user = get_logged_in_user() + desserts = Dessert.query.filter_by(user_id=user.id).all() + return render_template("menu.html", user=user.name, desserts=desserts) + # if no user is logged in, show all desserts + else: + desserts = Dessert.query.all() + return render_template("menu.html", user=None, desserts=desserts) + + # post method is user attempting to log in + # get username and password from posted login form + username = request.form.get('username_field') + password = request.form.get('password_field') + + try: + # login() from models.py + login(username, password) + except Exception as e: + return render_template("index.html", error=e.message) + + # once logged in, show only user's desserts + user = get_logged_in_user() + desserts = Dessert.query.filter_by(user_id=user.id).all() + return render_template("menu.html", user=user.name, desserts=desserts) + + +@app.route("/login") +def login_user(): + return render_template("index.html") + + +@app.route("/menu") +def menu(): + user = get_logged_in_user() - return render_template('index.html', desserts=desserts) + desserts = Dessert.query.filter_by(user_id=user.id).all() + return render_template('menu.html', user=user.name, desserts=desserts) + + +@app.route("/logout") +def logout(): + session.pop('username',None) + return index() + +@app.route('/edit/', methods=['GET', 'POST']) +def edit(id): + + dessert = Dessert.query.get(id) + + if request.method == 'GET': + if "username" in session: + user = get_logged_in_user() + return render_template('edit.html', user=user.name, dessert=dessert) + else: + return render_template('details.html', user=None, dessert=dessert, error="You must be logged in to edit a dessert.") + + user = get_logged_in_user() + + dessert_name = dessert.name + dessert_price = request.form.get('price_field') + dessert_cals = request.form.get('cals_field') + + try: + dessert = edit_dessert(user, dessert_name, dessert_price, dessert_cals) + desserts = Dessert.query.filter_by(user_id=user.id).all() + return render_template('menu.html', user=user.name, desserts=desserts) + except Exception as e: + return render_template('edit.html', user=user.name, dessert=dessert, error=e.message) + @app.route('/add', methods=['GET', 'POST']) def add(): if request.method == 'GET': - return render_template('add.html') + if "username" in session: + user = get_logged_in_user() + return render_template('add.html', user=user.name) + else: + desserts = Dessert.query.all() + return render_template("menu.html", user=None, desserts=desserts, message="You must be logged in to add a dessert.") # Because we 'returned' for a 'GET', if we get to this next bit, we must # have received a POST @@ -25,9 +98,46 @@ def add(): # The values on the right, inside get(), correspond to the 'name' # values in the HTML form that was submitted. + user = get_logged_in_user() dessert_name = request.form.get('name_field') dessert_price = request.form.get('price_field') dessert_cals = request.form.get('cals_field') + + # Now we are checking the input in create_dessert, we need to handle + # the Exception that might happen here. + + # Wrap the thing we're trying to do in a 'try' block: + try: + dessert = create_dessert(dessert_name, dessert_price, dessert_cals, user.id) + return render_template('add.html', user=user.name, dessert=dessert) + except Exception as e: + # Oh no, something went wrong! + # We can access the error message via e.message: + return render_template('add.html', error=e.message) + + +@app.route('/desserts/') +def view_dessert(id): + + dessert = Dessert.query.get(id) + + if "username" in session: + user = get_logged_in_user() + return render_template('details.html', user=user.name, dessert=dessert) + else: + return render_template('details.html', user=None, dessert=dessert) + + +@app.route('/delete/') +def delete(id): + + if "username" in session: + user = get_logged_in_user() + message = delete_dessert(user, id) + return index() + else: + dessert = Dessert.query.get(id) + return render_template('details.html', user=None, dessert=dessert, error="You must be logged in to delete a dessert.") + - dessert = create_dessert(dessert_name, dessert_price, dessert_cals) - return render_template('add.html', dessert=dessert) +app.secret_key = "desserts_app!"