Basic messages, config and XLSX export

This commit is contained in:
Yohan Boniface 2019-03-19 23:18:01 +01:00
parent 4623adb41c
commit cd12223d6d
6 changed files with 93 additions and 32 deletions

View file

@ -1,13 +1,17 @@
import csv import csv
from pathlib import Path
from time import perf_counter from time import perf_counter
import ujson as json import ujson as json
import hupper import hupper
import minicli import minicli
from jinja2 import Environment, PackageLoader, select_autoescape from jinja2 import Environment, PackageLoader, select_autoescape
from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook
from roll import Roll, Response from roll import Roll, Response
from roll.extensions import cors, options, traceback, simple_server from roll.extensions import cors, options, traceback, simple_server, static
from . import config
from .models import Delivery, Order, Person, Product, ProductOrder from .models import Delivery, Order, Person, Product, ProductOrder
@ -28,6 +32,9 @@ class Response(Response):
redirect = property(None, redirect) redirect = property(None, redirect)
def message(self, text, status="success"):
self.cookies.set("message", json.dumps((text, status)))
class Roll(Roll): class Roll(Roll):
Response = Response Response = Response
@ -61,7 +68,7 @@ async def attach_request(request, response):
@app.listen("startup") @app.listen("startup")
async def on_startup(): async def on_startup():
connect() configure()
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
@ -83,8 +90,8 @@ async def create_delivery(request, response):
data[name] = form.get(name) data[name] = form.get(name)
delivery = Delivery(**data) delivery = Delivery(**data)
delivery.persist() delivery.persist()
response.status = 302 response.message("La livraison a bien été créée!")
response.headers["Location"] = f"/livraison/{delivery.id}" response.redirect = f"/livraison/{delivery.id}"
@app.route("/livraison/{id}/importer/produits", methods=["POST"]) @app.route("/livraison/{id}/importer/produits", methods=["POST"])
@ -97,6 +104,7 @@ async def import_products(request, response, id):
for row in reader: for row in reader:
delivery.products.append(Product(**row)) delivery.products.append(Product(**row))
delivery.persist() delivery.persist()
response.message("Les produits de la livraison ont bien été mis à jour!")
response.redirect = f"/livraison/{delivery.id}" response.redirect = f"/livraison/{delivery.id}"
@ -114,8 +122,8 @@ async def post_delivery(request, response, id):
if name in form: if name in form:
setattr(delivery, name, form.get(name)) setattr(delivery, name, form.get(name))
delivery.persist() delivery.persist()
response.status = 302 response.message("La livraison a bien été mise à jour!")
response.headers["Location"] = f"/livraison/{delivery.id}" response.redirect = f"/livraison/{delivery.id}"
@app.route("/livraison/{id}", methods=["GET"]) @app.route("/livraison/{id}", methods=["GET"])
@ -128,7 +136,7 @@ async def view_delivery(request, response, id):
async def order_form(request, response, id): async def order_form(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
email = request.query.get("email") email = request.query.get("email")
order = delivery.orders.get(email) order = delivery.orders.get(email) or Order()
response.html( response.html(
"place_order.html", {"delivery": delivery, "person": email, "order": order} "place_order.html", {"delivery": delivery, "person": email, "order": order}
) )
@ -148,6 +156,7 @@ async def place_order(request, response, id):
delivery.orders = {} delivery.orders = {}
delivery.orders[email] = order delivery.orders[email] = order
delivery.persist() delivery.persist()
response.message("Jour de fête! Votre commande a bien été prise en compte!")
response.redirect = request.url.decode() response.redirect = request.url.decode()
@ -165,22 +174,42 @@ async def import_commande(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
delivery.orders[email] = order delivery.orders[email] = order
delivery.persist() delivery.persist()
response.message(f"Yallah! La commande de {email} a bien été importée!")
response.redirect = f"/livraison/{delivery.id}" response.redirect = f"/livraison/{delivery.id}"
def connect(): @app.route("/livraison/{id}/rapport.xlsx", methods=["GET"])
# db = os.environ.get("KABA_DB", "mongodb://localhost/kaba") async def xls_report(request, response, id):
# client = MongoClient(db) delivery = Delivery.load(id)
# db = client.get_database() wb = Workbook()
# Person.bind(db) ws = wb.active
# Delivery.bind(db) ws.title = f"Commande Epinamap - {delivery.producer} - {delivery.when.date()}"
# return client ws.append(["ref", "produit", "prix", "unités", "total"])
pass for product in delivery.products:
wanted = delivery.product_wanted(product)
ws.append(
[
product.ref,
product.name,
product.price,
wanted,
round(product.price * wanted, 2),
]
)
ws.append(["", "", "", "Total", delivery.total])
response.body = save_virtual_workbook(wb)
mimetype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
response.headers["Content-Disposition"] = f'attachment; filename="export.xlsx"'
response.headers["Content-Type"] = f"{mimetype}; charset=utf-8"
def configure():
config.init()
@minicli.cli() @minicli.cli()
def shell(): def shell():
"""Run an ipython already connected to Mongo.""" """Run an ipython in app context."""
try: try:
from IPython import start_ipython from IPython import start_ipython
except ImportError: except ImportError:
@ -200,7 +229,7 @@ def shell():
@minicli.wrap @minicli.wrap
def cli_wrapper(): def cli_wrapper():
connect() configure()
start = perf_counter() start = perf_counter()
yield yield
elapsed = perf_counter() - start elapsed = perf_counter() - start
@ -213,6 +242,7 @@ def serve(reload=False):
if reload: if reload:
hupper.start_reloader("kaba.serve") hupper.start_reloader("kaba.serve")
traceback(app) traceback(app)
static(app, root=Path(__file__).parent / "static")
simple_server(app, port=2244) simple_server(app, port=2244)

View file

@ -4,13 +4,18 @@
<title>{% if title %}{{ title }} - {% endif %}Commandes Epinamap</title> <title>{% if title %}{{ title }} - {% endif %}Commandes Epinamap</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css"> <!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css"> -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.4.1/css/bulma.css"> <link rel="stylesheet" type="text/css" href="/static/app.css">
{% block head %} {% block head %}
{% endblock head %} {% endblock head %}
</head> </head>
<body> <body>
<header>
{% if message %}
<div class="notification {{ message[1] }}">{{ message[0] }}</div>
{% endif %}
</header>
{% block body %} {% block body %}
{% endblock body %} {% endblock body %}
</body> </body>

View file

@ -7,9 +7,11 @@
<p>Date: {{ delivery.when }}</p> <p>Date: {{ delivery.when }}</p>
<form action="/livraison/{{ delivery.id }}/commander"> <form action="/livraison/{{ delivery.id }}/commander">
<label for="email">Commander</label> <label for="email">Commander</label>
<input type="email" name="email"> <input type="email" name="email" placeholder="Mon courriel">
<input type="submit" value="Commander"> <input type="submit" value="Commander">
</form> </form>
<a href="/livraison/{{ delivery.id }}">Résumé de la livraison</a> <a href="/livraison/{{ delivery.id }}">Résumé de la livraison</a>
<hr>
{% endfor %} {% endfor %}
<a href="/livraison/new">Nouvelle livraison</a>
{% endblock body %} {% endblock body %}

View file

@ -2,15 +2,27 @@
{% block body %} {% block body %}
<p><a href="/">&lt; Commandes</a></p> <p><a href="/">&lt; Commandes</a></p>
<p>Producteur: {{ delivery.producer }}</p> <p>Producteur: {{ delivery.producer }} — Lieu: {{ delivery.where }} — Date: {{ delivery.when }} — Date limite de commande: {{ delivery.order_before }}</p>
<p>Lieu: {{ delivery.where }}</p>
<p>Date: {{ delivery.when }}</p>
<p>Commande de «{{ person }}»</p> <p>Commande de «{{ person }}»</p>
<form method="post"> <form method="post">
<table> <table>
<tr><th>Produit</th><th>Prix</th><th>Quantité</th></tr> <tr><th>Produit</th><th>Prix</th><th>Quantité</th></tr>
{% for product in delivery.products %} {% for product in delivery.products %}
<tr><th>{{ product.name }}</th><td>{{ product.price }} €</td><td><input type="number" name="{{ product.ref }}" value="{{ order.get_quantity(product) if order else 0 }}"></td></tr> <tr>
<th>{{ product.name }}
<label for="toggleControl{{ loop.index }}" class="toggle-label">(Détails)</label>
<input type="checkbox" id="toggleControl{{ loop.index }}" class="toggle">
<div class="toggle-container">
<p>{{ product.description }}</p>
<p>{% if product.img %}
<img src="{{ product.img }}">
{% else %}
<img src="/static/img/default_product.svg">
{% endif %}</p>
<label for="toggleControl{{ loop.index }}" class="toggle-label">Fermer</label>
</div>
</th>
<td>{{ product.price }} €</td><td><input type="number" name="{{ product.ref }}" value="{{ order.get_quantity(product) }}"></td></tr>
{% endfor %} {% endfor %}
</table> </table>
<p>Total: {{ order.total(delivery.products) if order else 0 }} €</p> <p>Total: {{ order.total(delivery.products) if order else 0 }} €</p>

View file

@ -1,23 +1,24 @@
import os import os
from datetime import timedelta
import pytest import pytest
from roll.extensions import traceback from roll.extensions import traceback
from kaba import app as kaba_app from kaba import app as kaba_app
from kaba import config as kconfig
from kaba import utils from kaba import utils
from kaba.models import Delivery, Person from kaba.models import Delivery, Person
def pytest_configure(config): def pytest_configure(config):
from kaba import models os.environ["KABA_DATA_ROOT"] = "tmp/db"
models.Base.ROOT = "tmp/db" kconfig.init()
assert str(kconfig.DATA_ROOT) == "tmp/db"
# def pytest_runtest_setup(item): def pytest_runtest_setup(item):
# # assert get_db().name == "test_eurordis" for path in Delivery.get_root().glob("*.yml"):
# for cls in [Delivery]: path.unlink()
# collection = cls.collection
# collection.drop()
@pytest.fixture @pytest.fixture
@ -29,7 +30,9 @@ def app(): # Requested by Roll testing utilities.
@pytest.fixture @pytest.fixture
def delivery(): def delivery():
return Delivery( return Delivery(
producer="Andines", when=utils.utcnow(), order_before=utils.utcnow() producer="Andines",
when=utils.utcnow() + timedelta(days=10),
order_before=utils.utcnow() + timedelta(days=7),
) )

View file

@ -1,3 +1,5 @@
from datetime import timedelta
import pytest import pytest
from kaba import utils from kaba import utils
@ -19,6 +21,13 @@ def test_wrong_datetime_raise_valueerror():
Delivery(producer="Andines", order_before=utils.utcnow(), when="pouet") Delivery(producer="Andines", order_before=utils.utcnow(), when="pouet")
def test_delivery_is_open_when_order_before_is_in_the_future(delivery):
delivery.order_before = utils.utcnow() + timedelta(hours=1)
assert delivery.is_open
delivery.order_before = utils.utcnow() - timedelta(hours=1)
assert not delivery.is_open
def test_can_create_product(): def test_can_create_product():
product = Product(name="Lait 1.5L", ref="123", price=1.5) product = Product(name="Lait 1.5L", ref="123", price=1.5)
assert product.ref == "123" assert product.ref == "123"