Files
HiveGrid-Wireframes/hivegrid-cloud/.planning/plane-sync/set_startdates.py
T

55 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""set_startdates.py — Nachzug: start_date für die 21 neuen Screen-Tickets (gen_dayplan ordnet nach start_date).
Idempotent. Default DRY-RUN, scharf: --apply"""
import json, os, re, sys, time, urllib.request, urllib.error
APPLY = "--apply" in sys.argv
TOK = {}
for line in open(os.path.expanduser("~/.cli-tokens/plane")):
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1); TOK[k.strip()] = v.strip()
BASE = TOK["PLANE_BASE_URL"].rstrip("/"); WS = TOK["PLANE_WORKSPACE"]; KEY = TOK["PLANE_API_KEY"]
PID = "35d0d968-8ab3-40cc-97b8-b81d917dc5df"
API = f"{BASE}/api/v1/workspaces/{WS}/projects/{PID}"
STARTS = {
"D-P2":"2026-06-20","D-P4":"2026-06-20","D-P3":"2026-06-20","D-P5":"2026-06-20","D-P7":"2026-06-25",
"D-P8":"2026-06-27","D-P14":"2026-06-27","D-P9":"2026-06-30","D-P15":"2026-06-30",
"D-P16":"2026-07-04","D-P12":"2026-07-06","D-P20":"2026-07-08","D-P13":"2026-07-09","D-P21":"2026-07-09",
"VD-P22":"2026-07-13","VD-P23":"2026-07-13","VD-P24":"2026-07-13","VD-P25":"2026-07-16",
"VD-P32":"2026-07-20","VD-P28":"2026-07-27","VD-P30":"2026-07-29","VD-P31":"2026-07-29",
}
def http(method, path, body=None):
data = json.dumps(body).encode() if body is not None else None
r = urllib.request.Request(API + path, data=data, method=method); r.add_header("X-API-Key", KEY)
if data: r.add_header("Content-Type", "application/json")
for _ in range(4):
try:
with urllib.request.urlopen(r, timeout=30) as resp:
raw = resp.read().decode(); return resp.status, (json.loads(raw) if raw else {})
except urllib.error.HTTPError as e:
if e.code == 429: time.sleep(5); continue
return e.code, {}
return 999, {}
def get_all(path):
out, c = [], None
while True:
st, d = http("GET", path + (("?per_page=100&cursor=" + c) if c else "?per_page=100"))
if st >= 400 or isinstance(d, list): out += d if isinstance(d, list) else []; break
out += d.get("results", [])
if d.get("next_page_results") and d.get("next_cursor"): c = d["next_cursor"]
else: break
return out
wid2 = {re.split(r'\s*·\s*', i["name"])[0].strip(): i for i in get_all("/issues/")}
print(f"=== start_date {'(SCHARF)' if APPLY else '(DRY-RUN)'} ===")
done = 0
for wid, sd in STARTS.items():
i = wid2.get(wid)
if not i: print(f" ⚠ {wid} fehlt"); continue
if (i.get("start_date") or "")[:10] == sd: continue
print(f" ~ {wid}: start_date = {sd}")
if APPLY:
st, _ = http("PATCH", f"/issues/{i['id']}/", {"start_date": sd}); time.sleep(1.1)
if st < 300: done += 1
else: print(f" ✗ HTTP {st}")
print(f"{'gesetzt: '+str(done) if APPLY else '→ scharf: --apply'}")