New code for applying edits to an EDL.
This commit is contained in:
parent
5eb927565d
commit
66ea0d1826
89
sourcing/edit.py
Normal file
89
sourcing/edit.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
import attr
|
||||
|
||||
class EditOutOfRange(Exception):
|
||||
pass
|
||||
|
||||
def apply_delete(current_spans, edit):
|
||||
assert edit
|
||||
if not current_spans:
|
||||
raise ValueError('edit is out of bounds')
|
||||
|
||||
spans = []
|
||||
pos = 0
|
||||
edit_end = edit['start'] + len(edit['old'])
|
||||
|
||||
cur_span = current_spans.pop(0)
|
||||
while pos + cur_span.length < edit['start']:
|
||||
spans.append(cur_span)
|
||||
pos += cur_span.length
|
||||
cur_span = current_spans.pop(0)
|
||||
|
||||
if edit['start'] > pos:
|
||||
new_span = attr.evolve(cur_span, length=edit['start'] - pos)
|
||||
spans.append(new_span)
|
||||
|
||||
while pos + cur_span.length < edit_end:
|
||||
pos += cur_span.length
|
||||
cur_span = current_spans.pop(0)
|
||||
|
||||
if pos + cur_span.length != edit_end:
|
||||
offset = cur_span.start - pos
|
||||
new_start = offset + (edit_end - pos)
|
||||
diff = new_start - cur_span.start
|
||||
new_span = attr.evolve(cur_span,
|
||||
length=cur_span.length - diff,
|
||||
start=new_start)
|
||||
spans.append(new_span)
|
||||
|
||||
spans += current_spans
|
||||
return spans
|
||||
|
||||
def apply_insert(current_spans, edit):
|
||||
if not current_spans and edit['0'] == 0:
|
||||
return edit['span']
|
||||
|
||||
pos = 0
|
||||
spans = []
|
||||
cur_span = current_spans.pop(0)
|
||||
while pos + cur_span.length < edit['start']:
|
||||
spans.append(cur_span)
|
||||
pos += cur_span.length
|
||||
cur_span = current_spans.pop(0)
|
||||
|
||||
if edit['start'] >= pos:
|
||||
length_a = edit['start'] - pos
|
||||
length_b = cur_span.length - length_a
|
||||
|
||||
if length_a:
|
||||
span_a = attr.evolve(cur_span, length=length_a)
|
||||
pos += length_a
|
||||
spans.append(span_a)
|
||||
|
||||
spans.append(edit['span'])
|
||||
pos += edit['span'].length
|
||||
|
||||
if length_b:
|
||||
span_b = attr.evolve(cur_span,
|
||||
start=cur_span.start + length_a,
|
||||
length=length_b)
|
||||
spans.append(span_b)
|
||||
|
||||
pos += length_b
|
||||
else:
|
||||
spans.append(edit['span'])
|
||||
|
||||
spans += current_spans
|
||||
return spans
|
||||
|
||||
def apply_edits(spans, edits):
|
||||
for edit in edits:
|
||||
if edit['op'] == 'delete':
|
||||
spans = apply_delete(spans, edit)
|
||||
continue
|
||||
if edit['op'] == 'insert':
|
||||
spans = apply_insert(spans, edit)
|
||||
continue
|
||||
|
||||
return spans
|
||||
|
||||
|
18
sourcing/span.py
Normal file
18
sourcing/span.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
import attr
|
||||
|
||||
def greater_than_zero(instance, attribute, value):
|
||||
if value <= 0:
|
||||
raise ValueError('must be greater than 0')
|
||||
|
||||
def is_positive(instance, attribute, value):
|
||||
if value < 0:
|
||||
raise ValueError('must be positive')
|
||||
|
||||
@attr.s
|
||||
class Span:
|
||||
url: int = attr.ib()
|
||||
start: int = attr.ib(validator=is_positive)
|
||||
length: int = attr.ib(validator=greater_than_zero)
|
||||
|
||||
def end(self) -> int:
|
||||
return self.start + self.length
|
54
tests/test_edit.py
Normal file
54
tests/test_edit.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
from sourcing.span import Span
|
||||
from sourcing.edit import apply_edits
|
||||
import pytest
|
||||
|
||||
def test_xanadoc_apply_delete_start():
|
||||
spans = [Span('http://test/test', 0, 11)]
|
||||
|
||||
edits = [{'op': 'delete', 'start': 0, 'old': 'aaa'}]
|
||||
spans = apply_edits(spans, edits)
|
||||
assert spans == [Span('http://test/test', 3, 8)]
|
||||
|
||||
def test_xanadoc_apply_delete_start_offset():
|
||||
offset = 14
|
||||
spans = [Span('http://test/test', offset, 11)]
|
||||
|
||||
edits = [{'op': 'delete', 'start': 0, 'old': 'aaa'}]
|
||||
spans = apply_edits(spans, edits)
|
||||
assert spans == [Span('http://test/test', offset + 3, 8)]
|
||||
|
||||
def test_xanadoc_apply_delete_middle():
|
||||
spans = [Span('http://test/test', 0, 11)]
|
||||
|
||||
edits = [{'op': 'delete', 'start': 4, 'old': 'bbb'}]
|
||||
spans = apply_edits(spans, edits)
|
||||
assert spans == [Span('http://test/test', 0, 4),
|
||||
Span('http://test/test', 7, 4)]
|
||||
|
||||
def test_xanadoc_apply_delete_end():
|
||||
spans = [Span('http://test/test', 0, 11)]
|
||||
|
||||
edits = [{'op': 'delete', 'start': 8, 'old': 'ccc'}]
|
||||
spans = apply_edits(spans, edits)
|
||||
assert spans == [Span('http://test/test', 0, 8)]
|
||||
|
||||
def test_xanadoc_apply_delete_all():
|
||||
spans = [Span('http://test/test', 0, 3)]
|
||||
|
||||
edits = [{'op': 'delete', 'start': 0, 'old': 'aaa'}]
|
||||
spans = apply_edits(spans, edits)
|
||||
assert spans == []
|
||||
|
||||
def test_xanadoc_apply_insert_start():
|
||||
existing_span = Span('http://test/test', 0, 8)
|
||||
new_span = Span('http://test/new_span', 10, 4)
|
||||
edits = [{'op': 'insert', 'start': 0, 'span': new_span}]
|
||||
spans = apply_edits([existing_span], edits)
|
||||
assert spans == [new_span, existing_span]
|
||||
|
||||
def test_xanadoc_apply_insert_end():
|
||||
existing_span = Span('http://test/test', 0, 8)
|
||||
new_span = Span('http://test/new_span', 10, 4)
|
||||
edits = [{'op': 'insert', 'start': 8, 'span': new_span}]
|
||||
spans = apply_edits([existing_span], edits)
|
||||
assert spans == [existing_span, new_span]
|
Loading…
Reference in a new issue