"""Download every issue labelled "feature request" in musescore/MuseScore and split it by creation date. Writes data/explore.json (created before 2025-01-01) and data/holdout.json (created 2025-01-01 or later). Each issue keeps: number, title, body (first 2000 characters), created_at, state, total reactions, +1 reactions, comment count and labels. Pull requests are dropped. """ import json import os import subprocess import sys REPO = "musescore/MuseScore" LABEL = "feature request" CUTOFF = "2025-01-01T00:00:00Z" OUT = os.environ.get("CLAIMSET_RUN_DIR") or (sys.argv[1] if len(sys.argv) > 1 else "data") def search(created): """All issues matching one created-date range, via the search API (max 1000 per query).""" import time q = f'repo:{REPO} is:issue label:"{LABEL}" created:{created}' items, page = [], 1 while True: cmd = ["gh", "api", "-X", "GET", "search/issues", "-f", f"q={q}", "-f", "per_page=100", "-f", f"page={page}"] res = json.loads(subprocess.run(cmd, check=True, capture_output=True, text=True).stdout) if res.get("incomplete_results"): raise RuntimeError(f"incomplete search results for {created}") items += res["items"] if res["total_count"] > 1000: raise RuntimeError(f"range {created} has {res['total_count']} issues; split it further") if len(items) >= res["total_count"] or not res["items"]: return items page += 1 time.sleep(2.5) # search API: 30 requests per minute def fetch(): ranges = ["<2019-01-01"] + [f"{y}-01-01..{y}-12-31" for y in range(2019, 2027)] seen, out = set(), [] for r in ranges: for i in search(r): if i["number"] not in seen: seen.add(i["number"]) out.append(i) return out def slim(i): r = i.get("reactions") or {} return {"number": i["number"], "title": i["title"], "body": (i.get("body") or "")[:2000], "created_at": i["created_at"], "state": i["state"], "reactions_total": r.get("total_count", 0), "reactions_plus1": r.get("+1", 0), "comments": i.get("comments", 0), "labels": [l["name"] for l in i.get("labels", [])]} def main(): issues = [slim(i) for i in fetch() if "pull_request" not in i] issues.sort(key=lambda i: i["number"]) explore = [i for i in issues if i["created_at"] < CUTOFF] holdout = [i for i in issues if i["created_at"] >= CUTOFF] os.makedirs(OUT, exist_ok=True) for name, part in (("explore", explore), ("holdout", holdout)): with open(os.path.join(OUT, f"{name}.json"), "w") as f: json.dump(part, f, indent=0, ensure_ascii=False) results = [ {"quantity": "feature-request issues downloaded", "value": len(issues)}, {"quantity": "feature-request issues created before 2025-01-01 (exploration set)", "value": len(explore)}, {"quantity": "feature-request issues created on or after 2025-01-01 (held-out set)", "value": len(holdout)}, ] path = os.environ.get("CLAIMSET_RESULTS") if path: with open(path, "w") as f: json.dump(results, f) print(json.dumps(results)) if __name__ == "__main__": main()