mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 19:42:37 +02:00
Minimal POC
This commit is contained in:
parent
b25d8a28c8
commit
b3adffb8f7
6 changed files with 136 additions and 43 deletions
|
@ -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
|
||||
|
|
58
kaba/base.py
58
kaba/base.py
|
@ -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))
|
||||
def coerce(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()
|
||||
}
|
||||
# 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
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -1,18 +1,33 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}
|
||||
<p>Producteur: {{ order.producer }}</p>
|
||||
<p><a href="/">< 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 %}
|
||||
|
|
20
kaba/templates/place_order.html
Normal file
20
kaba/templates/place_order.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}
|
||||
<p><a href="/">< 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 %}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue