|
| 1 | +"""Count how many issues use each bpo field and print a report.""" |
| 2 | +""" sample output: https://github.com/psf/gh-migration/issues/5#issuecomment-935697646""" |
| 3 | + |
| 4 | +import xmlrpc.client |
| 5 | + |
| 6 | +from collections import defaultdict |
| 7 | + |
| 8 | +class SpecialTransport(xmlrpc.client.SafeTransport): |
| 9 | + def send_content(self, connection, request_body): |
| 10 | + connection.putheader("Referer", "https://bugs.python.org/") |
| 11 | + connection.putheader("Origin", "https://bugs.python.org") |
| 12 | + connection.putheader("X-Requested-With", "XMLHttpRequest") |
| 13 | + xmlrpc.client.SafeTransport.send_content(self, connection, request_body) |
| 14 | + |
| 15 | +# connect to bpo |
| 16 | +roundup = xmlrpc.client.ServerProxy('https://bugs.python.org/xmlrpc', |
| 17 | + transport=SpecialTransport(), |
| 18 | + allow_none=True) |
| 19 | + |
| 20 | +# map bpo classes -> propname |
| 21 | +# the class is the name of the class (e.g. issue_type, keyword -- |
| 22 | +# also used in e.g. in https://bugs.python.org/keyword) |
| 23 | +# the propname is the name used as attribute on the issue class |
| 24 | +# (e.g. issue.type, issue.keywords) |
| 25 | +classes = { |
| 26 | + # 'status': 'status', # skip this |
| 27 | + 'issue_type': 'type', |
| 28 | + 'stage': 'stage', |
| 29 | + 'component': 'components', |
| 30 | + 'version': 'versions', |
| 31 | + 'resolution': 'resolution', |
| 32 | + 'priority': 'priority', |
| 33 | + 'keyword': 'keywords', |
| 34 | +} |
| 35 | + |
| 36 | +# find the id for the 'open' status |
| 37 | +open_id = roundup.lookup('status', 'open') |
| 38 | + |
| 39 | +print(f'* Counting total issues...') |
| 40 | +total_issues_num = len(roundup.filter('issue', None, {})) |
| 41 | + |
| 42 | +print(f'* Counting open issues...') |
| 43 | +# use this list to filter only the open issues |
| 44 | +open_issues = roundup.filter('issue', None, {'status': open_id}) |
| 45 | +open_issues_num = len(open_issues) |
| 46 | + |
| 47 | +# save the totals in a dict with this structure: |
| 48 | +# totals[propname][open/all][num/perc][name] |
| 49 | +# where propname is e.g. 'keyword' and name is e.g. 'easy' |
| 50 | +totals = defaultdict(lambda: {'all': {'perc': {}, 'num': {}}, |
| 51 | + 'open': {'perc': {}, 'num': {}}}) |
| 52 | +for cls, propname in classes.items(): |
| 53 | + print(f'* Counting <{cls}>...') |
| 54 | + # get the list of ids/names for the given class (e.g. 'easy' is 6) |
| 55 | + ids = roundup.list(cls, 'id') |
| 56 | + names = roundup.list(cls, 'name') |
| 57 | + for id, name in zip(ids, names): |
| 58 | + # filter and count on *all* issues with the given propname |
| 59 | + tot_all = len(roundup.filter('issue', None, {propname: id})) |
| 60 | + totals[propname]['all']['num'][name] = tot_all |
| 61 | + totals[propname]['all']['perc'][name] = tot_all / total_issues_num |
| 62 | + # filter and count on *open* issues with the given propname |
| 63 | + tot_open = len(roundup.filter('issue', open_issues, {propname: id})) |
| 64 | + totals[propname]['open']['num'][name] = tot_open |
| 65 | + totals[propname]['open']['perc'][name] = tot_open / open_issues_num |
| 66 | + |
| 67 | + |
| 68 | +print(f'Issues (open/all): {open_issues_num}/{total_issues_num}') |
| 69 | + |
| 70 | +# print a list of markdown tables for each bpo class name |
| 71 | +for propname in classes.values(): |
| 72 | + print(f'### {propname}') |
| 73 | + print('| bpo field | open | all |') |
| 74 | + print('| :--- | ---: | ---: |') |
| 75 | + # pick the dict for the given propname (e.g. keywords) |
| 76 | + proptots = totals[propname] |
| 77 | + names = proptots['open']['num'] |
| 78 | + # sort the names (e.g. 'easy') in reverse order |
| 79 | + # based on the number of open issues |
| 80 | + for name in sorted(names, key=names.get, reverse=True): |
| 81 | + # get and print num/perc for all/open issues |
| 82 | + issues_all = proptots['all']['num'][name] |
| 83 | + issues_open = proptots['open']['num'][name] |
| 84 | + perc_all = proptots['all']['perc'][name] |
| 85 | + perc_open = proptots['open']['perc'][name] |
| 86 | + print(f'| {name:20} | {issues_open:>5} ({perc_open:5.1%}) |' |
| 87 | + f' {issues_all:>5} ({perc_all:5.1%}) |') |
| 88 | + # calc and print num/perc for all/open issues |
| 89 | + tot_issues_all = sum(proptots['all']['num'].values()) |
| 90 | + tot_issues_open = sum(proptots['open']['num'].values()) |
| 91 | + tot_perc_all = sum(proptots['all']['perc'].values()) |
| 92 | + tot_perc_open = sum(proptots['open']['perc'].values()) |
| 93 | + print(f'| **Total** | {tot_issues_open:>5} ({tot_perc_open:5.1%}) |' |
| 94 | + f' {tot_issues_all:>5} ({tot_perc_all:5.1%}) |') |
| 95 | + |
0 commit comments