Add form for choices, submitting fails

This commit is contained in:
Midgard 2020-02-21 18:38:30 +01:00
parent 91e76c2b45
commit f900c85931
Signed by: midgard
GPG key ID: 511C112F1331BBB4
4 changed files with 93 additions and 24 deletions

View file

@ -1,15 +1,17 @@
"Script for everything form related in Haldis"
from datetime import datetime, timedelta
from typing import Optional
from flask import session
from flask_login import current_user
from flask_wtf import FlaskForm as Form
from wtforms import (DateTimeField, SelectField, StringField, SubmitField,
validators)
from wtforms import (DateTimeField, SelectField, SelectMultipleField, StringField, SubmitField,
FieldList, validators)
from utils import euro_string
from hlds.definitions import location_definitions
from hlds.models import Location
from hlds.models import Location, Dish, Choice
from models import User
@ -45,18 +47,42 @@ class OrderForm(Form):
class OrderItemForm(Form):
"Class which defines the form for a new Item in an Order"
"New Item in an Order"
# pylint: disable=R0903
dish_id = SelectField("Dish")
single_choices = FieldList(SelectField())
multi_choices = FieldList(SelectMultipleField())
comment = StringField("Comment")
submit_button = SubmitField("Submit")
def populate(self, location: Location) -> None:
"Fill in all the dish options from the location"
def populate(self, location: Location, dish_id: Optional[str]) -> None:
self.dish_id.choices = [
(i.id, (i.name + ": " + euro_string(i.price)))
for i in location.dishes
]
dish = location.dish_by_id(dish_id) if dish_id else None
if dish:
self.add_choices_for(dish)
def add_choices_for(self, dish: Dish):
for (choice_type, choice) in dish.choices:
if choice_type == "single_choice":
field = self.single_choices.append_entry(choice.name)
elif choice_type == "multi_choice":
field = self.multi_choices.append_entry(choice.name)
else:
assert False, "Unsupported choice type"
field.label.text = choice.name
field.choices = self.options_for(choice)
@staticmethod
def options_for(choice: Choice):
return [
(c.id, (c.name +
(" (" + c.description + ")" if c.description else "") +
(": +" + euro_string(c.price) if c.price else "")))
for c in choice.options
]
class AnonOrderItemForm(OrderItemForm):

View file

@ -2,7 +2,7 @@
# pylint: disable=too-few-public-methods
from typing import Iterable, List, Mapping, Any, Optional
from utils import euro_string
from utils import euro_string, first
def _format_tags(tags: Iterable[str]) -> str:
@ -63,6 +63,7 @@ class Dish:
self.price: int = price
self.tags: List[str] = tags
# The str in (str, Choice) is the type of choice: single_choice or multi_choice
self.choices: List[(str, Choice)] = choices
def __str__(self):
@ -86,6 +87,9 @@ class Location:
self.dishes: List[Dish] = dishes
def dish_by_id(self, dish_id: str) -> Optional[Dish]:
return first(filter(lambda d: d.id == dish_id, self.dishes))
def __str__(self):
return (
"============================\n"

View file

@ -57,6 +57,21 @@
{{ form.comment(class='form-control', placeholder='Fill in comment, when applicable') }}
{{ util.render_form_field_errors(form.comment) }}
</div>
{% for choice_field in form.single_choices %}
<div class="form-group {{ 'has-errors' if choice_field }}">
{{ choice_field.label(class='control-label') }}<br>
{{ choice_field(class='form-control') }}
{{ util.render_form_field_errors(choice_field) }}
</div>
{% endfor %}
{% for choice_field in form.multi_choices %}
<div class="form-group {{ 'has-errors' if choice_field }}">
{{ choice_field.label(class='control-label') }}<br>
{{ choice_field(class='form-control') }}
{{ util.render_form_field_errors(choice_field) }}
</div>
{% endfor %}
{% if current_user.is_anonymous() %}
<div class="form-group{{ ' has-error' if form.name.errors }}{{ ' required' if form.name.flags.required }}">
{{ form.name.label(class='control-label') }}

View file

@ -11,6 +11,7 @@ from flask_login import current_user, login_required
from forms import AnonOrderItemForm, OrderForm, OrderItemForm
from models import Order, OrderItem, User, db
from hlds.definitions import location_definitions
from notification import post_order_to_webhook
order_bp = Blueprint("order_bp", "order")
@ -58,7 +59,7 @@ def order_from_id(order_id: int, form: OrderForm = None) -> str:
form = AnonOrderItemForm() if current_user.is_anonymous() \
else OrderItemForm()
if order.location:
form.populate(order.location)
form.populate(order.location, None)
if order.is_closed():
form = None
total_price = sum([o.price for o in order.items])
@ -101,7 +102,7 @@ def order_edit(order_id: int) -> typing.Union[str, Response]:
order_id=order_id)
@order_bp.route("/<order_id>/create", methods=["POST"])
@order_bp.route("/<order_id>/create", methods=["GET", "POST"])
def order_item_create(order_id: int) -> typing.Any:
# type is 'typing.Union[str, Response]', but this errors due to
# https://github.com/python/mypy/issues/7187
@ -114,23 +115,46 @@ def order_item_create(order_id: int) -> typing.Any:
if current_user.is_anonymous() and not current_order.public:
flash("Please login to see this order.", "info")
abort(401)
location = current_order.location
# If location doesn't exist any more, adding items is nonsensical
if not location:
abort(404)
form = AnonOrderItemForm() if current_user.is_anonymous() \
else OrderItemForm()
form.populate(current_order.location)
if form.validate_on_submit():
item = OrderItem()
form.populate_obj(item)
item.order_id = order_id
if not current_user.is_anonymous():
item.user_id = current_user.id
else:
session["anon_name"] = item.name
item.update_from_hlds()
db.session.add(item)
db.session.commit()
flash("Ordered %s" % (item.dish_name), "success")
return redirect(url_for("order_bp.order_from_id", order_id=order_id))
return order_from_id(order_id, form=form)
dish_id = form.dish_id.data if form.is_submitted() else request.args.get("dish")
if dish_id and not location.dish_by_id(dish_id):
abort(404)
form.populate(current_order.location, dish_id)
if not form.validate_on_submit():
return order_from_id(order_id, form=form)
# Form was submitted and is valid
# The form's validation tests that dish_id is valid and gives a friendly error if it's not
form_data = form.data
choices = location.dish_by_id(form_data["dish_id"]).choices
all_choices_present = all(
("choice_" + choice.id) in form_data
for (_choice_type, choice) in choices
)
if not all_choices_present:
return redirect(url_for("order_bp.order_item_create",
order_id=order_id, dish=form_data["dish_id"]))
item = OrderItem()
form.populate_obj(item)
item.order_id = order_id
if not current_user.is_anonymous():
item.user_id = current_user.id
else:
session["anon_name"] = item.name
item.update_from_hlds()
db.session.add(item)
db.session.commit()
flash("Ordered %s" % (item.dish_name), "success")
return redirect(url_for("order_bp.order_from_id", order_id=order_id))
@order_bp.route("/<order_id>/<item_id>/paid", methods=["POST"])