Minimal POC

This commit is contained in:
Yohan Boniface 2019-03-18 00:11:27 +01:00
parent b25d8a28c8
commit b3adffb8f7
6 changed files with 136 additions and 43 deletions

View file

@ -71,14 +71,23 @@ class Person(Document):
class ProductOrder(Document):
ref = Str()
wanted = Int()
ordered = Int()
class PersonOrder(Document):
person = Str()
products = Array(ProductOrder)
products = Mapping(str, ProductOrder)
def get_quantity(self, product):
choice = self.products.get(product.ref)
return choice.wanted if choice else 0
def total(self, products):
products = {p.ref: p for p in products}
return round(
sum(p.wanted * products[ref].price for ref, p in self.products.items()), 2
)
class Order(Document):
@ -87,7 +96,12 @@ class Order(Document):
where = Str()
producer = Reference(Producer, required=True)
products = Array(Product)
orders = Mapping(Str, PersonOrder)
orders = Mapping(str, PersonOrder)
def product_wanted(self, product):
return round(
sum([po.products[product.ref].wanted for po in self.orders.values()])
)
app = Roll()
@ -110,12 +124,33 @@ async def home(request, response):
response.html("home.html", {"orders": Order.find()})
@app.route("/commande/{order_id}", methods=["GET"])
async def get_order(request, response, order_id):
@app.route("/commande/{order_id}/total", methods=["GET"])
async def view_order(request, response, order_id):
order = Order.find_one(_id=ObjectId(order_id))
total = round(sum(po.total(order.products) for po in order.orders.values()), 2)
response.html(
"order.html",
{"order": order, "person": request.query.get("email"), "person_order": None},
{
"order": order,
"producer": Producer.find_one(_id=order.producer),
"total": total,
},
)
@app.route("/commande/{order_id}", methods=["GET"])
async def order_form(request, response, order_id):
order = Order.find_one(_id=ObjectId(order_id))
email = request.query.get("email")
person_order = order.orders.get(email)
response.html(
"place_order.html",
{
"order": order,
"person": email,
"person_order": person_order,
"producer": Producer.find_one(_id=order.producer),
},
)
@ -128,7 +163,7 @@ async def place_order(request, response, order_id):
for product in order.products:
quantity = form.int(product.ref, 0)
if quantity:
person_order.products.append(ProductOrder(ref=product.ref, wanted=quantity))
person_order.products[product.ref] = ProductOrder(wanted=quantity)
if not order.orders:
order.orders = {}
order.orders[email] = person_order

View file

@ -29,12 +29,16 @@ class Field:
if obj is None:
return self
value = obj.get(self.name)
return value
if value is None and self.default is not None:
if callable(self.default):
value = self.default()
else:
value = self.default
self.__set__(obj, value)
return obj.get(self.name)
def __set__(self, obj, value):
print("set", value, id(value))
value = self.coerce(value)
print("set after", value, id(value))
obj[self.name] = value
@ -51,7 +55,8 @@ class Int(Field):
class Datetime(Field):
def coerce(self, value):
@staticmethod
def coerce(value):
if isinstance(value, datetime):
return value
if isinstance(value, int):
@ -59,7 +64,8 @@ class Datetime(Field):
class Email(Field):
def coerce(self, value):
@staticmethod
def coerce(value):
# TODO proper validation
if "@" not in value:
raise ValueError(f"Invalid value for email: {value}")
@ -67,8 +73,8 @@ class Email(Field):
class Reference(Field):
def coerce(self, value):
@staticmethod
def coerce(value):
if isinstance(value, dict):
value = value["_id"]
return ObjectId(value)
@ -83,22 +89,21 @@ class Dict(Field):
class Mapping(Field):
def __init__(self, key_field, value_field, *args, **kwargs):
self.key_field = key_field
self.value_field = value_field
return super().__init__(*args, **kwargs)
def __init__(self, key_type, value_type, *args, **kwargs):
self.key_type = key_type
self.value_type = value_type
kwargs["default"] = dict
def coerce(self, value):
print("coerce raw", value, id(value))
if value is None:
value = {}
if not isinstance(value, dict):
raise ValueError(f"{value} is not a dict")
print("coerce", value, id(value))
return {
self.key_field.coerce(k): self.value_field.coerce(v)
for k, v in value.items()
}
def coerce(value):
if value is None:
value = {}
if not isinstance(value, dict):
raise ValueError(f"{value} is not a dict")
# TODO coerce in-place.
return {key_type(k): value_type(v) for k, v in value.items()}
self.coerce = coerce
return super().__init__(*args, **kwargs)
class Array(Field):
@ -111,11 +116,11 @@ class Array(Field):
return self
value = obj.get(self.name)
if value is None:
value = []
self.__set__(value)
return value
self.__set__(obj, value)
return obj[self.name]
def __set__(self, obj, value):
# TODO do not replace reference.
obj[self.name] = [self.coerce(v) for v in value or []]
@ -135,10 +140,21 @@ class Document(dict, metaclass=MetaDocument):
# def __repr__(self):
# return f"<{self.__class__.__name__} {self._id}>"
def __init__(self, data=None, **attrs):
if data:
for key, value in data.items():
setattr(self, key, value)
for key, value in attrs.items():
setattr(self, key, value)
@property
def _id(self):
return self["_id"]
@_id.setter
def _id(self, value):
self["_id"] = value
def insert_one(self):
self.collection.insert_one(self)
return self

