Anton Shestakov <av6@dwimlabs.net>, Sun, 12 Nov 2017 14:28:14 +0800
level0lify: skip invisible items
diff.py
Permissions: -rwxr-xr-x
from argparse import ArgumentParser from difflib import unified_diff from subprocess import run from tempfile import NamedTemporaryFile from urllib.request import urlopen from xml.etree import ElementTree as ET API = 'https://api.openstreetmap.org/api/0.6' def fetch_xml(url, what, n, total, delay=200, spins=r'/-\|'): stderr.write('\r{} {} {} out of {}'.format(spins[n % len(spins)], what, n, total)) with urlopen(url) as response: tree = ET.parse(response) def parse_item_xml(kind, el): tag.get('k'): tag.get('v') for tag in el.findall('tag') (ref.tag, ref.get('ref'), None) for ref in el.findall('*[@ref]') (member.get('type'), member.get('ref'), member.get('role')) for member in el.findall('member') items = {'node': {}, 'way': {}, 'relation': {}} # changeset metadata mostly xml = fetch_xml('{}/changeset/{}'.format(API, csid), 'changeset data', 1, 2, delay=0) 'attrs': xml.find('changeset').attrib, tag.get('k'): tag.get('v') for tag in xml.find('changeset').findall('tag') # current versions of nodes, ways, relations xml = fetch_xml('{}/changeset/{}/download'.format(API, csid), 'changeset data', 2, 2) if node.tag not in ('create', 'modify', 'delete'): if el.tag not in ('node', 'way', 'relation'): version = int(el.get('version')) version: parse_item_xml(el.tag, el) # fetch previous versions for kind in ('node', 'way', 'relation'): for n, id in enumerate(items[kind], 1): version = min(items[kind][id]) xml = fetch_xml('{}/{}/{}/{}'.format(API, kind, id, version), kind + 's', n, total) items[kind][id][version] = parse_item_xml(kind, xml.find(kind)) def level0lify(changeset, items, refmap={'node': 'nd', 'way': 'wy'}): a.append('# previous changeset is most likely unrelated') b.append('changeset {}'.format(changeset['attrs']['id'])) b += [' {} = {}'.format(k, v) for k, v in sorted(changeset['tags'].items())] for kind in ('relation', 'way', 'node'): for id in sorted(items[kind]): versions = items[kind][id] for lines, version in todo: if versions[version]['attrs']['visible'] != 'true': lines.append('{} {}'.format(kind, id)) for k, v in sorted(versions[version]['tags'].items()) ' {} {}{}'.format(refmap.get(kind, kind), id, ' ' + role if role is not None else '') for kind, id, role in versions[version].get('refs', []) parser = ArgumentParser() parser.add_argument('changeset', type=int, help='changeset id') group = parser.add_mutually_exclusive_group() group.add_argument('-U', '--unified', type=int, help='lines of context') group.add_argument('-t', '--tool', help='external tool to use for diffing') args = parser.parse_args() changeset, items = bootstrap(args.changeset) a, b = level0lify(changeset, items) context = args.unified if args.unified is not None else 3 diff = unified_diff(a, b, n=context, lineterm='') with NamedTemporaryFile() as afile, NamedTemporaryFile() as bfile: for fd, lines in ((afile, a), (bfile, b)): fd.write('\n'.join(lines).encode('utf-8')) run([args.tool, afile.name, bfile.name]) if __name__ == '__main__':