Exemplo n.º 1
0
def download_changesets(changeset_ids, print_status):
    """Downloads changesets and all their contents from API, returns (diffs, changeset_users) tuple."""
    ch_users = {}
    diffs = defaultdict(dict)
    for changeset_id in changeset_ids:
        print_status(changeset_id)
        root = api_download('changeset/{0}/download'.format(changeset_id),
                            sysexit_message='Failed to download changeset {0}'.format(changeset_id))
        # Iterate over each object, download previous version (unless it's creation) and make a diff
        count = total = 0
        for action in root:
            if action.tag != 'create':
                total += len(action)
        for action in root:
            for obj_xml in action:
                if action.tag != 'create':
                    count += 1
                if changeset_id not in ch_users:
                    ch_users[changeset_id] = obj_xml.get('user').encode('utf-8')
                obj = obj_to_dict(obj_xml)
                if obj['version'] > 1:
                    print_status(changeset_id, obj['type'], obj['id'], count, total)
                    try:
                        obj_prev = obj_to_dict(api_download('{0}/{1}/{2}'.format(
                            obj['type'], obj['id'], obj['version'] - 1), throw=[403])[0])
                    except HTTPError:
                        msg = '\nCannot revert redactions, see version {0} at https://openstreetmap.org/{1}/{2}/history'
                        raise RevertError(msg.format(obj['version'] - 1, obj['type'], obj['id']))
                else:
                    obj_prev = None
                diffs[(obj['type'], obj['id'])][obj['version']] = make_diff(obj, obj_prev)
        print_status('flush')
    return diffs, ch_users
Exemplo n.º 2
0
def revert_changes(diffs, print_status):
    """Actually reverts changes in diffs dict. Returns a changes list for uploading to API."""
    # merge versions of same objects in diffs
    for k in diffs:
        diff = None
        for v in sorted(diffs[k].keys()):
            diff = merge_diffs(diff, diffs[k][v])
        diffs[k] = diff

    changes = []
    count = 0
    for kobj, change in diffs.iteritems():
        count += 1
        if change is None:
            continue
        try:
            # Download the latest version of an object
            print_status(None, kobj[0], kobj[1], count, len(diffs))
            obj = obj_to_dict(
                api_download('{0}s?{0}s={1}'.format(kobj[0], kobj[1]))[0])

            # Apply the change
            obj_new = None
            if len(change) == 2 and change[1][0] == 'create':
                if not obj['deleted']:
                    obj_new = {
                        'type': obj['type'],
                        'id': obj['id'],
                        'deleted': True
                    }
            elif len(change) == 2 and change[1][0] == 'delete':
                # Restore only if the object is still absent
                if obj['deleted']:
                    obj_new = change[1][1]
                else:
                    # Controversial, but I've decided to replace the object with the old one in this case
                    obj_new = change[1][1]
            else:
                obj_new = apply_diff(change, deepcopy(obj))

            if obj_new is not None:
                obj_new['version'] = obj['version']
                if obj_new != obj:
                    changes.append(obj_new)
        except Exception as e:
            raise RevertError(
                '\nFailed to download the latest version of {0} {1}: {2}'.
                format(kobj[0], kobj[1], e))
    print_status('flush')
    return changes
Exemplo n.º 3
0
def print_changesets_for_user(user, limit=15):
    """Prints last 15 changesets for a user."""
    try:
        root = api_download('changesets?closed=true&display_name={0}'.format(quote(user)), throw=[404])
        for changeset in root[:limit]:
            created_by = '???'
            comment = '<no comment>'
            for tag in changeset.findall('tag'):
                if tag.get('k') == 'created_by':
                    created_by = tag.get('v').encode('utf-8')
                elif tag.get('k') == 'comment':
                    comment = tag.get('v').encode('utf-8')
            print('Changeset {0} created on {1} with {2}:\t{3}'.format(
                changeset.get('id'), changeset.get('created_at'), created_by, comment))
    except HTTPError:
        print('No such user found.')
