Files
BT411/tools/tracker_snapshot.py
T

100 lines
3.7 KiB
Python

"""Snapshot the playtest tracker sheet into the repo so tester edits are a git diff.
python tools/tracker_snapshot.py # write docs/tracker/{OPEN,CLOSED}.csv
python tools/tracker_snapshot.py --diff # show what changed vs the committed snapshot
Rows are written sorted by TICKET NUMBER, not sheet order, so re-prioritising the sheet
produces no diff noise -- only real content changes show up.
CREDENTIALS ARE NEVER STORED HERE. The bridge URL + token together grant write access to a
live shared document, so they come from the environment:
set BT_TRACKER_URL=https://script.google.com/macros/s/..../exec
set BT_TRACKER_TOKEN=....
or from an untracked file `scratchpad/tracker_bridge.local` holding those two lines as
KEY=VALUE. See context/playtest-tracker.md.
"""
import csv
import io
import json
import os
import subprocess
import sys
import urllib.request
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUTDIR = os.path.join(REPO, "docs", "tracker")
LOCAL = os.path.join(REPO, "scratchpad", "tracker_bridge.local")
TABS = {
"OPEN": ["#", "Priority", "Test Status", "Dev Status", "Dev Comments", "Tester Comments"],
"CLOSED": ["#", "Test Status", "Dev Status", "Dev Comments", "Tester Comments"],
}
def creds():
url = os.environ.get("BT_TRACKER_URL")
tok = os.environ.get("BT_TRACKER_TOKEN")
if (not url or not tok) and os.path.exists(LOCAL):
for line in io.open(LOCAL, encoding="utf-8"):
if "=" in line:
k, v = line.split("=", 1)
k, v = k.strip(), v.strip()
if k == "BT_TRACKER_URL" and not url:
url = v
if k == "BT_TRACKER_TOKEN" and not tok:
tok = v
if not url or not tok:
sys.exit("no bridge credentials -- set BT_TRACKER_URL / BT_TRACKER_TOKEN, or create "
"scratchpad/tracker_bridge.local (untracked). See context/playtest-tracker.md.")
return url, tok
def fetch(url, tok, tab, ncols):
body = json.dumps({"token": tok, "action": "get", "sheet": tab,
"range": "A1:%s300" % chr(ord('A') + ncols - 1)}).encode()
req = urllib.request.Request(url, data=body,
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as r:
out = json.load(r)
if not out.get("ok"):
sys.exit("bridge error: %s" % out)
rows = []
for raw in out["values"][1:]:
row = [str(x).strip() for x in raw] + [""] * ncols
if row[0]:
rows.append(row[:ncols])
rows.sort(key=lambda r: (0, int(r[0])) if r[0].isdigit() else (1, 0))
return rows
def write(tab, header, rows):
if not os.path.isdir(OUTDIR):
os.makedirs(OUTDIR)
path = os.path.join(OUTDIR, tab + ".csv")
with io.open(path, "w", encoding="utf-8", newline="") as f:
w = csv.writer(f, lineterminator="\n")
w.writerow(header)
w.writerows(rows)
return path
def main():
url, tok = creds()
if "--diff" in sys.argv:
for tab, header in TABS.items():
write(tab, header, fetch(url, tok, tab, len(header)))
print(subprocess.run(["git", "diff", "--stat", "--", "docs/tracker"],
cwd=REPO, capture_output=True, text=True).stdout or "(no changes)")
print(subprocess.run(["git", "diff", "--", "docs/tracker"],
cwd=REPO, capture_output=True, text=True).stdout[:8000])
return
for tab, header in TABS.items():
rows = fetch(url, tok, tab, len(header))
print("%-7s %3d rows -> %s" % (tab, len(rows), write(tab, header, rows)))
if __name__ == "__main__":
main()