Fix validation and saving for multi_choice, add price range
This commit is contained in:
parent
513e495665
commit
1025ade758
5 changed files with 53 additions and 11 deletions
11
app/forms.py
11
app/forms.py
|
@ -53,10 +53,17 @@ class OrderItemForm(Form):
|
||||||
comment = StringField("Comment")
|
comment = StringField("Comment")
|
||||||
submit_button = SubmitField("Submit")
|
submit_button = SubmitField("Submit")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_price_range(price_range):
|
||||||
|
if price_range[0] == price_range[1]:
|
||||||
|
return euro_string(price_range[0])
|
||||||
|
else:
|
||||||
|
return "from {}".format(euro_string(price_range[0]))
|
||||||
|
|
||||||
def populate(self, location: Location, dish_id: Optional[str]) -> None:
|
def populate(self, location: Location, dish_id: Optional[str]) -> None:
|
||||||
self.dish_id.choices = [
|
self.dish_id.choices = [
|
||||||
(i.id, (i.name + ": " + euro_string(i.price)))
|
(dish.id, (dish.name + ": " + self.format_price_range(dish.price_range())))
|
||||||
for i in location.dishes
|
for dish in location.dishes
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
from typing import Iterable, List, Mapping, Any, Optional
|
from typing import Iterable, List, Tuple, Mapping, Any, Optional
|
||||||
from utils import euro_string, first
|
from utils import euro_string, first
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +78,17 @@ class Dish:
|
||||||
"\n\t".join(map(_format_type_and_choice, self.choices))
|
"\n\t".join(map(_format_type_and_choice, self.choices))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def price_range(self) -> Tuple[int, int]:
|
||||||
|
return (self.price + self._sum_f_option_prices(min),
|
||||||
|
self.price + self._sum_f_option_prices(max))
|
||||||
|
|
||||||
|
def _sum_f_option_prices(self, f):
|
||||||
|
return sum(
|
||||||
|
f(option.price for option in choice.options)
|
||||||
|
for (choice_type, choice) in self.choices
|
||||||
|
if choice_type == "single_choice"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Location:
|
class Location:
|
||||||
def __init__(self, id_, *, name, dishes, osm=None, address=None, telephone=None, website=None):
|
def __init__(self, id_, *, name, dishes, osm=None, address=None, telephone=None, website=None):
|
||||||
|
|
|
@ -8,6 +8,7 @@ from tatsu import parse as tatsu_parse
|
||||||
from tatsu.ast import AST
|
from tatsu.ast import AST
|
||||||
from tatsu.exceptions import SemanticError
|
from tatsu.exceptions import SemanticError
|
||||||
from .models import Location, Choice, Option, Dish
|
from .models import Location, Choice, Option, Dish
|
||||||
|
from utils import first
|
||||||
|
|
||||||
|
|
||||||
# TODO Use proper way to get resources, see https://stackoverflow.com/a/10935674
|
# TODO Use proper way to get resources, see https://stackoverflow.com/a/10935674
|
||||||
|
@ -29,8 +30,16 @@ class HldsSemanticActions:
|
||||||
if not isinstance(choice[1], Choice):
|
if not isinstance(choice[1], Choice):
|
||||||
dish.choices[i] = (dish.choices[i][0], choices[choice[1]])
|
dish.choices[i] = (dish.choices[i][0], choices[choice[1]])
|
||||||
|
|
||||||
|
# Move the base price to the first single choice if there is any
|
||||||
|
first_single_choice = first(c[1] for c in dish.choices if c[0] == "single_choice")
|
||||||
|
if dish.price and first_single_choice:
|
||||||
|
for option in first_single_choice.options:
|
||||||
|
option.price += dish.price
|
||||||
|
dish.price = 0
|
||||||
|
|
||||||
attributes = {att["key"]: att["value"] for att in ast["attributes"]}
|
attributes = {att["key"]: att["value"] for att in ast["attributes"]}
|
||||||
|
|
||||||
|
|
||||||
return Location(
|
return Location(
|
||||||
ast["id"],
|
ast["id"],
|
||||||
name=ast["name"],
|
name=ast["name"],
|
||||||
|
@ -46,8 +55,8 @@ class HldsSemanticActions:
|
||||||
ast["id"],
|
ast["id"],
|
||||||
name=ast["name"],
|
name=ast["name"],
|
||||||
description=ast["description"],
|
description=ast["description"],
|
||||||
price=ast["price"] if ast["price"] else 0,
|
price=ast["price"] or 0,
|
||||||
tags=ast["tags"] if ast["tags"] else [],
|
tags=ast["tags"] or [],
|
||||||
choices=ast["choices"],
|
choices=ast["choices"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,7 +83,7 @@ class HldsSemanticActions:
|
||||||
ast["id"],
|
ast["id"],
|
||||||
name=ast["name"],
|
name=ast["name"],
|
||||||
description=ast["description"],
|
description=ast["description"],
|
||||||
price=ast["price"] if ast["price"] else 0,
|
price=ast["price"] or 0,
|
||||||
tags=ast["tags"],
|
tags=ast["tags"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -19,3 +19,6 @@ def first(iterable: Iterable, default=None):
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def ignore_none(iterable: Iterable):
|
||||||
|
return filter(lambda x: x is not None, iterable)
|
||||||
|
|
|
@ -13,6 +13,7 @@ from forms import AnonOrderItemForm, OrderForm, OrderItemForm
|
||||||
from models import Order, OrderItem, User, db
|
from models import Order, OrderItem, User, db
|
||||||
from hlds.definitions import location_definitions
|
from hlds.definitions import location_definitions
|
||||||
from notification import post_order_to_webhook
|
from notification import post_order_to_webhook
|
||||||
|
from utils import ignore_none
|
||||||
|
|
||||||
order_bp = Blueprint("order_bp", "order")
|
order_bp = Blueprint("order_bp", "order")
|
||||||
|
|
||||||
|
@ -104,6 +105,11 @@ def order_edit(order_id: int) -> typing.Union[str, Response]:
|
||||||
return render_template("order_edit.html", form=orderForm,
|
return render_template("order_edit.html", form=orderForm,
|
||||||
order_id=order_id)
|
order_id=order_id)
|
||||||
|
|
||||||
|
def _name(option):
|
||||||
|
try:
|
||||||
|
return option.name
|
||||||
|
except AttributeError:
|
||||||
|
return ", ".join(o.name for o in option)
|
||||||
|
|
||||||
@order_bp.route("/<order_id>/create", methods=["GET", "POST"])
|
@order_bp.route("/<order_id>/create", methods=["GET", "POST"])
|
||||||
def order_item_create(order_id: int) -> typing.Any:
|
def order_item_create(order_id: int) -> typing.Any:
|
||||||
|
@ -141,10 +147,14 @@ def order_item_create(order_id: int) -> typing.Any:
|
||||||
# The form's validation tests that dish_id is valid and gives a friendly error if it's not
|
# The form's validation tests that dish_id is valid and gives a friendly error if it's not
|
||||||
choices = location.dish_by_id(form.dish_id.data).choices
|
choices = location.dish_by_id(form.dish_id.data).choices
|
||||||
chosen = [
|
chosen = [
|
||||||
|
(
|
||||||
choice.option_by_id(request.form.get("choice_" + choice.id))
|
choice.option_by_id(request.form.get("choice_" + choice.id))
|
||||||
for (_choice_type, choice) in choices
|
if choice_type == "single_choice" else
|
||||||
|
list(ignore_none(request.form.getlist("choice_" + choice.id, type=choice.option_by_id)))
|
||||||
|
)
|
||||||
|
for (choice_type, choice) in choices
|
||||||
]
|
]
|
||||||
all_choices_present = all(chosen)
|
all_choices_present = all(x is not None for x in chosen)
|
||||||
if not all_choices_present:
|
if not all_choices_present:
|
||||||
return redirect(url_for("order_bp.order_item_create",
|
return redirect(url_for("order_bp.order_item_create",
|
||||||
order_id=order_id, dish=form.dish_id.data))
|
order_id=order_id, dish=form.dish_id.data))
|
||||||
|
@ -158,8 +168,10 @@ def order_item_create(order_id: int) -> typing.Any:
|
||||||
session["anon_name"] = item.name
|
session["anon_name"] = item.name
|
||||||
|
|
||||||
# XXX Temporary
|
# XXX Temporary
|
||||||
chosen_text = "; ".join(option.name for option in chosen)
|
comments = [_name(option) for option in chosen if option]
|
||||||
item.comment = chosen_text + "; Comment: " + item.comment if item.comment else chosen_text
|
if item.comment:
|
||||||
|
comments.append("Comment: " + item.comment)
|
||||||
|
item.comment = "; ".join(comments)
|
||||||
|
|
||||||
item.update_from_hlds()
|
item.update_from_hlds()
|
||||||
db.session.add(item)
|
db.session.add(item)
|
||||||
|
|
Loading…
Reference in a new issue