diff --git a/.envrc b/.envrc deleted file mode 100644 index 4d3e59f..0000000 --- a/.envrc +++ /dev/null @@ -1,2 +0,0 @@ -use flake -export FLASK_APP=app \ No newline at end of file diff --git a/app/forms.py b/app/forms.py index 452ad00..5cf3bcd 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,30 +1,65 @@ +from typing import Any from flask_wtf import FlaskForm -from wtforms import StringField, SubmitField, IntegerField, HiddenField +from wtforms import ( + StringField, + SubmitField, + IntegerField, + HiddenField, + FloatField, + URLField, +) + from wtforms.validators import DataRequired + class NewWishlist(FlaskForm): - title = StringField("Title:", validators=[DataRequired()]) - description = StringField("Description:", validators=[DataRequired()]) - submit = SubmitField("Submit") + title = StringField("Title:", validators=[DataRequired()]) + description = StringField("Description:", validators=[DataRequired()]) + submit = SubmitField("Submit") + # Each submit needs a different page fot it to work on the same page. class DeleteWishlist(FlaskForm): - wl_del_submit = SubmitField("Delete wishlist") + wl_del_submit = SubmitField("Delete wishlist") + class EditWishlistInfo(FlaskForm): - title = StringField("Title:", validators=[DataRequired()]) - description = StringField("Description:", validators=[DataRequired()]) - wl_edit_submit = SubmitField("Submit") + title = StringField("Title", validators=[DataRequired()]) + description = StringField("Description", validators=[DataRequired()]) + wl_edit_submit = SubmitField("Submit") + class ResetWishlistUrls(FlaskForm): - wl_reset_submit = SubmitField("Reset urls") + wl_reset_submit = SubmitField("Reset urls") + class NewItem(FlaskForm): - title = StringField("Title:", validators=[DataRequired()]) - description = StringField("Description:", validators=[DataRequired()]) - price = IntegerField("Price:", validators=[DataRequired()]) - it_new_submit = SubmitField("Submit") + title = StringField("Title", validators=[DataRequired()]) + description = StringField("Description", validators=[DataRequired()]) + price = FloatField("Price", validators=[DataRequired()]) + url = URLField("Url", validators=[DataRequired()]) + image = URLField("Image url", validators=[DataRequired()]) + it_new_submit = SubmitField("Submit") + + +class CheckItem(FlaskForm): + num = HiddenField() + class DeleteItem(FlaskForm): - index = HiddenField() - it_del_submit = SubmitField("Delete item") + index = HiddenField() + it_del_submit = SubmitField("Delete item") + + +def parseHiddenIndex(field: HiddenField, array: list[Any]) -> int | None: + try: + if field.data == None or field.data == "": + raise ValueError() + + index = int(field.data) + if index > len(array): + raise ValueError() + + return index - 1 + except ValueError: + return None diff --git a/app/models.py b/app/models.py index 00ecd18..33228ee 100644 --- a/app/models.py +++ b/app/models.py @@ -1,35 +1,42 @@ -from typing import List from app import db -from sqlalchemy.orm import Mapped, mapped_column, RelationshipProperty -from sqlalchemy import Uuid, String, Text -from uuid import uuid4 as uuid +from sqlalchemy.orm import Mapped, mapped_column, RelationshipProperty, relationship +from sqlalchemy import Uuid, String, Text, String, ForeignKey +from uuid import uuid4 as uuid, UUID + class Item(db.Model): - def __init__(self, title: str, description: str, price: float): - self.wishlist_id = None - self.title = title - self.description = description - self.price = price - self.bought = False + def __init__( + self, title: str, description: str, price: float, url: str, image: str + ): + self.bought = False + self.wishlist_id = None + self.title = title + self.description = description + self.price = price + self.url = url + self.image = image + + id: Mapped[int] = mapped_column(primary_key=True) + wishlist_id: Mapped[int | None] = mapped_column(ForeignKey("wishlist.id")) + title: Mapped[str] = mapped_column(String(250)) + description: Mapped[str] = mapped_column(Text) + price: Mapped[float] = mapped_column() + url: Mapped[str] = mapped_column(Text) + image: Mapped[str] = mapped_column(Text) + bought: Mapped[bool] = mapped_column() - id: Mapped[int] = mapped_column(primary_key=True) - wishlist_id: Mapped[int | None] = mapped_column(db.ForeignKey("wishlist.id")) - title: Mapped[str] = mapped_column(db.String(250)) - description: Mapped[str] = mapped_column(db.Text) - price: Mapped[float] = mapped_column() - bought: Mapped[bool] = mapped_column() class Wishlist(db.Model): - def __init__(self, title: str, description: str): - self.editId = uuid() - self.viewId = uuid() - self.title = title - self.description = description + def __init__(self, title: str, description: str): + self.editId = uuid() + self.viewId = uuid() + self.title = title + self.description = description - id: Mapped[int] = mapped_column(primary_key=True) - editId: Mapped[Uuid] = mapped_column(Uuid) - viewId: Mapped[Uuid] = mapped_column(Uuid) - title: Mapped[str] = mapped_column(String(250)) - description: Mapped[str] = mapped_column(Text) + id: Mapped[int] = mapped_column(primary_key=True) + editId: Mapped[UUID] = mapped_column(Uuid) + viewId: Mapped[UUID] = mapped_column(Uuid) + title: Mapped[str] = mapped_column(String(250)) + description: Mapped[str] = mapped_column(Text) - items: Mapped[List["Item"]] = db.relationship("Item", backref="post") \ No newline at end of file + items: Mapped[list["Item"]] = relationship("Item", backref="post") diff --git a/app/templates/edit.html b/app/templates/edit.html index e3cd676..1ffc299 100644 --- a/app/templates/edit.html +++ b/app/templates/edit.html @@ -7,19 +7,22 @@ {{ form_wl_editinfo.title.label }} {{ form_wl_editinfo.title() }} + {{ form_wl_editinfo.description.label }} {{ form_wl_editinfo.description() }} + {{ form_wl_editinfo.wl_edit_submit() }} +

