From 3d6e1c67fb2291c6933f37bac3b9a3f518e8487a Mon Sep 17 00:00:00 2001 From: Gabriel Ebner Date: Sun, 4 May 2008 13:52:43 +0000 Subject: [PATCH] api06: Preliminary support for diff uploading. This will not return anything useful and the code will need some heavy refactoring. --- app/controllers/changeset_controller.rb | 94 ++++++++++++++++++++++++- app/controllers/node_controller.rb | 56 +++++++++------ app/controllers/relation_controller.rb | 35 +++++---- app/controllers/way_controller.rb | 24 +++++-- app/models/node.rb | 53 +++++++------- app/models/relation.rb | 48 +++++++------ app/models/way.rb | 48 +++++++------ config/routes.rb | 1 + 8 files changed, 249 insertions(+), 110 deletions(-) diff --git a/app/controllers/changeset_controller.rb b/app/controllers/changeset_controller.rb index 1041c7469..19d1591a0 100644 --- a/app/controllers/changeset_controller.rb +++ b/app/controllers/changeset_controller.rb @@ -3,9 +3,10 @@ class ChangesetController < ApplicationController require 'xml/libxml' - before_filter :authorize, :only => [:create, :update, :delete] - before_filter :check_write_availability, :only => [:create, :update, :delete] - before_filter :check_read_availability, :except => [:create, :update, :delete] + before_filter :authorize, :only => [:create, :update, :delete, :upload] + before_filter :check_write_availability, :only => [:create, :update, :delete, :upload] + before_filter :check_read_availability, :except => [:create, :update, :delete, :upload] + after_filter :compress_output # Create a changeset from XML. def create @@ -23,4 +24,91 @@ class ChangesetController < ApplicationController render :nothing => true, :status => :method_not_allowed end end + + def create_prim(ids, prim, nd) + prim.version = 0 + prim.user_id = @user.id + prim.visible = true + prim.save_with_history! + + ids[nd['id'].to_i] = prim.id + end + + def fix_way(w, node_ids) + w.nds.each { |nd| + new_id = node_ids[nd.node_id] + nd.node_id = new_id unless new_id.nil? + } + end + + def fix_rel(r, ids) + r.members.each { |memb| + new_id = ids[memb.member_type][memb.member_id] + nd.member_id = new_id unless new_id.nil? + } + end + + def upload + if not request.put? + render :nothing => true, :status => :method_not_allowed + return + end + + # FIXME: this should really be done without loading the whole XML file + # into memory. + p = XML::Parser.new + p.string = request.raw_post + doc = p.parse + + node_ids, way_ids, rel_ids = {}, {}, {} + ids = {"node"=>node_ids, "way"=>way_ids, "relation"=>rel_ids} + + Changeset.transaction do + doc.find('//osm/create/node').each do |nd| + create_prim node_ids, Node.from_xml_node(nd, true), nd + end + doc.find('//osm/create/way').each do |nd| + way = Way.from_xml_node(nd, true) + raise OSM::APIPreconditionFailedError.new if !way.preconditions_ok? + create_prim way_ids, fix_way(way, node_ids), nd + end + doc.find('//osm/create/relation').each do |nd| + relation = Relation.from_xml_node(nd, true) + raise OSM::APIPreconditionFailedError.new if !way.preconditions_ok? + create_prim relation_ids, fix_rel(relation, ids), nd + end + + doc.find('//osm/modify/node').each do |nd| + unless NodeController.update_internal nil, Node.from_xml_node(nd) + raise OSM::APIPreconditionFailedError.new + end + end + doc.find('//osm/modify/way').each do |nd| + unless WayController.update_internal nil, fix_way(Way.from_xml_node(nd), node_ids) + raise OSM::APIPreconditionFailedError.new + end + end + doc.find('//osm/modify/relation').each do |nd| + unless RelationController.update_internal nil, fix_rel(Relation.from_xml_node(nd), ids) + raise OSM::APIPreconditionFailedError.new + end + end + + doc.find('//osm/delete/node').each do |nd| + unless NodeController.delete_internal nil, Node.from_xml_node(n) + raise OSM::APIPreconditionFailedError.new + end + end + doc.find('//osm/delete/way').each do |nd| + Way.from_xml_node(nd).delete_with_relations_and_history(@user) + end + doc.find('//osm/delete/relation').each do |nd| + unless RelationController.delete_internal nil, fix_rel(Relation.from_xml_node(nd), ids) + raise OSM::APIPreconditionFailedError.new + end + end + end + + render :text => "Ok, Fine. Upload worked without errors.\n", :status => 200 + end end diff --git a/app/controllers/node_controller.rb b/app/controllers/node_controller.rb index 9f8f4a38b..ae5245016 100644 --- a/app/controllers/node_controller.rb +++ b/app/controllers/node_controller.rb @@ -51,13 +51,7 @@ class NodeController < ApplicationController new_node = Node.from_xml(request.raw_post) if new_node and new_node.id == node.id - node.user_id = @user.id - node.latitude = new_node.latitude - node.longitude = new_node.longitude - node.tags = new_node.tags - node.visible = true - node.save_with_history! - + update_internal node, new_node render :nothing => true else render :nothing => true, :status => :bad_request @@ -67,32 +61,54 @@ class NodeController < ApplicationController end end + def update_internal(node, new_node) + node = Node.find(new_node.id) if node.nil? + + node.user_id = @user.id + node.latitude = new_node.latitude + node.longitude = new_node.longitude + node.tags = new_node.tags + node.visible = true + node.save_with_history! + + return true + end + # Delete a node. Doesn't actually delete it, but retains its history in a wiki-like way. # FIXME remove all the fricking SQL def delete begin node = Node.find(params[:id]) - if node.visible - if WayNode.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_nodes.id", :conditions => [ "current_ways.visible = 1 AND current_way_nodes.node_id = ?", node.id ]) - render :text => "", :status => :precondition_failed - elsif RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='node' and member_id=?", params[:id]]) - render :text => "", :status => :precondition_failed - else - node.user_id = @user.id - node.visible = 0 - node.save_with_history! - - render :nothing => true - end + res = delete_internal(node) + unless res + render :text => "", :status => :precondition_failed else - render :text => "", :status => :gone + render :text => "", :status => res end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found end end + def delete_internal(node) + if node.visible + if WayNode.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_nodes.id", :conditions => [ "current_ways.visible = 1 AND current_way_nodes.node_id = ?", node.id ]) + return false + elsif RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='node' and member_id=?", params[:id]]) + return false + else + node.user_id = @user.id + node.visible = 0 + node.save_with_history! + + return :ok + end + else + return :gone + end + end + # WTF does this do? def nodes ids = params['nodes'].split(',').collect { |n| n.to_i } diff --git a/app/controllers/relation_controller.rb b/app/controllers/relation_controller.rb index f4e938176..894ab40e8 100644 --- a/app/controllers/relation_controller.rb +++ b/app/controllers/relation_controller.rb @@ -77,20 +77,11 @@ class RelationController < ApplicationController begin relation = Relation.find(params[:id]) - if relation.visible - if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='relation' and member_id=?", params[:id]]) - render :text => "", :status => :precondition_failed - else - relation.user_id = @user.id - relation.tags = [] - relation.members = [] - relation.visible = false - relation.save_with_history! - - render :nothing => true - end + res = delete_internal(node) + unless res + render :text => "", :status => :precondition_failed else - render :text => "", :status => :gone + render :text => "", :status => res end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found @@ -99,6 +90,24 @@ class RelationController < ApplicationController end end + def delete_internal(relation) + if relation.visible + if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='relation' and member_id=?", params[:id]]) + return false + else + relation.user_id = @user.id + relation.tags = [] + relation.members = [] + relation.visible = false + relation.save_with_history! + + return :ok + end + else + return :gone + end + end + # ----------------------------------------------------------------- # full # diff --git a/app/controllers/way_controller.rb b/app/controllers/way_controller.rb index a7f74e50c..fd01008fa 100644 --- a/app/controllers/way_controller.rb +++ b/app/controllers/way_controller.rb @@ -51,15 +51,9 @@ class WayController < ApplicationController new_way = Way.from_xml(request.raw_post) if new_way and new_way.id == way.id - if !new_way.preconditions_ok? + unless update_internal(way, new_way) render :text => "", :status => :precondition_failed else - way.user_id = @user.id - way.tags = new_way.tags - way.nds = new_way.nds - way.visible = true - way.save_with_history! - render :nothing => true end else @@ -70,6 +64,22 @@ class WayController < ApplicationController end end + def update_internal way, new_way + way = Way.find(new_way.id) if way.nil? + + if !new_way.preconditions_ok? + return false + else + way.user_id = @user.id + way.tags = new_way.tags + way.nds = new_way.nds + way.visible = true + way.save_with_history! + + return true + end + end + # This is the API call to delete a way def delete begin diff --git a/app/models/node.rb b/app/models/node.rb index 5e5e7a0b2..872c5c922 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -55,40 +55,43 @@ class Node < GeoRecord p = XML::Parser.new p.string = xml doc = p.parse - - node = Node.new doc.find('//osm/node').each do |pt| - node.lat = pt['lat'].to_f - node.lon = pt['lon'].to_f - - return nil unless node.in_world? + return Node.from_xml_node(pt, create) + end + rescue + return nil + end + end - unless create - if pt['id'] != '0' - node.id = pt['id'].to_i - end - end + def self.from_xml_node(pt, create=false) + node = Node.new - node.visible = pt['visible'] and pt['visible'] == 'true' + node.lat = pt['lat'].to_f + node.lon = pt['lon'].to_f - if create - node.timestamp = Time.now - else - if pt['timestamp'] - node.timestamp = Time.parse(pt['timestamp']) - end - end + return nil unless node.in_world? - tags = [] + unless create + if pt['id'] != '0' + node.id = pt['id'].to_i + end + end - pt.find('tag').each do |tag| - node.add_tag_key_val(tag['k'],tag['v']) - end + node.visible = pt['visible'] and pt['visible'] == 'true' + if create + node.timestamp = Time.now + else + if pt['timestamp'] + node.timestamp = Time.parse(pt['timestamp']) end - rescue - node = nil + end + + tags = [] + + pt.find('tag').each do |tag| + node.add_tag_key_val(tag['k'],tag['v']) end return node diff --git a/app/models/relation.rb b/app/models/relation.rb index ec8c92c92..2c155e9e1 100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@ -16,32 +16,38 @@ class Relation < ActiveRecord::Base p.string = xml doc = p.parse - relation = Relation.new - doc.find('//osm/relation').each do |pt| - if !create and pt['id'] != '0' - relation.id = pt['id'].to_i - end + relation = Relation.from_xml_node pt, create + end + rescue + relation = nil + end - if create - relation.timestamp = Time.now - relation.visible = true - else - if pt['timestamp'] - relation.timestamp = Time.parse(pt['timestamp']) - end - end + return relation + end - pt.find('tag').each do |tag| - relation.add_tag_keyval(tag['k'], tag['v']) - end + def self.from_xml_node(pt, create=false) + relation = Relation.new - pt.find('member').each do |member| - relation.add_member(member['type'], member['ref'], member['role']) - end + if !create and pt['id'] != '0' + relation.id = pt['id'].to_i + end + + if create + relation.timestamp = Time.now + relation.visible = true + else + if pt['timestamp'] + relation.timestamp = Time.parse(pt['timestamp']) end - rescue - relation = nil + end + + pt.find('tag').each do |tag| + relation.add_tag_keyval(tag['k'], tag['v']) + end + + pt.find('member').each do |member| + relation.add_member(member['type'], member['ref'], member['role']) end return relation diff --git a/app/models/way.rb b/app/models/way.rb index 104c550a9..0c927c11c 100644 --- a/app/models/way.rb +++ b/app/models/way.rb @@ -17,32 +17,38 @@ class Way < ActiveRecord::Base p.string = xml doc = p.parse - way = Way.new - doc.find('//osm/way').each do |pt| - if !create and pt['id'] != '0' - way.id = pt['id'].to_i - end + way = Way.from_xml_node pt, create + end + rescue + way = nil + end - if create - way.timestamp = Time.now - way.visible = true - else - if pt['timestamp'] - way.timestamp = Time.parse(pt['timestamp']) - end - end + return way + end - pt.find('tag').each do |tag| - way.add_tag_keyval(tag['k'], tag['v']) - end + def self.from_xml_node(pt, create=false) + way = Way.new - pt.find('nd').each do |nd| - way.add_nd_num(nd['ref']) - end + if !create and pt['id'] != '0' + way.id = pt['id'].to_i + end + + if create + way.timestamp = Time.now + way.visible = true + else + if pt['timestamp'] + way.timestamp = Time.parse(pt['timestamp']) end - rescue - way = nil + end + + pt.find('tag').each do |tag| + way.add_tag_keyval(tag['k'], tag['v']) + end + + pt.find('nd').each do |nd| + way.add_nd_num(nd['ref']) end return way diff --git a/config/routes.rb b/config/routes.rb index e4d68f87a..64293b34d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,7 @@ ActionController::Routing::Routes.draw do |map| # API map.connect "api/#{API_VERSION}/changeset/create", :controller => 'changeset', :action => 'create' + map.connect "api/#{API_VERSION}/changeset/upload", :controller => 'changeset', :action => 'upload' map.connect "api/#{API_VERSION}/node/create", :controller => 'node', :action => 'create' map.connect "api/#{API_VERSION}/node/:id/ways", :controller => 'way', :action => 'ways_for_node', :id => /\d+/ -- 2.43.2