Track successful sync runs
This commit is contained in:
@@ -10,9 +10,11 @@ import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
from datetime import datetime, timezone
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlencode
|
||||
from zoneinfo import ZoneInfo
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import requests
|
||||
@@ -21,6 +23,7 @@ import requests
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
WORKBOOK_ID = os.getenv("WORKBOOK_ID", "1DPbdVsX3SG_kgDXs2j34mDAgplRSfcAWKKG9yqalFmw")
|
||||
PUBLISH_PATH = Path("data/merged.html")
|
||||
SYNC_LOG_PATH = Path("data/sync-log.json")
|
||||
GVIZ_PREFIX = "google.visualization.Query.setResponse("
|
||||
|
||||
|
||||
@@ -171,18 +174,28 @@ def run_git(args: list[str], env: dict[str, str]) -> subprocess.CompletedProcess
|
||||
)
|
||||
|
||||
|
||||
def publish(content: bytes) -> None:
|
||||
target = ROOT / PUBLISH_PATH
|
||||
if target.exists() and target.read_bytes() == content:
|
||||
print("data/merged.html is unchanged")
|
||||
def publish(content: bytes, sync_log: dict[str, object]) -> None:
|
||||
log_content = (json.dumps(sync_log, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
|
||||
targets = {
|
||||
PUBLISH_PATH: content,
|
||||
SYNC_LOG_PATH: log_content,
|
||||
}
|
||||
changed = False
|
||||
for relative_path, target_content in targets.items():
|
||||
target = ROOT / relative_path
|
||||
if not target.exists() or target.read_bytes() != target_content:
|
||||
changed = True
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(target_content)
|
||||
if not changed:
|
||||
print("data/merged.html and data/sync-log.json are unchanged")
|
||||
return
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(content)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["GIT_TERMINAL_PROMPT"] = "0"
|
||||
askpass_path: str | None = None
|
||||
credential = env.get("GIT_CREDENTIAL_SECRET", "")
|
||||
paths = [str(PUBLISH_PATH), str(SYNC_LOG_PATH)]
|
||||
if credential:
|
||||
with tempfile.NamedTemporaryFile("w", prefix="ttshop-askpass-", delete=False) as file:
|
||||
file.write(
|
||||
@@ -197,22 +210,23 @@ def publish(content: bytes) -> None:
|
||||
env["GIT_ASKPASS"] = askpass_path
|
||||
|
||||
try:
|
||||
run_git(["add", "--", str(PUBLISH_PATH)], env)
|
||||
run_git(["add", "--", *paths], env)
|
||||
staged = subprocess.run(
|
||||
["git", "diff", "--cached", "--quiet", "--", str(PUBLISH_PATH)],
|
||||
["git", "diff", "--cached", "--quiet", "--", *paths],
|
||||
cwd=ROOT, env=env, check=False,
|
||||
)
|
||||
if staged.returncode not in (0, 1):
|
||||
raise RuntimeError("could not inspect staged output")
|
||||
if staged.returncode == 0:
|
||||
print("data/merged.html is unchanged")
|
||||
print("data/merged.html and data/sync-log.json are unchanged")
|
||||
return
|
||||
run_git([
|
||||
"-c", "user.name=TTSHOP Sync", "-c", "user.email=ttshop-sync@localhost",
|
||||
"commit", "--no-gpg-sign", "-m", "Update merged Google Sheet",
|
||||
"commit", "--only", "--no-gpg-sign", "-m", "Update merged Google Sheet and sync log",
|
||||
"--", *paths,
|
||||
], env)
|
||||
run_git(["push", "origin", "HEAD:main"], env)
|
||||
print("published data/merged.html")
|
||||
print("published data/merged.html and data/sync-log.json")
|
||||
finally:
|
||||
if askpass_path:
|
||||
Path(askpass_path).unlink(missing_ok=True)
|
||||
@@ -222,13 +236,23 @@ def sync() -> None:
|
||||
export_url = f"https://docs.google.com/spreadsheets/d/{WORKBOOK_ID}/export?format=xlsx"
|
||||
response = requests.get(export_url, timeout=30)
|
||||
response.raise_for_status()
|
||||
worksheet_names = sheet_names(response.content)
|
||||
sheets = []
|
||||
for name in sheet_names(response.content):
|
||||
for name in worksheet_names:
|
||||
headers, rows = normalize_sheet(name, read_gviz(name))
|
||||
if headers:
|
||||
sheets.append((name, headers, rows))
|
||||
headers, rows = merge_sheets(sheets)
|
||||
publish(render_html(headers, rows))
|
||||
content = render_html(headers, rows)
|
||||
success_at = datetime.now(timezone.utc)
|
||||
sync_log = {
|
||||
"last_success_at": success_at.isoformat().replace("+00:00", "Z"),
|
||||
"last_success_at_shanghai": success_at.astimezone(ZoneInfo("Asia/Shanghai")).isoformat(),
|
||||
"worksheet_count": len(worksheet_names),
|
||||
"record_count": len(rows),
|
||||
"artifact_bytes": len(content),
|
||||
}
|
||||
publish(content, sync_log)
|
||||
|
||||
|
||||
def notify_failure(error: Exception) -> None:
|
||||
|
||||
Reference in New Issue
Block a user