Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from difflib import SequenceMatcher
import bleach
from django.utils.html import format_html
from django.utils.text import mark_safe
def wrap_with_span(text, class_name):
return format_html('<span class="{}">{}</span>', class_name, mark_safe(text))
def wrap_deleted(text):
return wrap_with_span(text, 'deleted')
def wrap_added(text):
return wrap_with_span(text, 'added')
def compare(answer_a, answer_b, should_bleach=True):
if not answer_a and not answer_b:
# This catches the case where both results are None and we cant compare
return answer_b
if isinstance(answer_a, dict) or isinstance(answer_b, dict):
# TODO: handle file dictionaries
return answer_b
if should_bleach:
answer_a = bleach.clean(answer_a)
answer_b = bleach.clean(answer_b)
diff = SequenceMatcher(None, answer_a, answer_b)
output = []
added = []
deleted = []
for opcode, a0, a1, b0, b1 in diff.get_opcodes():
if opcode == 'equal':
if a1 - a0 > 2 or not (added or deleted):
# if there is more than 2 chars the same commit the added and removed text
if added:
output.append(wrap_added(''.join(added)))
added = []
if deleted:
output.append(wrap_deleted(''.join(deleted)))
deleted = []
output.append(diff.a[a0:a1])
else:
# ignore the small gap pretend it has been both added and removed
added.append(diff.a[a0:a1])
deleted.append(diff.a[a0:a1])
elif opcode == 'insert':
added.append(diff.b[b0:b1])
elif opcode == 'delete':
deleted.append(diff.a[a0:a1])
elif opcode == 'replace':
deleted.append(diff.a[a0:a1])
added.append(diff.b[b0:b1])
# Handle text not added to the output already
if added == deleted:
output.append(added)
else:
if added:
output.append(wrap_deleted(''.join(deleted)))
if deleted:
output.append(wrap_added(''.join(added)))
return mark_safe(''.join(output))