Spaces:
Running
Running
File size: 7,494 Bytes
cc857c8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | import json, re, shlex, subprocess
from collections import Counter
pg = lambda f: {c['number']: c for c in json.loads(re.search(r'const DATA = (.*);\nconst KEY', open(f).read(), re.S).group(1).replace('<\\/', '</'))}
tri, cl, iv, op, mo = pg('triage.html'), pg('cleanup.html'), pg('issues_v4.html'), pg('open_prs.html'), pg('morning.html')
L = {} # (kind, n) -> dict
def rec(kind, n, **kw):
n = int(n); r = L.setdefault((kind, n), dict(kind=kind, n=n, actions=[], closed=False, cat=None, msg=None, title=''))
a = kw.pop('action', None)
if a and a not in r['actions']: r['actions'].append(a)
for k, v in kw.items():
if v: r[k] = v
return r
CAT = {'outdated': 'outdated (v4-era, inactive)', 'old_branch': 'old branch / code moved', 'philosophy': 'not aligned with v5',
'removed': 'removed in v5', 'stale_close': 'stale', 'already_fixed': 'already fixed', 'duplicate_open': 'duplicate', 'fixed': 'fixed'}
cat = lambda c: CAT.get(c, c)
# run 1
toks = shlex.split(open('cmds.sh').read())
for i, t in enumerate(toks):
if t == 'gh':
k, n = toks[i + 1], toks[i + 3]; m = toks[toks.index('-c', i) + 1]
rec(k, n, action='closed', closed=True, cat='fixed' if k == 'issue' else 'superseded', msg=m, title=(tri.get(int(n)) or {}).get('title', ''))
# run 2 cleanup (711)
toks = shlex.split(open('cleanup_close.sh').read().split('R=huggingface/transformers\n', 1)[1]); cur = None; cmsg = {}
for t in toks:
if t == 'gh': cur = []
else:
cur.append(t)
if len(cur) == 7: cmsg[int(cur[2])] = cur[6]
V4 = next(c['message'] for c in cl.values() if 'based on v4' in c['message'])
for n in set(int(x) for x in re.findall(r'#(\d+) ok', open('cleanup_run.log').read() + open('cleanup_run2.log').read())):
m = V4 if n in {13752, 15773, 18152} else cmsg.get(n, cl[n]['message'])
rec('pr', n, action='closed', closed=True, cat=cat(cl[n]['category']), msg=m, title=cl[n]['title'])
# issue run (first 80)
jobs = {j['n']: j for j in json.load(open('issues_jobs.json'))}
for n in set(int(x) for x in re.findall(r'#(\d+) \w+ ok', open('issues_run.log').read())):
rec('issue', n, action='closed' if jobs[n]['op'] == 'close' else 'commented', closed=jobs[n]['op'] == 'close', cat=cat(iv[n]['category']), msg=jobs[n]['msg'], title=iv[n]['title'])
def okset(log): return set((k, int(n), o) for k, n, o in re.findall(r'(issue|pr)#(\d+) (\w+)(?: \S+)? ok', open(log).read()))
# batch A/B
ok = okset('final_run.log')
for q in json.load(open('queue_A.json')) + json.load(open('queue_B.json')):
if (q['kind'], q['n'], q['op']) not in ok: continue
src = iv if q['kind'] == 'issue' else op; c = src.get(q['n'], {})
if q['op'] == 'comment': rec('issue', q['n'], action='commented', cat={'new_model': 'new-model details', 'pointers': 'pointers (how to do it)'}.get(c.get('category'), c.get('category')), msg=q['msg'], title=c.get('title', ''))
else: rec(q['kind'], q['n'], action='closed', closed=True, cat=cat(c.get('category', 'stale')), msg=q['msg'], title=c.get('title', ''))
# morning
ok = okset('morning_run.log')
for q in json.load(open('queue_M.json')):
if (q['kind'], q['n'], q['op']) not in ok: continue
c = mo.get(q['n'], {}); src = (iv if q['kind'] == 'issue' else op).get(q['n'], {})
if q['op'] == 'edit': rec(q['kind'], q['n'], action='comment corrected', msg=q['msg'])
else: rec(q['kind'], q['n'], action='closed', closed=True, cat=cat(src.get('category')) if src.get('category') in ('fixed', 'superseded', 'obsolete', 'removed') else 'stale', msg=q['msg'], title=c.get('title') or src.get('title', ''))
# nudge-audit run (R) + final queue (F)
NV = {r['number']: r for k in range(3) for r in json.load(open(f'results_N{k}.json'))}
QA = {c['number']: c for c in json.load(open('qa_cards.json'))}
for f, log in (('queue_R.json', 'reviews_run.log'), ('queue_F.json', 'final2_run.log')):
ok = okset(log)
for q in json.load(open(f)):
if (q['kind'], q['n'], q['op']) not in ok: continue
t = (NV.get(q['n']) or QA.get(q['n']) or {}).get('title', '')
if q['op'] == 'addrev': rec(q['kind'], q['n'], action=f"review requested: @{q['reviewer']}", title=t)
elif q['op'] == 'comment': rec(q['kind'], q['n'], action='maintainer pinged' if q['msg'].startswith('@') else 'commented', msg=q['msg'], title=t)
else:
v = (QA.get(q['n']) or {}).get('category') if f == 'queue_F.json' else (NV.get(q['n']) or {}).get('verdict')
rec(q['kind'], q['n'], action='closed', closed=True, cat=cat(v or 'superseded'), msg=q['msg'], title=t)
# labels
for line in open('labels.txt'):
p = line.split(); lab = ' '.join(x for x in p if not x.isdigit())
for n in p:
if n.isdigit(): rec('issue', n, action=f'labelled: {lab}')
# exporters edits
for p in json.load(open('exporters_plan.json')):
k = 'pr' if p['n'] == 29315 else 'issue'
rec(k, p['n'], action='comment corrected', msg=p['new'])
# open items (fresh snapshot)
open_rows = {('pr' if r['k'] == 'PR' else 'issue', r['n']): r for r in json.load(open('land_rows.json'))}
items = []
for key, r in L.items():
o = open_rows.get(key)
if r['closed'] and o: r['closed'] = False # reopened by someone (e.g. #41827)
if o: r['title'] = r['title'] or o['t']
for key, o in open_rows.items():
r = L.get(key) or dict(kind=key[0], n=key[1], actions=[], closed=False, cat=None, msg=None, title=o['t'])
r.update(area=o['area'], type=o['kind'], status=o['status'], author=o['au'], age=o['age'], idle=o['idle'], models=', '.join(o['models']))
L[key] = r
need = [k for k, r in L.items() if not r['title']]
for i in range(0, len(need), 80):
part = need[i:i + 80]
q = '{repository(owner:"huggingface",name:"transformers"){' + ' '.join(f'n{n}: issueOrPullRequest(number:{n}){{... on Issue{{title}} ... on PullRequest{{title}}}}' for _, n in part) + '}}'
d = json.loads(subprocess.run(['gh', 'api', 'graphql', '-f', 'query=' + q], capture_output=True, text=True).stdout)['data']['repository']
for k in part: L[k]['title'] = (d.get(f'n{k[1]}') or {}).get('title', '')
for (k, n), r in sorted(L.items(), key=lambda x: -x[0][1]):
isopen = (k, n) in open_rows
if r['closed'] and not isopen: group = 'Closed issues' if k == 'issue' else 'Closed PRs'; category = r['cat'] or 'other'
elif isopen: group = 'Open issues' if k == 'issue' else 'Open PRs'; category = (r.get('type') if k == 'issue' else r.get('status')) or 'other'
else: group = 'Commented (now closed by others)'; category = r['cat'] or 'other'
items.append(dict(key=f'{k}#{n}', n=n, kind=('issue' if k == 'issue' else 'PR'), group=group, category=category, area=r.get('area') or '',
type=r.get('type') or '', title=r['title'] or '', action='; '.join(r['actions']) or ('—' if isopen else ''),
comment=(r['msg'] or '')[:900], author=r.get('author') or '', idle_days=r.get('idle') if isopen else None, models=r.get('models') or '',
url=f"https://github.com/huggingface/transformers/{'pull' if k == 'pr' else 'issues'}/{n}"))
json.dump(items, open('triage_items.json', 'w'))
print(len(items), 'items;', round(len(json.dumps(items)) / 1e6, 2), 'MB')
print(Counter(i['group'] for i in items))
for g in ('Closed issues', 'Closed PRs'): print(g, Counter(i['category'] for i in items if i['group'] == g).most_common())
print('open acted-on:', sum(1 for i in items if i['group'].startswith('Open') and i['action'] not in ('—', '')))
|