"""Classify feature requests into themes with fixed keyword rules (title + body, case-insensitive). An issue can belong to several themes. For each theme: number of issues, total reactions, total comments, and the share of issues. Writes .tsv listing each theme's issues into $CLAIMSET_RUN_DIR. Usage: themes.py """ import json import os import re import sys THEMES = { "arranging_instrumentation": r"\barrang|\borchestrat|\breduction\b|\bexplode\b|\bimplode\b|\binstrument change|" r"\bchange (?:the )?instrument|\bswap instrument|\breplace (?:an |the )?instrument|" r"\breassign|\bensemble\b|\binstrumentation\b|\bplayable range|\bout of range|" r"\bunplayable\b|\brange of (?:the )?instrument|\bdoubling", "parts_and_linked_staves": r"\bparts?\b(?! of)|\blinked staves|\blinked staff|\bexcerpt", "transposition": r"\btranspos", "generation_assistance": r"\bgenerat|\bai\b|\bartificial intelligence|\bharmoni[sz]|\baccompaniment|\bsuggest(?:ion)? (?:chords|notes|harmony)|\bauto-?(?:arrang|harmon|accompan)", "notation_engraving": r"\bslur|\btie\b|\bties\b|\baccidental|\bstem|\bbeam|\bnotehead|\btuplet|\bclef|\btime signature|" r"\bkey signature|\bledger|\blyric|\bchord symbol|\btremolo|\barticulation|\bdynamic|\bengrav|\bspanner|\brest\b|\brests\b", "playback_sound": r"\bplayback|\bmuse ?sounds|\bmixer|\baudio|\bvst|\bsoundfont|\bvelocity|\bmidi (?:device|input|output)", "platform_install": r"\blinux|\bflatpak|\bflathub|\bwindows|\bmacos|\bmac os|\barm\b|\binstaller|\.deb\b|\bmsi\b|\bmuse ?hub", "import_export": r"\bimport|\bexport|\bmusicxml|\bpdf\b|\bmidi file|\bencore\b|\bsibelius\b|\bfinale\b|\bdorico\b", "input_workflow_ui": r"\bshortcut|\bpalette|\bmenu\b|\bnote input|\bkeyboard|\bcopy\b|\bpaste\b|\bselect", } RX = {k: re.compile(v, re.I) for k, v in THEMES.items()} issues = json.load(open(sys.argv[1])) setname = sys.argv[2] out = os.environ.get("CLAIMSET_RUN_DIR", ".") n = len(issues) results = [] for theme, rx in RX.items(): hits = [i for i in issues if rx.search(i["title"] + "\n" + i["body"])] hits.sort(key=lambda i: (-i["reactions_total"], i["number"])) with open(os.path.join(out, f"{theme}.tsv"), "w") as f: for i in hits: f.write(f"{i['number']}\t{i['reactions_total']}\t{i['comments']}\t{i['title']}\n") cond = {"theme": theme, "set": setname} results += [ {"quantity": "issues in theme", "value": len(hits), "conditions": cond}, {"quantity": "share of issues in theme", "value": round(len(hits) / n, 4), "conditions": cond}, {"quantity": "total reactions on issues in theme", "value": sum(i["reactions_total"] for i in hits), "conditions": cond}, {"quantity": "total comments on issues in theme", "value": sum(i["comments"] for i in hits), "conditions": cond}, ] path = os.environ.get("CLAIMSET_RESULTS") if path: with open(path, "w") as f: json.dump(results, f) for r in results: if r["quantity"] in ("issues in theme", "total reactions on issues in theme"): print(r["conditions"]["theme"], r["quantity"], r["value"])