Implement uploading to OSM database
This commit is contained in:
parent
589751de2b
commit
22c44aaced
128
web_view.py
128
web_view.py
|
@ -1,14 +1,17 @@
|
|||
#!/usr/bin/python3
|
||||
#!/usr/bin/python3.9
|
||||
|
||||
from flask import (Flask, render_template, request, jsonify, redirect, url_for, g,
|
||||
flash, session, Response)
|
||||
flash, session, Response, stream_with_context)
|
||||
from sqlalchemy import func
|
||||
from matcher import (nominatim, model, database, commons, wikidata, wikidata_api,
|
||||
osm_oauth, edit, mail, api)
|
||||
from matcher.data import property_map
|
||||
from time import time, sleep
|
||||
from requests_oauthlib import OAuth1Session
|
||||
from lxml import etree
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
import flask_login
|
||||
import requests
|
||||
import json
|
||||
import GeoIP
|
||||
import re
|
||||
|
@ -509,7 +512,7 @@ def validate_edit_list(edits):
|
|||
osm_type, _, osm_id = e['osm'].partition('/')
|
||||
osm_id = int(osm_id)
|
||||
if osm_type == 'node':
|
||||
assert model.Point.get(osm_id)
|
||||
assert model.Point.query.get(osm_id)
|
||||
else:
|
||||
src_id = osm_id if osm_type == "way" else -osm_id
|
||||
assert (model.Line.query.get(src_id)
|
||||
|
@ -546,45 +549,138 @@ def api_edit_session(session_id):
|
|||
|
||||
return cors_jsonify(success=True, session_id=session_id)
|
||||
|
||||
@app.route("/api/1/real_save/<int:session_id>")
|
||||
class VersionMismatch(Exception):
|
||||
pass
|
||||
|
||||
def osm_object(osm_type, osm_id):
|
||||
if osm_type == "node":
|
||||
return model.Point.query.get(osm_id)
|
||||
|
||||
src_id = osm_id * {'way': 1, 'relation': -1}[osm_type]
|
||||
for cls in model.Line, model.Polygon:
|
||||
obj = cls.query.get(src_id)
|
||||
if obj:
|
||||
return obj
|
||||
|
||||
|
||||
def process_match(changeset_id, e):
|
||||
osm_type, _, osm_id = e['osm'].partition('/')
|
||||
qid = e["qid"]
|
||||
item_id = qid[1:]
|
||||
|
||||
osm = osm_object(osm_type, osm_id)
|
||||
assert osm
|
||||
|
||||
r = edit.get_existing(osm_type, osm_id)
|
||||
if r.status_code == 410 or r.content == b"":
|
||||
return "deleted"
|
||||
|
||||
root = etree.fromstring(r.content)
|
||||
existing = root.find('.//tag[@k="wikidata"]')
|
||||
if e["op"] == "add" and existing is not None:
|
||||
return "already_added"
|
||||
if e["op"] == "remove" and existing is None:
|
||||
return "already_removed"
|
||||
|
||||
root = etree.fromstring(r.content)
|
||||
root[0].set("changeset", str(changeset_id))
|
||||
if e["op"] == "add":
|
||||
tag = etree.Element("tag", k="wikidata", v=qid)
|
||||
root[0].append(tag)
|
||||
if e["op"] == "remove":
|
||||
root[0].remove(existing)
|
||||
|
||||
element_data = etree.tostring(root)
|
||||
try:
|
||||
success = edit.save_element(osm_type, osm_id, element_data)
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 409 and "Version mismatch" in r.text:
|
||||
raise VersionMismatch
|
||||
mail.error_mail(
|
||||
"error saving element", element_data.decode("utf-8"), e.response
|
||||
)
|
||||
database.session.commit()
|
||||
return "element-error"
|
||||
|
||||
if not success:
|
||||
return "element-error"
|
||||
|
||||
osm.tags["wikidata"] = qid
|
||||
flag_modified(osm, "tags")
|
||||
|
||||
db_edit = model.ChangesetEdit(
|
||||
changeset_id=changeset_id,
|
||||
item_id=item_id,
|
||||
osm_id=osm_id,
|
||||
osm_type=osm_type,
|
||||
)
|
||||
database.session.add(db_edit)
|
||||
database.session.commit()
|
||||
|
||||
return "saved"
|
||||
|
||||
@app.route("/api/1/save/<int:session_id>")
|
||||
def api_save_changeset(session_id):
|
||||
assert g.user.is_authenticated
|
||||
|
||||
mock = g.user.mock_upload
|
||||
api_call = api_mock_save_changeset if mock else api_real_save_changeset
|
||||
return api_call(session_id)
|
||||
|
||||
|
||||
def api_real_save_changeset(session_id):
|
||||
es = model.EditSession.query.get(session_id)
|
||||
|
||||
def send_message(event, **data):
|
||||
def send(event, **data):
|
||||
data["type"] = event
|
||||
return f"data: {json.dumps(data)}\n\n"
|
||||
|
||||
def stream():
|
||||
def stream(user):
|
||||
changeset = edit.new_changeset(es.comment)
|
||||
r = edit.create_changeset(changeset)
|
||||
reply = r.text.strip()
|
||||
|
||||
if reply == "Couldn't authenticate you":
|
||||
mail.open_changeset_error(session_id, changeset, r)
|
||||
yield send_message("auth-fail", error=reply)
|
||||
yield send("auth-fail", error=reply)
|
||||
return
|
||||
|
||||
if not reply.isdigit():
|
||||
mail.open_changeset_error(session_id, changeset, r)
|
||||
yield send_message("changeset-error", error=reply)
|
||||
yield send("changeset-error", error=reply)
|
||||
return
|
||||
|
||||
changeset_id = int(reply)
|
||||
yield send_message("open", id=changeset_id)
|
||||
yield send("open", id=changeset_id)
|
||||
|
||||
update_count = 0
|
||||
|
||||
edit.record_changeset(
|
||||
id=changeset_id, comment=es.comment, update_count=update_count
|
||||
change = edit.record_changeset(
|
||||
id=changeset_id, user=user, comment=es.comment, update_count=update_count
|
||||
)
|
||||
|
||||
for e in es.edit_list:
|
||||
pass
|
||||
# each edit contains these keys:
|
||||
# qid: Wikidata item QID
|
||||
# osm: OpenStreetMap identifier
|
||||
# op: either 'add' or 'remove'
|
||||
|
||||
return Response(stream(), mimetype='text/event-stream')
|
||||
for num, e in enumerate(es.edit_list):
|
||||
print(num, e)
|
||||
yield send("progress", edit=e, num=num)
|
||||
result = process_match(changeset_id, e)
|
||||
yield send(result, edit=e, num=num)
|
||||
if result == "saved":
|
||||
update_count += 1
|
||||
change.update_count = update_count
|
||||
database.session.commit()
|
||||
|
||||
@app.route("/api/1/save/<int:session_id>")
|
||||
def mock_api_save_changeset(session_id):
|
||||
yield send("closing")
|
||||
edit.close_changeset(changeset_id)
|
||||
yield send("done")
|
||||
|
||||
return Response(stream_with_context(stream(g.user)), mimetype='text/event-stream')
|
||||
|
||||
def api_mock_save_changeset(session_id):
|
||||
es = model.EditSession.query.get(session_id)
|
||||
|
||||
def send(event, **data):
|
||||
|
|
Loading…
Reference in a new issue