diff --git a/app/main.py b/app/main.py index 4c11cdf..0226481 100644 --- a/app/main.py +++ b/app/main.py @@ -88,6 +88,35 @@ def _wants_add_next(submit_action: str | None) -> bool: return submit_action == "save_and_add_next" +def _format_average(total: int, divisor: int) -> str: + if divisor == 0: + return "0.0" + return f"{total / divisor:.1f}" + + +def _build_boxes_overview_summary(db: Session) -> dict[str, int | str]: + box_count = db.query(func.count(Box.id)).scalar() or 0 + item_count = db.query(func.count(Item.id)).scalar() or 0 + subitem_count = db.query(func.count(SubItem.id)).scalar() or 0 + container_item_count = ( + db.query(func.count(Item.id)) + .filter(Item.is_container.is_(True)) + .scalar() + or 0 + ) + + return { + "box_count": box_count, + "item_count": item_count, + "item_and_subitem_count": item_count + subitem_count, + "avg_items_per_box": _format_average(item_count, box_count), + "avg_subitems_per_container_item": _format_average( + subitem_count, + container_item_count, + ), + } + + def _build_search_results(db: Session, query: str) -> list[dict]: keyword = f"%{query.lower()}%" results: list[dict] = [] @@ -231,10 +260,11 @@ def create_app() -> FastAPI: @app.get("/boxes") def list_boxes(request: Request, db: Session = Depends(get_db)): boxes = db.query(Box).order_by(Box.id.desc()).all() + summary = _build_boxes_overview_summary(db) return templates.TemplateResponse( request=request, name="boxes/index.html", - context={"page_title": "箱子", "boxes": boxes}, + context={"page_title": "箱子", "boxes": boxes, "summary": summary}, ) @app.get("/boxes/new") diff --git a/app/static/style.css b/app/static/style.css index d3589bc..8dcd774 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -231,6 +231,25 @@ button:focus-visible { gap: 10px; } +.summary-section { + margin-bottom: 18px; +} + +.summary-block { + padding-top: 12px; + padding-bottom: 12px; +} + +.summary-list { + margin: 0; + padding-left: 18px; + color: #333; +} + +.summary-list li + li { + margin-top: 6px; +} + .compact-row { display: grid; grid-template-columns: auto 1fr auto; diff --git a/app/templates/boxes/index.html b/app/templates/boxes/index.html index b94687e..3394e69 100644 --- a/app/templates/boxes/index.html +++ b/app/templates/boxes/index.html @@ -15,6 +15,22 @@ 新建箱子 +
+
+

当前概览

+

快速查看当前装箱记录的核心统计。

+
+
+ +
+
+ {% if boxes %}
{% for box in boxes %} diff --git a/tests/test_app.py b/tests/test_app.py index e959ad2..e735356 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -178,6 +178,53 @@ def test_boxes_overview_renders_cleanly_when_note_is_empty(client, db_session): assert "状态:" not in response.text +def test_boxes_overview_summary_shows_expected_counts_and_averages(client, db_session): + first_box = Box(name="卧室箱") + second_box = Box(name="书房箱") + regular_item = Item(name="书", box=first_box, is_container=False) + container_item = Item(name="配件袋", box=first_box, is_container=True) + second_container_item = Item(name="文件袋", box=second_box, is_container=True) + db_session.add_all( + [ + first_box, + second_box, + regular_item, + container_item, + second_container_item, + SubItem(name="转接头", parent_item=container_item), + SubItem(name="数据线", parent_item=container_item), + ] + ) + db_session.commit() + + response = client.get("/boxes") + + assert response.status_code == 200 + assert "当前概览" in response.text + assert "箱子总数" in response.text + assert "物品总数(不含子物品)" in response.text + assert "物品总数(含子物品)" in response.text + assert "平均每箱物品数" in response.text + assert "平均每个容器型 Item 的子物品数" in response.text + assert "箱子总数:2" in response.text + assert "物品总数(不含子物品):3" in response.text + assert "物品总数(含子物品):5" in response.text + assert "平均每箱物品数:1.5" in response.text + assert "平均每个容器型 Item 的子物品数:1.0" in response.text + + +def test_boxes_overview_summary_handles_empty_data_safely(client): + response = client.get("/boxes") + + assert response.status_code == 200 + assert "当前概览" in response.text + assert "箱子总数:0" in response.text + assert "物品总数(不含子物品):0" in response.text + assert "物品总数(含子物品):0" in response.text + assert "平均每箱物品数:0.0" in response.text + assert "平均每个容器型 Item 的子物品数:0.0" in response.text + + def test_can_create_box(client, db_session): response = create_box(client, name="Kitchen Box")