View file

@ -10,5 +10,6 @@
<input type="text" name="email">
<input type="submit" value="Commander">
</form>
<a href="/commande/{{ order._id }}/total">Résumé de la commande</a>
{% endfor %}
{% endblock body %}

View file

@ -1,18 +1,33 @@
{% extends "base.html" %}
{% block body %}
<p>Producteur: {{ order.producer }}</p>
<p><a href="/">&lt; Commandes</a></p>
<p>Producteur: {{ producer.name }}</p>
<p>Lieu: {{ order.where }}</p>
<p>Date: {{ order.when }}</p>
<p>Commande pour {{ person }}</p>
<form method="post">
<table>
<tr><th>Produit</th><th>Prix</th><th>Quantité</th></tr>
{% for product in order.products %}
<tr><th>{{ product.name }}</th><td>{{ product.price }} €</td><td><input type="number" name="{{ product.ref }}"></td></tr>
<table>
<tr>
<th>Produit</th>
<th>Prix</th>
{% for email, person_order in order.orders.items() %}
<th><a href="/commande/{{ order._id }}?email={{ email }}">{{ email }}</a></th>
{% endfor %}
</table>
<input type="hidden" name="email" value="{{ person }}">
<input type="submit" value="Valider ma commande">
</form>
<th>Total</th>
</tr>
{% for product in order.products %}
<tr>
<th>{{ product.name }}</th>
<td>{{ product.price }} €</td>
{% for email, person_order in order.orders.items() %}
{% if product.ref in person_order.products %}
<td>{{ person_order.products[product.ref].wanted }}</td>
{% else %}
<td></td>
{% endif %}
{% endfor %}
<th>{{ order.product_wanted(product) }}</th>
</tr>
{% endfor %}
</table>
<p>Total: {{ total }} €</p>
{% endblock body %}

View file

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block body %}
<p><a href="/">&lt; Commandes</a></p>
<p>Producteur: {{ producer.name }}</p>
<p>Lieu: {{ order.where }}</p>
<p>Date: {{ order.when }}</p>
<p>Commande de «{{ person }}»</p>
<form method="post">
<table>
<tr><th>Produit</th><th>Prix</th><th>Quantité</th></tr>
{% for product in order.products %}
<tr><th>{{ product.name }}</th><td>{{ product.price }} €</td><td><input type="number" name="{{ product.ref }}" value="{{ person_order.get_quantity(product) if person_order else 0 }}"></td></tr>
{% endfor %}
</table>
<p>Total: {{ person_order.total(order.products) if person_order else 0 }} €</p>
<input type="hidden" name="email" value="{{ person }}">
<input type="submit" value="Valider ma commande">
</form>
{% endblock body %}

View file

@ -15,3 +15,9 @@ def test_can_create_order():
order.insert_one()
retrieved = Order.find_one(_id=order._id)
assert retrieved.products[0].name == "riz"
def test_can_update_order_products():
order = Order()
order.products.append(Product(name="riz", price="2.4"))
assert len(order.products) == 1