diff --git a/app.py b/app.py index 5c817c5..beed05b 100644 --- a/app.py +++ b/app.py @@ -496,10 +496,8 @@ for _bp_module, _bp_name in [ except Exception as _e: sys_log.error(f"[Blueprint] ❌ {_bp_name} 註冊失敗: {_e}") -# V-Fix: 註冊 slugify 函數供模板使用,解決 'slugify is undefined' 錯誤 -def slugify(text): - if not text: return "" - return str(text).replace(' ', '_').replace(':', '').replace('!', '').replace('?', '').replace('/', '').replace('&', '').replace('(', '').replace(')', '').replace('+', '_').replace('.', '_').replace('%', '').replace("'", "") +# V-Fix: 註冊 slugify 函數供模板使用(實作搬至 utils/text_helpers.py) +from utils.text_helpers import slugify # noqa: E402 LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = "服務啟動中..." @@ -552,35 +550,18 @@ def load_scheduler_stats(): # ================= 🛠️ 數據處理核心 (封裝) ================= -def get_color_for_string(s): - """為字串生成一個穩定且美觀的 HSL 顏色""" - if not s: return "hsl(0, 0%, 85%)" # 預設灰色 - # 使用 md5 hash 確保顏色穩定,並映射到 HSL 色彩空間以獲得柔和色彩 - hash_val = int(hashlib.md5(s.encode('utf-8'), usedforsecurity=False).hexdigest(), 16) - hue = hash_val % 360 - return f"hsl({hue}, 60%, 88%)" +# 純工具:實作已搬至 utils/text_helpers.py +from utils.text_helpers import ( # noqa: E402 + get_color_for_string, + extract_snapshot_date_from_filename, + number_format as _number_format, +) -def extract_snapshot_date_from_filename(filename): - """從檔名提取日期:即時業績_當日_20260111.xlsx → 2026-01-11""" - match = re.search(r'(\d{8})', filename) - if match: - date_str = match.group(1) # '20260111' - try: - # 轉換為 YYYY-MM-DD 格式 - year = date_str[:4] - month = date_str[4:6] - day = date_str[6:8] - return f"{year}-{month}-{day}" - except: - return None - return None @app.template_filter('number_format') def number_format_filter(value): - """V9.61: 將數字格式化,加上千分位符號。""" - if isinstance(value, (int, float)): - return "{:,.0f}".format(value) - return value + """Jinja filter wrapper — 實作見 utils.text_helpers.number_format。""" + return _number_format(value) # V-Refactor: 將 find_col 移至全域,方便多個函式共用 from utils.df_helpers import find_col # noqa: E402 diff --git a/utils/text_helpers.py b/utils/text_helpers.py new file mode 100644 index 0000000..11d0df5 --- /dev/null +++ b/utils/text_helpers.py @@ -0,0 +1,58 @@ +"""文字 / 顏色 / 檔名 / 數字格式化工具。 + +從 app.py 抽出的純函數,無外部副作用。 +""" +import hashlib +import re + + +def slugify(text): + """將任意字串轉為適合用於 HTML id / URL 的 slug。""" + if not text: + return "" + return ( + str(text) + .replace(' ', '_') + .replace(':', '') + .replace('!', '') + .replace('?', '') + .replace('/', '') + .replace('&', '') + .replace('(', '') + .replace(')', '') + .replace('+', '_') + .replace('.', '_') + .replace('%', '') + .replace("'", "") + ) + + +def get_color_for_string(s): + """為字串生成一個穩定且美觀的 HSL 顏色(柔和淺色,適合背景)。""" + if not s: + return "hsl(0, 0%, 85%)" # 預設灰色 + hash_val = int(hashlib.md5(s.encode('utf-8'), usedforsecurity=False).hexdigest(), 16) + hue = hash_val % 360 + return f"hsl({hue}, 60%, 88%)" + + +def extract_snapshot_date_from_filename(filename): + """從檔名提取 8 碼日期:即時業績_當日_20260111.xlsx → 2026-01-11。""" + match = re.search(r'(\d{8})', filename) + if not match: + return None + date_str = match.group(1) + try: + year = date_str[:4] + month = date_str[4:6] + day = date_str[6:8] + return f"{year}-{month}-{day}" + except Exception: + return None + + +def number_format(value): + """將數字格式化,加上千分位符號(無小數)。非數字直接回傳原值。""" + if isinstance(value, (int, float)): + return "{:,.0f}".format(value) + return value