Working on the reverter
This commit is contained in:
parent
abfab98136
commit
004dc5bbc3
2 changed files with 126 additions and 7 deletions
|
@ -1,8 +1,9 @@
|
|||
from www import app
|
||||
from flask import render_template, session, url_for, redirect
|
||||
from flask import session, url_for, redirect, request
|
||||
from flask_oauthlib.client import OAuth, get_etree
|
||||
import config
|
||||
from db import database, Change
|
||||
from db import Change
|
||||
from peewee import fn
|
||||
import json
|
||||
|
||||
API_ENDPOINT = 'https://api.openstreetmap.org/api/0.6/'
|
||||
|
||||
|
@ -12,8 +13,8 @@ openstreetmap = oauth.remote_app('OpenStreetMap',
|
|||
request_token_url='https://www.openstreetmap.org/oauth/request_token',
|
||||
access_token_url='https://www.openstreetmap.org/oauth/access_token',
|
||||
authorize_url='https://www.openstreetmap.org/oauth/authorize',
|
||||
consumer_key=config.OAUTH_KEY,
|
||||
consumer_secret=config.OAUTH_SECRET
|
||||
consumer_key=app.config['OAUTH_KEY'],
|
||||
consumer_secret=app.config['OAUTH_SECRET']
|
||||
)
|
||||
|
||||
|
||||
|
@ -21,7 +22,20 @@ openstreetmap = oauth.remote_app('OpenStreetMap',
|
|||
def revert():
|
||||
if 'osm_token' not in session:
|
||||
return openstreetmap.authorize(callback=url_for('oauth'))
|
||||
return 'TODO'
|
||||
|
||||
objects = request.args.get('objects').split(',')
|
||||
q = Change.select(fn.Distinct(Change.user)).where(Change.id << objects).tuples()
|
||||
names = [ch[0].encode('utf-8') for ch in q]
|
||||
|
||||
return '''
|
||||
You are to revert {count} edit{s} by {names}.<br>
|
||||
<a href="javascript:window.close();">Close this window</a> if you pressed the button by mistake.<br><br>
|
||||
<form action="{action}" method="get">
|
||||
<input type="hidden" name="objects" value="{objects}">
|
||||
<input type="submit" value="Continue with the revert">
|
||||
</form>'''.format(
|
||||
count=len(objects), objects=','.join(objects), s=('' if len(objects) == 1 else 's'),
|
||||
names=', '.join(names), action=url_for('actual_revert'))
|
||||
|
||||
|
||||
@app.route('/oauth')
|
||||
|
@ -48,3 +62,105 @@ def logout():
|
|||
if 'osm_token' in session:
|
||||
del session['osm_token']
|
||||
return redirect(url_for('the_one_and_only_page'))
|
||||
|
||||
|
||||
@app.route('/dorevert')
|
||||
def actual_revert():
|
||||
if 'osm_token' not in session:
|
||||
return 'Not authenticated'
|
||||
|
||||
objects = [int(x) for x in request.args.get('objects').split(',')]
|
||||
q = Change.select().where(Change.id << objects)
|
||||
changes = [ch for ch in q]
|
||||
|
||||
# Build a list of objects and request latest versions
|
||||
nwr_list = {'n': [], 'w': [], 'r': []}
|
||||
ch_list = {}
|
||||
notes = []
|
||||
for ch in changes:
|
||||
if ch.action in ('c', 'm') and ch.obj_type in ('n', 'w', 'r'):
|
||||
nwr_list[ch.obj_type].append(ch.obj_id)
|
||||
if ch.action == 'm':
|
||||
ch_list['{0}{1}'.format(ch.obj_type, ch.obj_id)] = json.loads(ch.changes)
|
||||
elif ch.action == 'n':
|
||||
notes.append(ch.changeset)
|
||||
|
||||
# Make three requests for all objects from lists
|
||||
# For each object, revert unchanged tags and coords, prepare osc
|
||||
etree = get_etree()
|
||||
osc = etree.Element('osmChange', {'version': '0.6'})
|
||||
for typ in ('node', 'way', 'relation'):
|
||||
if len(nwr_list[typ[0]]) == 0:
|
||||
continue
|
||||
resp = openstreetmap.get('{0}s?{0}s={1}'.format(typ, ','.join((str(x) for x in nwr_list[typ[0]]))))
|
||||
if resp.status != 200:
|
||||
return 'Failed to get {0}s: {1} {2}'.format(typ, resp.status, resp.data)
|
||||
for obj in resp.data:
|
||||
if obj.get('visible') == 'false':
|
||||
# Not undeleting objects
|
||||
continue
|
||||
v = int(obj.get('version'))
|
||||
ref = '{0}{1}'.format(typ[0], obj.get('id'))
|
||||
if v == 1 and ref not in ch_list:
|
||||
# First version that was created, deleting it
|
||||
d = etree.SubElement(osc, 'delete')
|
||||
etree.SubElement(d, obj.tag, {
|
||||
'id': obj.get('id'),
|
||||
'version': obj.get('version')
|
||||
})
|
||||
elif v > 1 and ref in ch_list:
|
||||
# Reverting tag and coord changes
|
||||
m = etree.SubElement(osc, 'modify')
|
||||
rev = revert_change(obj, ch_list[ref])
|
||||
if rev is not None:
|
||||
m.append(rev)
|
||||
|
||||
if len(osc) == 0:
|
||||
return 'These changes have already been reverted.'
|
||||
|
||||
# Create a changeset
|
||||
q = Change.select(fn.Distinct(Change.user)).where(Change.id << objects).tuples()
|
||||
names = [ch[0].encode('utf-8') for ch in q]
|
||||
comment = 'Reverting MAPS.ME changes by {0}'.format(', '.join(names))
|
||||
|
||||
create_xml = etree.Element('osm')
|
||||
ch = etree.SubElement(create_xml, 'changeset')
|
||||
etree.SubElement(ch, 'tag', {'k': 'created_by', 'v': 'MMWatch Reverter'})
|
||||
etree.SubElement(ch, 'tag', {'k': 'comment', 'v': comment.decode('utf-8')})
|
||||
changeset_xml = etree.tostring(create_xml)
|
||||
resp = openstreetmap.put('changeset/create', changeset_xml, format=None)
|
||||
if resp.status != 200:
|
||||
return 'Failed to open a changeset: {0} {1}'.format(resp.status, resp.data)
|
||||
changeset_id = int(resp.raw_data)
|
||||
|
||||
# Upload changes
|
||||
fill_changeset(osc, changeset_id)
|
||||
print etree.tostring(osc)
|
||||
try:
|
||||
resp = openstreetmap.post('/changeset/{0}/upload'.format(changeset_id), data=etree.tostring(osc), format=None)
|
||||
if resp.status != 200:
|
||||
return 'Failed to upload changes: {0} {1}'.format(resp.status, resp.data)
|
||||
finally:
|
||||
# Close the changeset
|
||||
openstreetmap.put('changeset/{0}/close'.format(changeset_id))
|
||||
|
||||
return redirect('https://www.openstreetmap.org/changeset/{0}'.format(changeset_id))
|
||||
|
||||
|
||||
def revert_change(obj, change):
|
||||
"""Receives XML node of an element and a list of changes.
|
||||
Returns either an XML for OSC, or None."""
|
||||
etree = get_etree()
|
||||
elem = etree.Element(obj.tag)
|
||||
for k in ('id', 'version', 'lat', 'lon'):
|
||||
if k in obj.keys():
|
||||
elem.set(k, obj.get(k))
|
||||
# TODO
|
||||
print 'Reverting', obj.tag, obj.get('id')
|
||||
return elem
|
||||
|
||||
|
||||
def fill_changeset(osc, changeset_id):
|
||||
for act in osc:
|
||||
for elem in act:
|
||||
elem.set('changeset', changeset_id)
|
||||
|
|
|
@ -16,6 +16,7 @@ function btnClear() {
|
|||
|
||||
function btnLevel0() {
|
||||
var checks = getCheckedObjects(0);
|
||||
if (!checks.length) return;
|
||||
var param = checks.join(',');
|
||||
var w = window.open('http://level0.osmz.ru/?url=' + param, '_blank');
|
||||
w.focus();
|
||||
|
@ -23,6 +24,8 @@ function btnLevel0() {
|
|||
|
||||
function btnRevert(url) {
|
||||
var checks = getCheckedObjects(1);
|
||||
if (!checks.length) return;
|
||||
var param = checks.join(',');
|
||||
window.location.assign(url + '?objects=' + param);
|
||||
var w = window.open(url + '?objects=' + param, '_blank');
|
||||
w.focus();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue