CaiWuXiTong/scripts/import_store_lifecycle_sqlite.py

116 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""导入店铺存续时间表到 runtime/bills.sqlite。"""
from __future__ import annotations
import argparse
import json
import sqlite3
from datetime import date, datetime, time, timedelta
from pathlib import Path
from typing import Any
def text(value: Any) -> str | None:
if value is None:
return None
value = str(value).strip()
return value or None
def sqlite_time(value: Any) -> str | None:
if value in (None, ""):
return None
if isinstance(value, datetime):
return value.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(value, date):
return datetime.combine(value, time()).strftime("%Y-%m-%d %H:%M:%S")
if isinstance(value, (int, float)):
value = datetime(1899, 12, 30) + timedelta(days=float(value))
return value.strftime("%Y-%m-%d %H:%M:%S")
value = str(value).strip().replace("/", "-")
if value in {"存续中", "至今", "长期", "无"}:
return None
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d"):
try:
return datetime.strptime(value, fmt).strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
pass
raise ValueError(f"时间无法解析:{value!r}")
def main() -> int:
parser = argparse.ArgumentParser(description="导入店铺存续时间 Excel 到 SQLite")
parser.add_argument("file", type=Path, default=Path("/Users/dodo/Documents/店铺存续时间.xlsx"), nargs="?")
parser.add_argument("--db", type=Path, default=Path("runtime/bills.sqlite"))
parser.add_argument("--reset", action="store_true", help="清空已有店铺存续时间后重新导入")
args = parser.parse_args()
try:
import openpyxl
except ImportError as exc:
raise SystemExit("缺少 openpyxl,请使用项目提供的 bundled Python 运行此脚本。") from exc
if not args.file.is_file():
raise SystemExit(f"文件不存在:{args.file}")
args.db.parent.mkdir(parents=True, exist_ok=True)
db = sqlite3.connect(args.db)
db.executescript("""
CREATE TABLE IF NOT EXISTS store_lifecycles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
erp_store_name TEXT NOT NULL,
company_name TEXT,
effective_at TEXT,
expires_at TEXT,
source_file TEXT NOT NULL,
raw_json TEXT NOT NULL,
imported_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(erp_store_name, effective_at, expires_at)
);
CREATE INDEX IF NOT EXISTS idx_store_lifecycles_lookup
ON store_lifecycles(erp_store_name, effective_at, expires_at);
""")
if args.reset:
db.execute("DELETE FROM store_lifecycles")
workbook = openpyxl.load_workbook(args.file, read_only=True, data_only=True)
inserted = 0
try:
for sheet in workbook.worksheets:
rows = sheet.iter_rows(values_only=True)
header = next(rows, None)
if not header or all(value in (None, "") for value in header):
continue
headers = [text(value) or "" for value in header]
required = ["ERP系统店铺名称", "公司名称", "生效时间", "失效时间"]
missing = [name for name in required if name not in headers]
if missing:
raise SystemExit(f"{args.file.name}/{sheet.title} 缺少列:{', '.join(missing)}")
index = {name: headers.index(name) for name in required}
for row in rows:
if not row or all(value in (None, "") for value in row):
continue
get = lambda name: row[index[name]] if index[name] < len(row) else None
store = text(get("ERP系统店铺名称"))
if not store:
continue
raw = {headers[i]: row[i] for i in range(min(len(headers), len(row))) if row[i] not in (None, "")}
db.execute("""INSERT INTO store_lifecycles
(erp_store_name,company_name,effective_at,expires_at,source_file,raw_json)
VALUES (?,?,?,?,?,?)
ON CONFLICT(erp_store_name,effective_at,expires_at) DO UPDATE SET
company_name=excluded.company_name, source_file=excluded.source_file,
raw_json=excluded.raw_json, imported_at=CURRENT_TIMESTAMP""",
(store, text(get("公司名称")), sqlite_time(get("生效时间")),
sqlite_time(get("失效时间")), args.file.name,
json.dumps(raw, ensure_ascii=False, default=str)))
inserted += 1
finally:
workbook.close()
db.commit()
total = db.execute("SELECT COUNT(*) FROM store_lifecycles").fetchone()[0]
db.close()
print(f"导入完成:{args.db},本次处理 {inserted} 条,表内共 {total} 条")
return 0
if __name__ == "__main__":
raise SystemExit(main())