Fix validation and saving for multi_choice, add price range

This commit is contained in:
Midgard 2020-02-24 00:31:14 +01:00
parent 513e495665
commit 1025ade758
Signed by: midgard
GPG key ID: 511C112F1331BBB4
5 changed files with 53 additions and 11 deletions

View file

@ -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
] ]

View file

@ -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):

View file

@ -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"],
) )

View file

@ -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)

View file

@ -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)