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")
|
||||
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:
|
||||
self.dish_id.choices = [
|
||||
(i.id, (i.name + ": " + euro_string(i.price)))
|
||||
for i in location.dishes
|
||||
(dish.id, (dish.name + ": " + self.format_price_range(dish.price_range())))
|
||||
for dish in location.dishes
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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
|
||||
|
||||
|
||||
|
@ -78,6 +78,17 @@ class Dish:
|
|||
"\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:
|
||||
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.exceptions import SemanticError
|
||||
from .models import Location, Choice, Option, Dish
|
||||
from utils import first
|
||||
|
||||
|
||||
# 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):
|
||||
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"]}
|
||||
|
||||
|
||||
return Location(
|
||||
ast["id"],
|
||||
name=ast["name"],
|
||||
|
@ -46,8 +55,8 @@ class HldsSemanticActions:
|
|||
ast["id"],
|
||||
name=ast["name"],
|
||||
description=ast["description"],
|
||||
price=ast["price"] if ast["price"] else 0,
|
||||
tags=ast["tags"] if ast["tags"] else [],
|
||||
price=ast["price"] or 0,
|
||||
tags=ast["tags"] or [],
|
||||
choices=ast["choices"],
|
||||
)
|
||||
|
||||
|
@ -74,7 +83,7 @@ class HldsSemanticActions:
|
|||
ast["id"],
|
||||
name=ast["name"],
|
||||
description=ast["description"],
|
||||
price=ast["price"] if ast["price"] else 0,
|
||||
price=ast["price"] or 0,
|
||||
tags=ast["tags"],
|
||||
)
|
||||
|
||||
|
|
|
@ -19,3 +19,6 @@ def first(iterable: Iterable, default=None):
|
|||
except StopIteration:
|
||||
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 hlds.definitions import location_definitions
|
||||
from notification import post_order_to_webhook
|
||||
from utils import ignore_none
|
||||
|
||||
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,
|
||||
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"])
|
||||
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
|
||||
choices = location.dish_by_id(form.dish_id.data).choices
|
||||
chosen = [
|
||||
(
|
||||
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:
|
||||
return redirect(url_for("order_bp.order_item_create",
|
||||
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
|
||||
|
||||
# XXX Temporary
|
||||
chosen_text = "; ".join(option.name for option in chosen)
|
||||
item.comment = chosen_text + "; Comment: " + item.comment if item.comment else chosen_text
|
||||
comments = [_name(option) for option in chosen if option]
|
||||
if item.comment:
|
||||
comments.append("Comment: " + item.comment)
|
||||
item.comment = "; ".join(comments)
|
||||
|
||||
item.update_from_hlds()
|
||||
db.session.add(item)
|
||||
|
|
Loading…
Reference in a new issue