Add pagination support for trend figures and update rendering logic for individual plots. Introduce make_trend_figure_paged function and modify render_any to handle page and tags per page inputs.
Browse files
app.py
CHANGED
|
@@ -331,6 +331,7 @@ def make_trend_figure(
|
|
| 331 |
thr_mode: str, # "excel" or "auto"
|
| 332 |
date_min: Optional[str] = None,
|
| 333 |
date_max: Optional[str] = None,
|
|
|
|
| 334 |
) -> Optional[go.Figure]:
|
| 335 |
if df is None or not process_name:
|
| 336 |
return None
|
|
@@ -357,7 +358,7 @@ def make_trend_figure(
|
|
| 357 |
for r in recs:
|
| 358 |
tag = extract_measure_tag(r["item"])
|
| 359 |
groups.setdefault(tag, []).append(r)
|
| 360 |
-
tags = list(groups.keys())
|
| 361 |
if not tags:
|
| 362 |
return None
|
| 363 |
|
|
@@ -447,6 +448,41 @@ def make_trend_figure(
|
|
| 447 |
)
|
| 448 |
return fig
|
| 449 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
# ======================================
|
| 451 |
# 新規:計測項目タグごとに個別Figure
|
| 452 |
# ======================================
|
|
@@ -648,30 +684,32 @@ def render_figs(process_name: str, items: List[str], thr_mode: str, date_min, da
|
|
| 648 |
return "⚠ 図を生成できませんでした(データ無し or 条件不一致)", []
|
| 649 |
return f"✅ {process_name}: {len(figs)}枚のトレンド図を生成しました(計測項目タグごと)", figs
|
| 650 |
|
| 651 |
-
def render_any(process_name: str, items: List[str], display_mode: str, thr_mode_label: str,
|
| 652 |
-
|
| 653 |
-
表示形式に応じて Plot
|
| 654 |
-
"""
|
| 655 |
if G_DF is None:
|
| 656 |
-
return "⚠ データ未読み込み", gr.update(visible=False), gr.update(
|
| 657 |
if not process_name:
|
| 658 |
-
return "⚠ プロセスを選択してください", gr.update(visible=False), gr.update(
|
| 659 |
if not items:
|
| 660 |
-
return "⚠ 項目を選択してください", gr.update(visible=False), gr.update(
|
| 661 |
|
| 662 |
mode = "excel" if str(thr_mode_label).startswith("excel") else "auto"
|
| 663 |
|
| 664 |
if str(display_mode).startswith("サブプロット"):
|
| 665 |
fig = make_trend_figure(G_DF, G_PROCESS_MAP, process_name, items, G_THRESHOLDS_DF, mode, date_min, date_max)
|
| 666 |
if fig is None:
|
| 667 |
-
return "⚠ 図を生成できませんでした(データ無し or 条件不一致)", gr.update(visible=False), gr.update(
|
| 668 |
-
return "✅ トレンド図(1枚サブプロット)を生成しました", gr.update(value=fig, visible=True), gr.update(
|
| 669 |
else:
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
|
|
|
|
|
|
|
|
|
| 675 |
|
| 676 |
# ======================================
|
| 677 |
# UI
|
|
@@ -700,7 +738,7 @@ with gr.Blocks(css="""
|
|
| 700 |
|
| 701 |
# 表示形式の切り替え
|
| 702 |
display_mode = gr.Radio(
|
| 703 |
-
["サブプロット(1枚)", "個別(
|
| 704 |
value="サブプロット(1枚)",
|
| 705 |
label="表示形式"
|
| 706 |
)
|
|
@@ -716,10 +754,13 @@ with gr.Blocks(css="""
|
|
| 716 |
btn_render = gr.Button("トレンド図を生成", variant="primary")
|
| 717 |
|
| 718 |
msg = gr.Markdown()
|
| 719 |
-
#
|
| 720 |
-
plot = gr.Plot(label="
|
| 721 |
-
#
|
| 722 |
-
|
|
|
|
|
|
|
|
|
|
| 723 |
|
| 724 |
# コールバック接続
|
| 725 |
# 既定CSVの手動代入は不要(生成時に付与済み)
|
|
@@ -747,9 +788,9 @@ with gr.Blocks(css="""
|
|
| 747 |
|
| 748 |
# 5) 図生成
|
| 749 |
btn_render.click(
|
| 750 |
-
fn=lambda proc, items, disp_mode, mode, dmin, dmax: render_any(proc, items, disp_mode, mode, dmin, dmax),
|
| 751 |
-
inputs=[process_dd, items_cb, display_mode, thr_mode, date_min, date_max],
|
| 752 |
-
outputs=[msg, plot,
|
| 753 |
)
|
| 754 |
|
| 755 |
if __name__ == "__main__":
|
|
|
|
| 331 |
thr_mode: str, # "excel" or "auto"
|
| 332 |
date_min: Optional[str] = None,
|
| 333 |
date_max: Optional[str] = None,
|
| 334 |
+
_force_tags: Optional[List[str]] = None, # ← 追加:ページ分割用に表示するタグを指定
|
| 335 |
) -> Optional[go.Figure]:
|
| 336 |
if df is None or not process_name:
|
| 337 |
return None
|
|
|
|
| 358 |
for r in recs:
|
| 359 |
tag = extract_measure_tag(r["item"])
|
| 360 |
groups.setdefault(tag, []).append(r)
|
| 361 |
+
tags = list(groups.keys()) if _force_tags is None else _force_tags
|
| 362 |
if not tags:
|
| 363 |
return None
|
| 364 |
|
|
|
|
| 448 |
)
|
| 449 |
return fig
|
| 450 |
|
| 451 |
+
# 追加:ページ分割版(tags_per_pageごとに make_trend_figure を呼ぶだけ)
|
| 452 |
+
def make_trend_figure_paged(
|
| 453 |
+
df: pd.DataFrame,
|
| 454 |
+
process_map: Dict[str, List[dict]],
|
| 455 |
+
process_name: str,
|
| 456 |
+
selected_items: List[str],
|
| 457 |
+
thr_df: Optional[pd.DataFrame],
|
| 458 |
+
thr_mode: str,
|
| 459 |
+
date_min: Optional[str],
|
| 460 |
+
date_max: Optional[str],
|
| 461 |
+
page: int,
|
| 462 |
+
tags_per_page: int,
|
| 463 |
+
) -> Tuple[Optional[go.Figure], int, List[str]]:
|
| 464 |
+
# 対象タグの全一覧を作る
|
| 465 |
+
recs = process_map.get(process_name, [])
|
| 466 |
+
if not recs:
|
| 467 |
+
return None, 0, []
|
| 468 |
+
selected_items_set = set([normalize(x) for x in (selected_items or [])])
|
| 469 |
+
recs = [r for r in recs if normalize(r["item"]) in selected_items_set]
|
| 470 |
+
if not recs:
|
| 471 |
+
return None, 0, []
|
| 472 |
+
groups: Dict[str, List[dict]] = {}
|
| 473 |
+
for r in recs:
|
| 474 |
+
groups.setdefault(extract_measure_tag(r["item"]), []).append(r)
|
| 475 |
+
all_tags = list(groups.keys())
|
| 476 |
+
total_pages = max(1, int(np.ceil(len(all_tags) / max(1, tags_per_page))))
|
| 477 |
+
page = int(max(1, min(page, total_pages)))
|
| 478 |
+
start = (page - 1) * tags_per_page
|
| 479 |
+
end = start + tags_per_page
|
| 480 |
+
tags_slice = all_tags[start:end]
|
| 481 |
+
fig = make_trend_figure(
|
| 482 |
+
df, process_map, process_name, selected_items, thr_df, thr_mode, date_min, date_max, _force_tags=tags_slice
|
| 483 |
+
)
|
| 484 |
+
return fig, total_pages, all_tags
|
| 485 |
+
|
| 486 |
# ======================================
|
| 487 |
# 新規:計測項目タグごとに個別Figure
|
| 488 |
# ======================================
|
|
|
|
| 684 |
return "⚠ 図を生成できませんでした(データ無し or 条件不一致)", []
|
| 685 |
return f"✅ {process_name}: {len(figs)}枚のトレンド図を生成しました(計測項目タグごと)", figs
|
| 686 |
|
| 687 |
+
def render_any(process_name: str, items: List[str], display_mode: str, thr_mode_label: str,
|
| 688 |
+
date_min, date_max, page: int, tpp: int):
|
| 689 |
+
"""表示形式に応じて Plot を返す(個別はページ分割)。"""
|
|
|
|
| 690 |
if G_DF is None:
|
| 691 |
+
return "⚠ データ未読み込み", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
|
| 692 |
if not process_name:
|
| 693 |
+
return "⚠ プロセスを選択してください", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
|
| 694 |
if not items:
|
| 695 |
+
return "⚠ 項目を選択してください", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
|
| 696 |
|
| 697 |
mode = "excel" if str(thr_mode_label).startswith("excel") else "auto"
|
| 698 |
|
| 699 |
if str(display_mode).startswith("サブプロット"):
|
| 700 |
fig = make_trend_figure(G_DF, G_PROCESS_MAP, process_name, items, G_THRESHOLDS_DF, mode, date_min, date_max)
|
| 701 |
if fig is None:
|
| 702 |
+
return "⚠ 図を生成できませんでした(データ無し or 条件不一致)", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
|
| 703 |
+
return "✅ トレンド図(1枚サブプロット)を生成しました", gr.update(value=fig, visible=True), gr.update(visible=False), gr.update(visible=False)
|
| 704 |
else:
|
| 705 |
+
fig, total_pages, all_tags = make_trend_figure_paged(
|
| 706 |
+
G_DF, G_PROCESS_MAP, process_name, items, G_THRESHOLDS_DF, mode, date_min, date_max,
|
| 707 |
+
page=int(page), tags_per_page=int(tpp)
|
| 708 |
+
)
|
| 709 |
+
if fig is None:
|
| 710 |
+
return "⚠ 図を生成できませんでした(データ無し or 条件不一致)", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
|
| 711 |
+
info = f"タグ総数: {len(all_tags)} | ページ {int(max(1,min(page,total_pages)))} / {total_pages} | タグ/ページ={int(tpp)}"
|
| 712 |
+
return "✅ 個別(ページ分割)を描画しました", gr.update(value=fig, visible=True), gr.update(value=info, visible=True), gr.update(visible=True)
|
| 713 |
|
| 714 |
# ======================================
|
| 715 |
# UI
|
|
|
|
| 738 |
|
| 739 |
# 表示形式の切り替え
|
| 740 |
display_mode = gr.Radio(
|
| 741 |
+
["サブプロット(1枚)", "個別(ページ分割)"],
|
| 742 |
value="サブプロット(1枚)",
|
| 743 |
label="表示形式"
|
| 744 |
)
|
|
|
|
| 754 |
btn_render = gr.Button("トレンド図を生成", variant="primary")
|
| 755 |
|
| 756 |
msg = gr.Markdown()
|
| 757 |
+
# 表示領域:Plot 1枚を使い回す(個別=ページ分割も同じPlotを再描画)
|
| 758 |
+
plot = gr.Plot(label="トレンド図(タグ別)", show_label=True, visible=True)
|
| 759 |
+
# ページ分割用コントロール(個別モード時のみ使用)
|
| 760 |
+
with gr.Row():
|
| 761 |
+
tags_per_page = gr.Slider(1, 12, value=8, step=1, label="タグ/ページ(個別モード)")
|
| 762 |
+
page_no = gr.Number(value=1, label="ページ(1〜)", precision=0)
|
| 763 |
+
page_info = gr.Markdown(visible=False)
|
| 764 |
|
| 765 |
# コールバック接続
|
| 766 |
# 既定CSVの手動代入は不要(生成時に付与済み)
|
|
|
|
| 788 |
|
| 789 |
# 5) 図生成
|
| 790 |
btn_render.click(
|
| 791 |
+
fn=lambda proc, items, disp_mode, mode, dmin, dmax, p, tpp: render_any(proc, items, disp_mode, mode, dmin, dmax, p, tpp),
|
| 792 |
+
inputs=[process_dd, items_cb, display_mode, thr_mode, date_min, date_max, page_no, tags_per_page],
|
| 793 |
+
outputs=[msg, plot, page_info, page_no],
|
| 794 |
)
|
| 795 |
|
| 796 |
if __name__ == "__main__":
|