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):
|
class ProductOrder(Document):
|
||||||
ref = Str()
|
|
||||||
wanted = Int()
|
wanted = Int()
|
||||||
ordered = Int()
|
ordered = Int()
|
||||||
|
|
||||||
|
|
||||||
class PersonOrder(Document):
|
class PersonOrder(Document):
|
||||||
person = Str()
|
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):
|
class Order(Document):
|
||||||
|
@ -87,7 +96,12 @@ class Order(Document):
|
||||||
where = Str()
|
where = Str()
|
||||||
producer = Reference(Producer, required=True)
|
producer = Reference(Producer, required=True)
|
||||||
products = Array(Product)
|
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()
|
app = Roll()
|
||||||
|
@ -110,12 +124,33 @@ async def home(request, response):
|
||||||
response.html("home.html", {"orders": Order.find()})
|
response.html("home.html", {"orders": Order.find()})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/commande/{order_id}", methods=["GET"])
|
@app.route("/commande/{order_id}/total", methods=["GET"])
|
||||||
async def get_order(request, response, order_id):
|
async def view_order(request, response, order_id):
|
||||||
order = Order.find_one(_id=ObjectId(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(
|
response.html(
|
||||||
"order.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:
|
for product in order.products:
|
||||||
quantity = form.int(product.ref, 0)
|
quantity = form.int(product.ref, 0)
|
||||||
if quantity:
|
if quantity:
|
||||||
person_order.products.append(ProductOrder(ref=product.ref, wanted=quantity))
|
person_order.products[product.ref] = ProductOrder(wanted=quantity)
|
||||||
if not order.orders:
|
if not order.orders:
|
||||||
order.orders = {}
|
order.orders = {}
|
||||||
order.orders[email] = person_order
|
order.orders[email] = person_order
|
||||||
|
|
58
kaba/base.py
58
kaba/base.py
|
@ -29,12 +29,16 @@ class Field:
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return self
|
return self
|
||||||
value = obj.get(self.name)
|
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):
|
def __set__(self, obj, value):
|
||||||
print("set", value, id(value))
|
|
||||||
value = self.coerce(value)
|
value = self.coerce(value)
|
||||||
print("set after", value, id(value))
|
|
||||||
obj[self.name] = value
|
obj[self.name] = value
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,7 +55,8 @@ class Int(Field):
|
||||||
|
|
||||||
|
|
||||||
class Datetime(Field):
|
class Datetime(Field):
|
||||||
def coerce(self, value):
|
@staticmethod
|
||||||
|
def coerce(value):
|
||||||
if isinstance(value, datetime):
|
if isinstance(value, datetime):
|
||||||
return value
|
return value
|
||||||
if isinstance(value, int):
|
if isinstance(value, int):
|
||||||
|
@ -59,7 +64,8 @@ class Datetime(Field):
|
||||||
|
|
||||||
|
|
||||||
class Email(Field):
|
class Email(Field):
|
||||||
def coerce(self, value):
|
@staticmethod
|
||||||
|
def coerce(value):
|
||||||
# TODO proper validation
|
# TODO proper validation
|
||||||
if "@" not in value:
|
if "@" not in value:
|
||||||
raise ValueError(f"Invalid value for email: {value}")
|
raise ValueError(f"Invalid value for email: {value}")
|
||||||
|
@ -67,8 +73,8 @@ class Email(Field):
|
||||||
|
|
||||||
|
|
||||||
class Reference(Field):
|
class Reference(Field):
|
||||||
|
@staticmethod
|
||||||
def coerce(self, value):
|
def coerce(value):
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
value = value["_id"]
|
value = value["_id"]
|
||||||
return ObjectId(value)
|
return ObjectId(value)
|
||||||
|
@ -83,22 +89,21 @@ class Dict(Field):
|
||||||
|
|
||||||
|
|
||||||
class Mapping(Field):
|
class Mapping(Field):
|
||||||
def __init__(self, key_field, value_field, *args, **kwargs):
|
def __init__(self, key_type, value_type, *args, **kwargs):
|
||||||
self.key_field = key_field
|
self.key_type = key_type
|
||||||
self.value_field = value_field
|
self.value_type = value_type
|
||||||
return super().__init__(*args, **kwargs)
|
kwargs["default"] = dict
|
||||||
|
|
||||||
def coerce(self, value):
|
def coerce(value):
|
||||||
print("coerce raw", value, id(value))
|
|
||||||
if value is None:
|
if value is None:
|
||||||
value = {}
|
value = {}
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
raise ValueError(f"{value} is not a dict")
|
raise ValueError(f"{value} is not a dict")
|
||||||
print("coerce", value, id(value))
|
# TODO coerce in-place.
|
||||||
return {
|
return {key_type(k): value_type(v) for k, v in value.items()}
|
||||||
self.key_field.coerce(k): self.value_field.coerce(v)
|
|
||||||
for k, v in value.items()
|
self.coerce = coerce
|
||||||
}
|
return super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Array(Field):
|
class Array(Field):
|
||||||
|
@ -111,11 +116,11 @@ class Array(Field):
|
||||||
return self
|
return self
|
||||||
value = obj.get(self.name)
|
value = obj.get(self.name)
|
||||||
if value is None:
|
if value is None:
|
||||||
value = []
|
self.__set__(obj, value)
|
||||||
self.__set__(value)
|
return obj[self.name]
|
||||||
return value
|
|
||||||
|
|
||||||
def __set__(self, obj, value):
|
def __set__(self, obj, value):
|
||||||
|
# TODO do not replace reference.
|
||||||
obj[self.name] = [self.coerce(v) for v in value or []]
|
obj[self.name] = [self.coerce(v) for v in value or []]
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,10 +140,21 @@ class Document(dict, metaclass=MetaDocument):
|
||||||
# def __repr__(self):
|
# def __repr__(self):
|
||||||
# return f"<{self.__class__.__name__} {self._id}>"
|
# 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
|
@property
|
||||||
def _id(self):
|
def _id(self):
|
||||||
return self["_id"]
|
return self["_id"]
|
||||||
|
|
||||||
|
@_id.setter
|
||||||
|
def _id(self, value):
|
||||||
|
self["_id"] = value
|
||||||
|
|
||||||
def insert_one(self):
|
def insert_one(self):
|
||||||
self.collection.insert_one(self)
|
self.collection.insert_one(self)
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -10,5 +10,6 @@
|
||||||
<input type="text" name="email">
|
<input type="text" name="email">
|
||||||
<input type="submit" value="Commander">
|
<input type="submit" value="Commander">
|
||||||
</form>
|
</form>
|
||||||
|
<a href="/commande/{{ order._id }}/total">Résumé de la commande</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<p>Producteur: {{ order.producer }}</p>
|
<p><a href="/">< Commandes</a></p>
|
||||||
|
<p>Producteur: {{ producer.name }}</p>
|
||||||
<p>Lieu: {{ order.where }}</p>
|
<p>Lieu: {{ order.where }}</p>
|
||||||
<p>Date: {{ order.when }}</p>
|
<p>Date: {{ order.when }}</p>
|
||||||
<p>Commande pour {{ person }}</p>
|
|
||||||
<form method="post">
|
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Produit</th><th>Prix</th><th>Quantité</th></tr>
|
<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 %}
|
||||||
|
<th>Total</th>
|
||||||
|
</tr>
|
||||||
{% for product in order.products %}
|
{% for product in order.products %}
|
||||||
<tr><th>{{ product.name }}</th><td>{{ product.price }} €</td><td><input type="number" name="{{ product.ref }}"></td></tr>
|
<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 %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
<input type="hidden" name="email" value="{{ person }}">
|
<p>Total: {{ total }} €</p>
|
||||||
<input type="submit" value="Valider ma commande">
|
|
||||||
</form>
|
|
||||||
{% endblock body %}
|
{% 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()
|
order.insert_one()
|
||||||
retrieved = Order.find_one(_id=order._id)
|
retrieved = Order.find_one(_id=order._id)
|
||||||
assert retrieved.products[0].name == "riz"
|
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