Reset urls

@@ -29,41 +32,67 @@ {{ form_wl_reseturls.wl_reset_submit() }} -
-

Delete wishlist

-
- {{ form_wl_delete.hidden_tag() }} - {{ form_wl_delete.wl_del_submit() }} -
-

New item

{{ form_it_new.hidden_tag() }} + {{ form_it_new.title.label }} {{ form_it_new.title() }} + {{ form_it_new.description.label }} {{ form_it_new.description() }} + {{ form_it_new.price.label }} {{ form_it_new.price() }} + + {{ form_it_new.url.label }} + {{ form_it_new.url() }} + + + {{ form_it_new.image.label }} + {{ form_it_new.image() }} + + {{ form_it_new.it_new_submit() }}

Delete items

+ +{% if wishlist.items|length == 0 %} +

No items yet

+{% endif %} \ No newline at end of file + + +
+

Delete wishlist

+
+ {{ form_wl_delete.hidden_tag() }} + {{ form_wl_delete.wl_del_submit() }} +
+ + diff --git a/app/templates/new.html b/app/templates/new.html index 5667c9e..1979102 100644 --- a/app/templates/new.html +++ b/app/templates/new.html @@ -1,4 +1,4 @@ -
+ {{ form.hidden_tag() }} {{ form.title.label }} @@ -8,4 +8,4 @@ {{ form.description() }} {{ form.submit() }} -
\ No newline at end of file + diff --git a/app/templates/view.html b/app/templates/view.html index 448bfb0..5df40ec 100644 --- a/app/templates/view.html +++ b/app/templates/view.html @@ -4,7 +4,53 @@ edit \ No newline at end of file + {% if wishlist.items|length == 0 %} +

No items yet

+ {% endif %} + {% for item in wishlist.items %} +
  • +
    + {{ form.csrf_token }} + {{ form.num(value = loop.index) }} + + + {{ item.title }}: {{ item.description }} +
    +
  • + {% endfor %} + + + +

    Are you sure you bought this product. You won't be able to undo this.

    +
    + + +
    +
    + + diff --git a/app/views.py b/app/views.py index ce6d1b4..c168c6a 100644 --- a/app/views.py +++ b/app/views.py @@ -1,4 +1,4 @@ -from flask import url_for, redirect, render_template +from flask import url_for, redirect, render_template, abort from app import app, db from app.forms import ( NewWishlist, @@ -7,6 +7,8 @@ from app.forms import ( ResetWishlistUrls, NewItem, DeleteItem, + CheckItem, + parseHiddenIndex, ) from app.models import Wishlist, Item from uuid import UUID, uuid4 as uuid @@ -21,7 +23,7 @@ def index(): def new(): form = NewWishlist() if form.validate_on_submit(): - wishlist = Wishlist(form.title.data, form.description.data) + wishlist = Wishlist(str(form.title.data), str(form.description.data)) db.session.add(wishlist) db.session.commit() @@ -44,7 +46,7 @@ def edit(id: str): form_it_delete = DeleteItem() # Each submit needs a different page fot it to work on the same page. - if form_wl_delete.validate_on_submit() and form_wl_delete.wl_del_submit.data: + if form_wl_delete.validate_on_submit() and form_wl_delete.wl_del_submit.data: for i in wishlist.items: db.session.delete(i) db.session.delete(wishlist) @@ -52,12 +54,15 @@ def edit(id: str): return redirect(url_for("index")) elif form_wl_editinfo.validate_on_submit() and form_wl_editinfo.wl_edit_submit.data: - wishlist.title = form_wl_editinfo.title.data - wishlist.description = form_wl_editinfo.description.data + wishlist.title = str(form_wl_editinfo.title.data) + wishlist.description = str(form_wl_editinfo.description.data) db.session.commit() return redirect(url_for("edit", id=id)) - elif form_wl_reseturls.validate_on_submit() and form_wl_reseturls.wl_reset_submit.data: + elif ( + form_wl_reseturls.validate_on_submit() + and form_wl_reseturls.wl_reset_submit.data + ): wishlist.editId = uuid() wishlist.viewId = uuid() db.session.commit() @@ -65,27 +70,36 @@ def edit(id: str): return redirect(url_for("edit", id=wishlist.editId)) elif form_it_new.validate_on_submit() and form_it_new.it_new_submit.data: f = form_it_new + price = f.price.data if f.price.data != None else 0 + item = Item( - f.title.data, - f.description.data, - f.price.data + str( + f.title.data, + ), + str( + f.description.data, + ), + price, + str( + f.url.data, + ), + str( + f.image.data, + ), ) wishlist.items.append(item) db.session.commit() return redirect(url_for("edit", id=id)) elif form_it_delete.validate_on_submit() and form_it_delete.it_del_submit.data: - print(form_it_delete.index.data) - data = form_it_delete.index.data - i = int(data) if data != None else 0 - if i + 1 > len(wishlist.items): - return "Invalid item to delete", 400 - - wishlist.items.remove(wishlist.items[i]) + index = parseHiddenIndex(form_it_delete.index, wishlist.items) + if index == None: + return abort(400) + + wishlist.items.remove(wishlist.items[index]) db.session.commit() return redirect(url_for("edit", id=id)) - return render_template( "edit.html", @@ -98,11 +112,22 @@ def edit(id: str): ) -@app.route("/view/") +@app.route("/view/", methods=["GET", "POST"]) def view(id: str): - wishlist = db.one_or_404( + wishlist: Wishlist = db.one_or_404( db.select(Wishlist).filter_by(viewId=UUID(id)), description="Failed to get wishlist. Are you sure this is the correct url?", ) + checkform = CheckItem() + checkform.num + if checkform.validate_on_submit(): + index = parseHiddenIndex(checkform.num, wishlist.items) + if index == None: + return abort(400) - return render_template("view.html", wishlist=wishlist) + wishlist.items[index].bought = True + db.session.commit() + + return redirect(url_for("view", id=id)) + + return render_template("view.html", wishlist=wishlist, form=checkform) diff --git a/flake.nix b/flake.nix index a97daa2..6fa056c 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ { nativeBuildInputs = [ (pkgs.python3.withPackages - (x: [x.flask x.flask-wtf x.wtforms x.flask-sqlalchemy x.uuid])) + (x: [x.flask x.flask-wtf x.wtforms x.flask-sqlalchemy])) pkgs.entr ]; }; diff --git a/instance/application.db b/instance/application.db index 8580d3a..556d43a 100644 Binary files a/instance/application.db and b/instance/application.db differ