Skip to content

Commit 3d49fd1

Browse files
committed
refactor: modernize demo loader and clean up report CSS
- Rewrite scripts/load_inventory_demo.py for Python 3.10+ with PEP 604 union types, built-in generic annotations on every method and class attribute, and from-x-import-y imports only (argparse, logging, random, sys, traceback, typing, budy). Drop the implicit appier import that was never used, and switch the log formatting over to f-strings. - Extend the module docstring with Options, Examples and Requires sections so the invocation contract matches the pattern used by omni's scripts/generate_docs.py. - Remove the section-divider comments ("/* -- Header -- */" etc.) from src/budy/static/css/report.css; selector grouping already reads through the blank-line separators, and the labels were beginning to drift out of sync with the rule groupings.
1 parent 5657937 commit 3d49fd1

2 files changed

Lines changed: 71 additions & 68 deletions

File tree

scripts/load_inventory_demo.py

Lines changed: 69 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,39 @@
1212
product across different orders, so that the row explosion and
1313
ordering behavior of the inventory report can be verified end-to-end.
1414
15-
Run from the project root:
16-
17-
python scripts/load_inventory_demo.py --verbose
18-
1915
The script appends to the currently configured MongoDB database. No
2016
existing data is removed.
21-
"""
2217
23-
import logging
24-
import random
18+
Run from the project root with:
19+
python scripts/load_inventory_demo.py [options]
20+
21+
Options:
22+
--orders=N Number of orders to create (default: 12)
23+
--paid-ratio=R Fraction of orders that should be marked paid
24+
(default: 0.75)
25+
--seed=N Seed for the pseudo random generator
26+
(default: 42)
27+
--verbose, -v Enable verbose output
28+
29+
Examples:
30+
python scripts/load_inventory_demo.py
31+
python scripts/load_inventory_demo.py -v
32+
python scripts/load_inventory_demo.py --orders=30 -v
33+
python scripts/load_inventory_demo.py --orders=20 --paid-ratio=0.5 -v
34+
python scripts/load_inventory_demo.py --seed=1234 -v
35+
36+
Requires:
37+
Python 3.10+
38+
"""
2539

40+
from argparse import ArgumentParser, Namespace
41+
from logging import ERROR
42+
from random import Random
2643
from sys import exit
2744
from traceback import print_exc
28-
from argparse import ArgumentParser, Namespace
29-
30-
import appier
45+
from typing import Any
3146

32-
import budy
47+
from budy import Address, BudyApp, Order, OrderLine, Product
3348

3449

