iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
佛心分享-IT 人技術創業

Berry AI:從零開始打造全美第一的得來速 Vision AI系列 第 22 篇

從 xlsx 樣板到 Superset 看板:營運報表如何轉成 PDF 寄到客戶信箱

  • 分享至 

  • xImage
  •  

昨天說明了營運指標如何計算,今天說明營運報表如何寄送。客戶的營運主管不見得每天登入看板 (dashboard),許多人需要的是每天早上準時出現在信箱裡的報表。負責產出 PDF 與寄信的,是我們的內部服務 — Pigeon。

在 Pigeon 之前,這份報表經歷過一段以 xlsx 樣板產出的時期。

xlsx 樣板時期

三年多前,排程寄送報表的功能上線:後端定時計算數字、套用 xlsx 樣板,再由 Amazon SES (Simple Email Service,AWS 的寄信服務) 以附件寄出。

後來多門市報表日漸複雜,欄位排列、儲存格樣式、數值色階等格式設定,大量寫在 Python 程式碼裡。PM 要調整欄位順序或顏色,流程是開單、工程師修改樣板、程式碼審查、部署。報表格式又經常需要微調,每一次都要由工程師處理。

以 Selenium 將 Superset 看板轉成圖片

Day 18 介紹的 BI 動態看板遷移到 Superset 後,調整圖表比修改 xlsx 樣板快得多。因此我們決定,信箱裡的報表直接沿用看板的版面。

2024 年 11 月,Pigeon 上線接手這項工作。整體流程如下:瀏覽器自動化工具 Selenium 控制 headless Chrome (不開視窗執行的 Chrome),登入 Superset、套用日期與門市篩選,再把看板轉成圖片。Python 端以 Pillow 將圖片封裝成 PDF、存進 S3,寄送時再由 SES 附上檔案。何時產生、寄給誰,由後端的排程決定。

Pigeon 的 API 與瀏覽器分開部署。API 透過 Selenium Hub (Selenium Grid 中負責分派瀏覽器 session 的節點),把報表的瀏覽器工作分派到 Chromium Node;登入 Superset、套用篩選與載入頁面,都在 Node 裡執行。

Pigeon 架構圖:後端排程呼叫 Pigeon API,API 透過 Selenium Hub 把工作分派給多個 Chromium Node,Node 開啟 Superset 看板並載入畫面,結果以 JPEG 或 PDF 回傳 API,再以 PDF 存進 S3

固定高度表格的展開

直接對看板截圖會遇到一個問題:看板上的表格高度固定,超出的列在表格內捲動。使用者在網頁上可以往下捲,截圖只會取得目前可見的範圍,因此每張表只留下開頭幾列。

所以 Pigeon 截圖前會依看板進行前置處理,例如把表格的顯示列數設為 All。等內容載入後,再找出含有表格的區塊,解除高度、寬度與 overflow 的限制,讓內容完整展開。DOM 操作簡化後大致如下:

document.querySelectorAll(".dragdroppable-row").forEach((el) => {
  if (!(el.querySelector("tr") && el.querySelector("td"))) return;
  [el, ...el.querySelectorAll("div, table")].forEach((node) => {
    node.style.height = "auto";
    node.style.width = "auto";
    node.style.overflow = "unset";
  });
});

.dragdroppable-row 是 Superset 看板中每一列圖表的外層容器,同時含有 tr 與 td 的列才視為表格。

實際產生圖片時,Pigeon 會在頁面中注入 html-to-image,把看板的 DOM 轉成 JPEG,再交給 Pillow。這條路徑輸出的 PDF,內容是一張很長的圖片。

大型報表與 html-to-image 的尺寸上限

這套做法使用一年多後,遇到了大型報表。

html-to-image 的做法是把 DOM 包進 SVG 的 foreignObject,再轉成圖片。報表超過約一百家店、一千五百個儲存格後,會超過一個沒有文件記載的 foreignObject 尺寸上限,轉圖因此失敗。

為了解決這個問題,2026 年 4 月我們加入 Chrome DevTools Protocol (CDP) 的 Page.printToPDF,直接使用 Chrome 的列印功能產出 PDF,省去把整份看板先轉成 JPEG 的步驟。

列印前仍然要等圖表載入、展開表格,printToPDF 不會自動補上尚未載入的資料。另外,列印樣式會影響顏色,因此我們同時設定 Emulation.setEmulatedMedia 的 { media: "screen" } 與 printBackground: true,保留畫面上的表頭與儲存格底色,並依看板尺寸設定紙張大小。

現在兩條路徑並存:不超過 100 家店的報表走 html-to-image,超過 100 家的走 CDP。兩條路徑最後都產出 PDF,後續的儲存與寄送流程共用。

小結

報表格式移到 Superset 後,Pigeon 負責把看板轉成 PDF:以 Selenium 控制 headless Chrome 登入、套用篩選並展開表格,100 家店以內的報表用 html-to-image 轉圖,更大的報表用 CDP 直接列印。同一份看板同時供網頁與信箱使用,PM 在看板裡調整欄位、顏色與圖表,下一次產出的 PDF 就會跟著更新,不需要為這些調整重新部署 Pigeon,工程師也不必再維護另一份 xlsx 樣板。

我們也開始在內部的 Superset 環境接上 MCP,搭配撰寫好的 skills,讓 AI 協助 PM 調整看板,加快修改的速度。

明天的 Day 23 回到門市,說明如何監控掛在廚房牆上的即時看板螢幕。

參考資料


本系列由 Berry AI 工程團隊出品。更多工程實戰紀錄都在 Berry AI 技術部落格。


上一篇
從 6,000 行 Python 到 dbt:得來速營運指標的計算如何遷移
下一篇
螢幕上真的有畫面嗎?使用 Chrome DevTools Protocol 確認門市看板的狀態
系列文
Berry AI:從零開始打造全美第一的得來速 Vision AI 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言