|
149
|
1 |
import difflib, string |
|
|
2 |
from django.utils.translation import ugettext as _ |
|
|
3 |
|
|
|
4 |
def isatag(x): return x.startswith("<") and x.endswith(">") |
|
|
5 |
|
|
|
6 |
def text_diff(a, b): |
|
|
7 |
res = [] |
|
|
8 |
a, b = createlist(a), createlist(b) |
|
|
9 |
s = difflib.SequenceMatcher(isatag, a, b) |
|
|
10 |
for e in s.get_opcodes(): |
|
|
11 |
if e[0] == "replace": |
|
|
12 |
res.append('<del class="diff modified">'+''.join(a[e[1]:e[2]]) + '</del><ins class="diff modified">'+''.join(b[e[3]:e[4]])+"</ins>") |
|
|
13 |
elif e[0] == "delete": |
|
|
14 |
res.append('<del class="diff">'+ ''.join(a[e[1]:e[2]]) + "</del>") |
|
|
15 |
elif e[0] == "insert": |
|
|
16 |
res.append('<ins class="diff">'+''.join(b[e[3]:e[4]]) + "</ins>") |
|
|
17 |
elif e[0] == "equal": |
|
|
18 |
res.append(''.join(b[e[3]:e[4]])) |
|
|
19 |
else: |
|
|
20 |
raise "error parsing %s" %(e[0]) |
|
|
21 |
return ''.join(res) |
|
|
22 |
|
|
|
23 |
COLORS = [ |
|
|
24 |
'#FF0000', |
|
|
25 |
'#EE0000', |
|
|
26 |
'#DD0000', |
|
|
27 |
'#CC0000', |
|
|
28 |
'#BB0000', |
|
|
29 |
'#AA0000', |
|
|
30 |
'#990000', |
|
|
31 |
'#880000', |
|
|
32 |
'#770000', |
|
|
33 |
] |
|
|
34 |
|
|
|
35 |
from colorsys import hls_to_rgb, hsv_to_rgb |
|
|
36 |
|
|
|
37 |
def generatePastelColors(n): |
|
|
38 |
s = .4 |
|
|
39 |
v = .99 |
|
|
40 |
return ['#'+''.join((hex(int(r*255))[2:],hex(int(g*255))[2:],hex(int(b*255))[2:])) for r,g,b in [hsv_to_rgb(h/200.,s,v) for h in range(1,100,100/n)]] |
|
|
41 |
|
|
|
42 |
|
|
|
43 |
DEFAULT_COLOR = '#D9D9D9' |
|
|
44 |
def get_colors(authors, last_white = False): |
|
|
45 |
""" |
|
|
46 |
returns a dict for authors's colors (with last one white if 'last_white') |
|
|
47 |
""" |
|
|
48 |
authors = set(authors) |
|
|
49 |
#new_colors = list(tuple(COLORS)) |
|
|
50 |
new_colors = generatePastelColors(len(set(authors))) |
|
|
51 |
res = [] |
|
|
52 |
if None in authors or '' in authors: |
|
|
53 |
res = [(_(u'unknown'),DEFAULT_COLOR) ] |
|
|
54 |
res.extend([(author,new_colors.pop()) for author in authors if author]) |
|
|
55 |
#if authors[-1]: |
|
|
56 |
# res[authors[-1]] = '#FFFFFF' |
|
|
57 |
return res |
|
|
58 |
|
|
|
59 |
|
|
|
60 |
def text_history(versions, authors): |
|
|
61 |
res = versions[0] |
|
|
62 |
colors = get_colors(authors) |
|
|
63 |
for ind in range(len(versions)-1): |
|
|
64 |
author = authors[ind] |
|
|
65 |
color = colors.get(author,DEFAULT_COLOR) |
|
|
66 |
v_2 = versions[ind + 1] |
|
|
67 |
res = text_diff_add(v_2, res, color) |
|
|
68 |
return res,colors.items() |
|
|
69 |
|
|
|
70 |
from cm.utils.html import surrond_text_node |
|
|
71 |
|
|
|
72 |
def text_diff_add(a,b, color): |
|
|
73 |
res = [] |
|
|
74 |
a, b = createlist(a), createlist(b) |
|
|
75 |
s = difflib.SequenceMatcher(isatag, a, b) |
|
|
76 |
for e in s.get_opcodes(): |
|
|
77 |
if e[0] == "replace": |
|
|
78 |
html_chunk = ''.join(b[e[3]:e[4]]) |
|
|
79 |
new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>') |
|
|
80 |
res.append(new_html_chunk) |
|
|
81 |
#res.append('<font style="background: %s;" class="diff modified">' %color+''.join(b[e[3]:e[4]])+"</font>") |
|
|
82 |
elif e[0] == "delete": |
|
|
83 |
pass |
|
|
84 |
elif e[0] == "insert": |
|
|
85 |
html_chunk = ''.join(b[e[3]:e[4]]) |
|
|
86 |
new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>') |
|
|
87 |
res.append(new_html_chunk) |
|
|
88 |
#res.append('<font style="background: %s;" class="diff">' %color+''.join(b[e[3]:e[4]]) + "</font>") |
|
|
89 |
elif e[0] == "equal": |
|
|
90 |
res.append(''.join(b[e[3]:e[4]])) |
|
|
91 |
else: |
|
|
92 |
raise "error parsing %s" %(e[0]) |
|
|
93 |
return ''.join(res) |
|
|
94 |
|
|
|
95 |
def createlist(x, b=0): |
|
|
96 |
mode = 'char' |
|
|
97 |
cur = '' |
|
|
98 |
out = [] |
|
|
99 |
for c in x: |
|
|
100 |
if mode == 'tag': |
|
|
101 |
if c == '>': |
|
|
102 |
if b: cur += ']' |
|
|
103 |
else: cur += c |
|
|
104 |
out.append(cur); cur = ''; mode = 'char' |
|
|
105 |
else: cur += c |
|
|
106 |
elif mode == 'char': |
|
|
107 |
if c == '<': |
|
|
108 |
out.append(cur) |
|
|
109 |
if b: cur = '[' |
|
|
110 |
else: cur = c |
|
|
111 |
mode = 'tag' |
|
|
112 |
elif c in string.whitespace: out.append(cur+c); cur = '' |
|
|
113 |
else: cur += c |
|
|
114 |
out.append(cur) |
|
|
115 |
return filter(lambda x: x is not '', out) |