Exemplo n.º 4
0
def revert_changes(diffs, print_status):
    """Actually reverts changes in diffs dict. Returns a changes list for uploading to API."""
    # merge versions of same objects in diffs
    for k in diffs:
        diff = None
        for v in sorted(diffs[k].keys()):
            diff = merge_diffs(diff, diffs[k][v])
        diffs[k] = diff

    changes = []
    count = 0
    for kobj, change in diffs.iteritems():
        count += 1
        if change is None:
            continue
        try:
            # Download the latest version of an object
            print_status(None, kobj[0], kobj[1], count, len(diffs))
            obj = obj_to_dict(api_download('{0}s?{0}s={1}'.format(kobj[0], kobj[1]))[0])

            # Apply the change
            obj_new = None
            if len(change) == 2 and change[1][0] == 'create':
                if not obj['deleted']:
                    obj_new = {'type': obj['type'], 'id': obj['id'], 'deleted': True}
            elif len(change) == 2 and change[1][0] == 'delete':
                # Restore only if the object is still absent
                if obj['deleted']:
                    obj_new = change[1][1]
                else:
                    # Controversial, but I've decided to replace the object with the old one in this case
                    obj_new = change[1][1]
            else:
                obj_new = apply_diff(change, deepcopy(obj))

            if obj_new is not None:
                obj_new['version'] = obj['version']
                if obj_new != obj:
                    changes.append(obj_new)
        except Exception as e:
            raise RevertError('\nFailed to download the latest version of {0} {1}: {2}'.format(kobj[0], kobj[1], e))
    print_status('flush')
    return changes
Exemplo n.º 5
0
        print('Omit version number to see an object history.')
        sys.exit(1)

    obj_type, obj_id, obj_version = parse_url(sys.argv[1])
    if obj_type is None or obj_id is None:
        safe_print('Please specify correct object type and id.')
        sys.exit(1)
    if len(sys.argv) > 2:
        obj_version = int(sys.argv[2])

    # Download full object history
    # If we fail, revert to a given version blindly
    history = None
    safe_print('Downloading history of {0} {1}'.format(obj_type, obj_id))
    try:
        history = api_download('{0}/{1}/history'.format(obj_type, obj_id),
                               throw=[408, 500, 503, 504])
    except HTTPError:
        # Failed to read the complete history due to a timeout, read only two versions
        safe_print(
            'History is too large to download. Querying the last version only.'
        )
        history = etree.Element('osm')
        try:
            obj = api_download('{0}/{1}'.format(obj_type, obj_id),
                               throw=[410])[0]
            history.append(obj)
        except HTTPError:
            safe_print(
                'To restore a deleted version, we need to know the last version number, and we failed.'
            )
            sys.exit(2)
Exemplo n.º 6
0
        print('Omit version number to see an object history.')
        sys.exit(1)

    obj_type, obj_id, obj_version = parse_url(sys.argv[1])
    if obj_type is None or obj_id is None:
        safe_print('Please specify correct object type and id.')
        sys.exit(1)
    if len(sys.argv) > 2:
        obj_version = int(sys.argv[2])

    # Download full object history
    # If we fail, revert to a given version blindly
    history = None
    safe_print('Downloading history of {0} {1}'.format(obj_type, obj_id))
    try:
        history = api_download('{0}/{1}/history'.format(obj_type, obj_id), throw=[408, 500, 503, 504])
    except HTTPError:
        # Failed to read the complete history due to a timeout, read only two versions
        safe_print('History is too large to download. Querying the last version only.')
        history = etree.Element('osm')
        try:
            obj = api_download('{0}/{1}'.format(obj_type, obj_id), throw=[410])[0]
            history.append(obj)
        except HTTPError:
            safe_print('To restore a deleted version, we need to know the last version number, and we failed.')
            sys.exit(2)

    if obj_version is None:
        # Print history and exit
        for h in history[-MAX_DEPTH-1:]:
            print('Version {0}: {1}changeset {2} on {3} by {4}'.format(