forked from zerebubuth/openstreetmap-license-change
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a677cdd
Showing
7 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
def mkstruct(*args) | ||
klass = Struct.new(*args) | ||
klass.class_eval do | ||
def to_s | ||
self.class.to_s + "[" + each_pair.collect {|k,v| "#{k}=#{v.inspect}"}.join(",") + "]" | ||
end | ||
def inspect | ||
self.to_s | ||
end | ||
def pretty_inspect | ||
self.to_s | ||
end | ||
def pretty_print_inspect | ||
self.to_s | ||
end | ||
def pretty_print(io) | ||
io.text(self.to_s) | ||
end | ||
end | ||
return klass | ||
end | ||
|
||
Delete = mkstruct(:klass, :element_id) | ||
Redact = mkstruct(:klass, :element_id, :version, :mode) | ||
Edit = mkstruct(:obj) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
require './osm' | ||
require './db' | ||
require './actions' | ||
|
||
class History | ||
def initialize(versions) | ||
# todo: ensure sort | ||
@versions = versions | ||
@tainted_keys = Array.new | ||
@tainted_values = Hash.new | ||
@cleans = Array.new | ||
end | ||
|
||
def each_version | ||
@versions.each do |v| | ||
yield v | ||
end | ||
end | ||
|
||
def merge_clean_onto_clean(obj) | ||
@cleans << true | ||
end | ||
|
||
def merge_clean_onto_dirty(obj) | ||
@cleans << true | ||
end | ||
|
||
def merge_dirty_onto_clean(obj) | ||
@cleans << false | ||
end | ||
|
||
def merge_dirty_onto_dirty(obj) | ||
@cleans << false | ||
end | ||
|
||
def is_clean? | ||
@cleans.all? | ||
end | ||
|
||
def actions | ||
acts = Array.new | ||
clean_flag = true | ||
prev_obj = nil | ||
max_version = nil | ||
@versions.zip(@cleans).each do |obj,clean| | ||
clean_flag = clean_flag && clean | ||
unless clean_flag | ||
if acts.empty? | ||
if prev_obj.nil? | ||
acts << Delete[obj.class, obj.element_id] | ||
else | ||
acts << Edit[prev_obj] | ||
end | ||
end | ||
acts << Redact[obj.class, obj.element_id, obj.version, clean ? :visible : :hidden] | ||
end | ||
prev_obj = obj | ||
max_version = obj.version | ||
end | ||
# need to adjust any edit actions to represent changes from | ||
# the last version of the object we've seen. | ||
if (not acts.empty?) and (acts[0].class == Edit) | ||
acts[0] = acts[0].clone | ||
acts[0].obj.changeset_id = -1 | ||
acts[0].obj.version = max_version | ||
end | ||
acts | ||
end | ||
end | ||
|
||
class ChangeBot | ||
def initialize(db) | ||
@db = db | ||
@pending_deletes = Array.new | ||
end | ||
|
||
def action_for(history) | ||
h = History.new(history) | ||
h.each_version do |element| | ||
if changeset_is_accepted?(element.changeset_id) | ||
if h.is_clean? | ||
h.merge_clean_onto_clean(element) | ||
else | ||
h.merge_clean_onto_dirty(element) | ||
end | ||
else | ||
if h.is_clean? | ||
h.merge_dirty_onto_clean(element) | ||
else | ||
h.merge_dirty_onto_dirty(element) | ||
end | ||
end | ||
end | ||
h.actions | ||
end | ||
|
||
def changeset_is_accepted?(changeset_id) | ||
cs = @db.changeset(changeset_id) | ||
accepted = cs.user.accepted_cts? | ||
accepted = accepted or (not cs.user.adopter.nil? and cs.user.adopter.accepted_cts?) | ||
accepted = accepted or cs.override_accepted? | ||
return accepted | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
class Changeset | ||
attr_accessor :user | ||
|
||
def self.[](user, override_accepted = false) | ||
Changeset.new(user, override_accepted) | ||
end | ||
|
||
def override_accepted? | ||
@override_accepted | ||
end | ||
|
||
private | ||
def initialize(user, override_accepted) | ||
@user = user | ||
@override_accepted = override_accepted | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require './osm' | ||
|
||
class DB | ||
def initialize(changesets) | ||
@changesets = changesets | ||
end | ||
|
||
def changeset(cs_id) | ||
@changesets[cs_id] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
module OSM | ||
|
||
module Taggable | ||
attr_accessor :tags | ||
private | ||
def init_tags(tags) | ||
@tags = tags | ||
end | ||
end | ||
|
||
module Element | ||
attr_accessor :element_id, :changeset_id, :timestamp, :visible, :version | ||
private | ||
def init_attrs(attrs) | ||
@element_id = attrs[:id] | ||
@changeset_id = attrs[:changeset] | ||
@timestamp = attrs[:timestamp] | ||
@visible = attrs[:visible] || false | ||
@version = attrs[:version] | ||
end | ||
|
||
def self.parse_options(options) | ||
groups = options.group_by {|k, v| k.instance_of? String and v.instance_of? String} | ||
tags = groups[true].nil? ? Hash.new : Hash[groups[true]] | ||
attrs = groups[false].nil? ? Hash.new : Hash[groups[false]] | ||
return tags, attrs | ||
end | ||
|
||
def compare(o, arg, *args) | ||
cmp = self.send(arg) <=> o.send(arg) | ||
if cmp != 0 or args.empty? | ||
cmp | ||
else | ||
compare(o, *args) | ||
end | ||
end | ||
end | ||
|
||
class Node | ||
include Taggable, Element, Comparable | ||
|
||
attr_accessor :position | ||
|
||
def self.[](pos, options = {}) | ||
tags, attrs = Element.parse_options(options) | ||
Node.new(attrs, pos, tags) | ||
end | ||
|
||
def <=>(o) | ||
compare(o, :position, :tags, :element_id, :changeset_id, :timestamp, :visible, :version) | ||
end | ||
|
||
def to_s | ||
self.class.to_s + "[" + | ||
@position.inspect + "," + | ||
[:element_id, :changeset_id, :timestamp, :visible, :version].collect {|attr| "#{attr.inspect}=>#{self.send(attr)}"}.join(",") + "," + | ||
@tags.to_a.collect {|k,v| "#{k.inspect}=>#{v.inspect}"}.join(",") + "]" | ||
end | ||
|
||
private | ||
def initialize(attrs, pos, tags) | ||
@position = pos | ||
init_attrs(attrs) | ||
init_tags(tags) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
require './change_bot' | ||
require './user' | ||
require './changeset' | ||
require './db' | ||
require './actions' | ||
require 'test/unit' | ||
|
||
class TestChangeBox < Test::Unit::TestCase | ||
def setup | ||
@db = DB.new(1 => Changeset[User[true]], | ||
2 => Changeset[User[true]], | ||
3 => Changeset[User[false]]) | ||
end | ||
|
||
# if a node has been edited only by people who have agreed then | ||
# it should be clean. | ||
def test_simple_node_clean | ||
history = [OSM::Node[[0,0], :changeset => 1], | ||
OSM::Node[[0,0], :changeset => 2]] | ||
bot = ChangeBot.new(@db) | ||
actions = bot.action_for(history) | ||
assert_equal([], actions) | ||
end | ||
|
||
# if a node has been created by a person who hasn't agreed then | ||
# it should be deleted and the one version redacted. | ||
def test_simple_node_unclean | ||
history = [OSM::Node[[0,0], :id => 1, :changeset => 3, :version => 1]] | ||
bot = ChangeBot.new(@db) | ||
actions = bot.action_for(history) | ||
assert_equal([Delete[OSM::Node, 1], Redact[OSM::Node, 1, 1, :hidden]], actions) | ||
end | ||
|
||
# if a node has been created by a person who hasn't agreed and | ||
# edited by another who hasn't agreed then it should be deleted | ||
# and all the versions redacted. | ||
def test_simple_node_unclean_multiple_edit | ||
history = [OSM::Node[[0,0], :id => 1, :changeset => 3, :version => 1], | ||
OSM::Node[[0,0], :id => 1, :changeset => 3, :version => 2]] | ||
bot = ChangeBot.new(@db) | ||
actions = bot.action_for(history) | ||
assert_equal([Delete[OSM::Node, 1], Redact[OSM::Node, 1, 1, :hidden], Redact[OSM::Node, 1, 2, :hidden]], actions) | ||
end | ||
|
||
# if a node has been created by a person who hasn't agreed and | ||
# edited by someone who has agreed then it should be deleted | ||
# and all the versions redacted, but the version by the person | ||
# who agreed should be redacted as "visible". | ||
def test_simple_node_unclean_edited_clean_later | ||
history = [OSM::Node[[0,0], :id => 1, :changeset => 3, :version => 1], | ||
OSM::Node[[0,0], :id => 1, :changeset => 1, :version => 2]] | ||
bot = ChangeBot.new(@db) | ||
actions = bot.action_for(history) | ||
assert_equal([Delete[OSM::Node, 1], Redact[OSM::Node, 1, 1, :hidden], Redact[OSM::Node, 1, 2, :visible]], actions) | ||
end | ||
|
||
# if a node has been created by a person who has agreed, with | ||
# some tags, and then a person who hasn't agreed edits those | ||
# tags then it should be edited to revert to the previous | ||
# version of that node and the non-agreeing edit should be | ||
# redacted. | ||
def test_simple_node_clean_edited_unclean_later | ||
history = [OSM::Node[[0,0], :id => 1, :changeset => 1, :version => 1, "foo" => "bar"], | ||
OSM::Node[[0,0], :id => 1, :changeset => 3, :version => 2, "foo" => "blah"]] | ||
bot = ChangeBot.new(@db) | ||
actions = bot.action_for(history) | ||
assert_equal([Edit[OSM::Node[[0,0], :id => 1, :changeset => -1, :version => 2, "foo" => "bar"]], | ||
Redact[OSM::Node, 1, 2, :hidden] | ||
], actions) | ||
end | ||
|
||
# same as above, but there's a subsequent clean edit which adds | ||
# a new tag to the element. this extra tag isn't tainted in any | ||
# way by the previous edit, so should be preserved and the extra | ||
# edit redacted 'visible'. | ||
def test_simple_node_clean_edited_unclean_later_then_clean_again | ||
history = [OSM::Node[[0,0], :id => 1, :changeset => 1, :version => 1, "foo" => "bar"], | ||
OSM::Node[[0,0], :id => 1, :changeset => 3, :version => 2, "foo" => "blah"], | ||
OSM::Node[[0,0], :id => 1, :changeset => 2, :version => 3, "foo" => "blah", "bar" => "blah"]] | ||
bot = ChangeBot.new(@db) | ||
actions = bot.action_for(history) | ||
assert_equal([Edit[OSM::Node[[0,0], :id => 1, :changeset => -1, :version => 3, "foo" => "bar", "bar" => "blah"]], | ||
Redact[OSM::Node, 1, 2, :hidden], | ||
Redact[OSM::Node, 1, 3, :visible] | ||
], actions) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
class User | ||
attr_accessor :adopter | ||
|
||
def self.[](accepted_cts, adopter = nil) | ||
User.new(accepted_cts, adopter) | ||
end | ||
|
||
def accepted_cts? | ||
@accepted_cts | ||
end | ||
|
||
private | ||
def initialize(accepted_cts, adopter) | ||
@accepted_cts = accepted_cts | ||
@adopter = adopter | ||
end | ||
end |