3550
class InventoryDemoLoader:
@@ -38,7 +53,7 @@ class InventoryDemoLoader:
3853
of products and orders, tailored to exercise the inventory report.
3954
"""
4055

41-
PRODUCTS = (
56+
PRODUCTS: tuple[dict[str, Any], ...] = (
4257
dict(
4358
sku="SHIRT-BLUE",
4459
short_description="Blue Cotton Shirt",
@@ -97,17 +112,17 @@ class InventoryDemoLoader:
97112
""" The catalog of demo products, spread across the supported
98113
genders and with enough on-hand stock to absorb any order mix """
99114

100-
SIZES = (36, 38, 40, 42, 44)
115+
SIZES: tuple[int, ...] = (36, 38, 40, 42, 44)
101116
""" The set of sizes randomly assigned to order lines, picked
102117
small enough so that collisions across lines are common """
103118

104119
def __init__(
105120
self,
106-
orders_count=12,
107-
paid_ratio=0.75,
108-
seed=42,
109-
verbose=True,
110-
):
121+
orders_count: int = 12,
122+
paid_ratio: float = 0.75,
123+
seed: int = 42,
124+
verbose: bool = True,
125+
) -> None:
111126
"""
112127
Initializes the inventory demo loader.
113128
@@ -120,12 +135,12 @@ def __init__(
120135

121136
self.orders_count = orders_count
122137
self.paid_ratio = paid_ratio
123-
self.random = random.Random(seed)
138+
self.random = Random(seed)
124139
self.verbose = verbose
125-
self.app = None
126-
self.products = []
140+
self.app: BudyApp | None = None
141+
self.products: list[Product] = []
127142

128-
def log(self, message, level="INFO"):
143+
def log(self, message: str, level: str = "INFO") -> None:
129144
"""
130145
Logs a message if verbose mode is enabled.
131146
@@ -136,30 +151,30 @@ def log(self, message, level="INFO"):
136151
if not self.verbose:
137152
return
138153
for part in message.split("\n"):
139-
print("[%s] %s" % (level, part))
154+
print(f"[{level}] {part}")
140155

141-
def init_app(self):
156+
def init_app(self) -> None:
142157
"""
143158
Initializes the underlying Budy application so that model
144159
operations can be executed against the configured database.
145160
"""
146161

147162
self.log("Initializing Budy application...")
148-
self.app = budy.BudyApp(level=logging.ERROR)
163+
self.app = BudyApp(level=ERROR)
149164
self.log("Budy application initialized successfully")
150165

151-
def stop_app(self):
166+
def stop_app(self) -> None:
152167
"""
153168
Unloads the Budy application, ensuring that any open
154169
resources (scheduler, database connection) are released.
155170
"""
156171

157-
if self.app == None:
172+
if self.app is None:
158173
return
159174
self.app.unload()
160175
self.app = None
161176

162-
def load_products(self):
177+
def load_products(self) -> None:
163178
"""
164179
Loads the demo products into the database.
165180
"""
@@ -173,12 +188,12 @@ def load_products(self):
173188
for key, value in product_data.items()
174189
if key not in ("thumbnail_url", "image_url")
175190
}
176-
product = budy.Product(**base_data)
191+
product = Product(**base_data)
177192
product.save()
178193
self.products.append(product)
179-
self.log(" + Created Product: %s (%s)" % (product.sku, product.gender))
194+
self.log(f" + Created Product: {product.sku} ({product.gender})")
180195

181-
def load_orders(self):
196+
def load_orders(self) -> None:
182197
"""
183198
Loads the demo orders into the database, creating a mix of
184199
paid and unpaid orders and exploding multi-size product
@@ -193,7 +208,7 @@ def load_orders(self):
193208
paid = index < paid_count
194209
self._create_order(index, paid)
195210

196-
def load_product_images(self):
211+
def load_product_images(self) -> None:
197212
"""
198213
Applies the pre-defined thumbnail and full-size image URLs to
199214
each product by bypassing the normal save pipeline, so that
@@ -204,10 +219,10 @@ def load_product_images(self):
204219
self.log("\n" + "=" * 60)
205220
self.log("Loading Product Images")
206221
self.log("=" * 60)
207-
collection = budy.Product._collection()
208-
by_sku = dict(
209-
(product_data["sku"], product_data) for product_data in self.PRODUCTS
210-
)
222+
collection = Product._collection()
223+
by_sku: dict[str, dict[str, Any]] = {
224+
product_data["sku"]: product_data for product_data in self.PRODUCTS
225+
}
211226
for product in self.products:
212227
data = by_sku.get(product.sku, {})
213228
thumbnail_url = data.get("thumbnail_url")
@@ -220,9 +235,9 @@ def load_product_images(self):
220235
)
221236
product.thumbnail_url = thumbnail_url
222237
product.image_url = image_url
223-
self.log(" + Applied Images: %s" % product.sku)
238+
self.log(f" + Applied Images: {product.sku}")
224239

225-
def _create_order(self, index, paid):
240+
def _create_order(self, index: int, paid: bool) -> None:
226241
"""
227242
Creates a single demo order with a random set of lines and,
228243
if requested, marks it as paid so that it shows up under the
@@ -233,7 +248,7 @@ def _create_order(self, index, paid):
233248
:param paid: Whether the order should end up in the paid state.
234249
"""
235250

236-
order = budy.Order()
251+
order = Order()
237252
order.save()
238253

239254
lines_count = self.random.randint(2, 4)
@@ -245,10 +260,10 @@ def _create_order(self, index, paid):
245260
if self.random.random() < 0.35:
246261
self._add_line(order, product)
247262

248-
address = budy.Address(
263+
address = Address(
249264
first_name="Demo",
250-
last_name="Customer %d" % (index + 1),
251-
address="Rua Demo %d" % (index + 1),
265+
last_name=f"Customer {index + 1}",
266+
address=f"Rua Demo {index + 1}",
252267
city="Lisboa",
253268
postal_code="1000-001",
254269
country="PT",
@@ -258,24 +273,23 @@ def _create_order(self, index, paid):
258273

259274
order.shipping_address = address
260275
order.billing_address = address
261-
order.email = "demo%d@budy.test" % (index + 1)
276+
order.email = f"demo{index + 1}@budy.test"
262277
order.save()
263278

264279
if not paid:
265280
self.log(
266-
" + Created Order: %s (%d lines, unpaid)"
267-
% (order.reference, len(order.lines))
281+
f" + Created Order: {order.reference} "
282+
f"({len(order.lines)} lines, unpaid)"
268283
)
269284
return
270285

271286
order.mark_waiting_payment_s()
272287
order.mark_paid_s()
273288
self.log(
274-
" + Created Order: %s (%d lines, paid)"
275-
% (order.reference, len(order.lines))
289+
f" + Created Order: {order.reference} " f"({len(order.lines)} lines, paid)"
276290
)
277291

278-
def _add_line(self, order, product):
292+
def _add_line(self, order: Order, product: Product) -> None:
279293
"""
280294
Appends a new order line for the given product into the
281295
provided order, assigning a random quantity and size.
@@ -284,14 +298,14 @@ def _add_line(self, order, product):
284298
:param product: The product associated with the new line.
285299
"""
286300

287-
line = budy.OrderLine(quantity=float(self.random.randint(1, 3)))
301+
line = OrderLine(quantity=float(self.random.randint(1, 3)))
288302
line.product = product
289303
line.size = self.random.choice(self.SIZES)
290304
line.size_s = str(line.size)
291305
line.save()
292306
order.add_line_s(line)
293307

294-
def load_all_data(self):
308+
def load_all_data(self) -> bool:
295309
"""
296310
Loads all demo data into the database, ensuring that
297311
dependencies are loaded in the correct order.
@@ -310,18 +324,18 @@ def load_all_data(self):
310324
self.log("Demo Data Loading Complete!")
311325
self.log("=" * 60)
312326
self.log("\nSummary:")
313-
self.log(" - Products created: %d" % len(self.products))
314-
self.log(" - Orders created: %d" % self.orders_count)
327+
self.log(f" - Products created: {len(self.products)}")
328+
self.log(f" - Orders created: {self.orders_count}")
315329
return True
316330
except Exception as exception:
317-
self.log("Error loading demo data: %s" % exception, "ERROR")
331+
self.log(f"Error loading demo data: {exception}", "ERROR")
318332
print_exc()
319333
return False
320334
finally:
321335
self.stop_app()
322336

323337

324-
def parse_args():
338+
def parse_args() -> Namespace:
325339
"""
326340
Parses command line arguments.
327341
"""
@@ -353,7 +367,7 @@ def parse_args():
353367
return parser.parse_args()
354368

355369

356-
def main():
370+
def main() -> None:
357371
"""
358372
Main entry point for the CLI.
359373
"""

src/budy/static/css/report.css

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ body {
3232
padding: 60px 24px 80px 24px;
3333
}
3434

35-
/* -- Header -- */
3635
.report-header {
3736
background: #ffffff;
3837
border: 1px solid #e8e8e8;
@@ -129,7 +128,6 @@ body {
129128
font-size: 13px;
130129
}
131130

132-
/* -- Sections -- */
133131
.report-section {
134132
margin-bottom: 40px;
135133
}
@@ -160,7 +158,6 @@ body {
160158
text-transform: uppercase;
161159
}
162160

163-
/* -- Definition list -- */
164161
.report-dl {
165162
display: grid;
166163
grid-column-gap: 16px;
@@ -198,7 +195,6 @@ body {
198195
text-align: right;
199196
}
200197

201-
/* -- Paired definition lists -- */
202198
.report-dl-pair {
203199
display: grid;
204200
grid-column-gap: 24px;
@@ -222,7 +218,6 @@ body {
222218
margin-bottom: 12px;
223219
}
224220

225-
/* -- Table -- */
226221
.report-table {
227222
background: #ffffff;
228223
border: 1px solid #e8e8e8;
@@ -312,7 +307,6 @@ body {
312307
font-weight: 600;
313308
}
314309

315-
/* -- Thumbnail column -- */
316310
.report-table-thumbnail {
317311
padding: 8px 0px 8px 12px;
318312
vertical-align: middle;
@@ -334,7 +328,6 @@ body {
334328
width: 40px;
335329
}
336330

337-
/* -- Check column (print only) -- */
338331
.report-table-check {
339332
display: none;
340333
}
@@ -343,7 +336,6 @@ body {
343336
display: none;
344337
}
345338

346-
/* -- Footer -- */
347339
.report-footer {
348340
border-top: 1px solid #e0e0e0;
349341
color: #bdbdbd;
@@ -353,10 +345,7 @@ body {
353345
text-align: center;
354346
}
355347

356-
/* -- Print (A4 portrait, high contrast B&W) -- */
357348
@page {
358-
size: A4 portrait;
359-
margin: 10mm 10mm 10mm 10mm;
360349
@bottom-right {
361350
color: #000000;
362351
content: "Page " counter(page) " of " counter(pages);
@@ -477,8 +466,8 @@ body {
477466
}
478467
.report-table thead th {
479468
background: transparent;
480-
border-top: 1pt solid #000000;
481469
border-bottom: 1pt solid #000000;
470+
border-top: 1pt solid #000000;
482471
color: #000000;
483472
font-size: 8pt;
484473
padding: 3pt 5pt 3pt 5pt;
@@ -504,8 +493,8 @@ body {
504493
}
505494
.report-table tfoot td {
506495
background: transparent;
507-
border-top: 1pt solid #000000;
508496
border-bottom: 1pt solid #000000;
497+
border-top: 1pt solid #000000;
509498
color: #000000;
510499
font-size: 9pt;
511500
padding: 3pt 5pt 3pt 5pt;

0 commit comments

Comments
 (0)