src/cm/utils/diff.py
author gibus
Thu, 31 Jan 2013 14:54:07 +0100
changeset 497 b478eb3f1a0d
parent 251 3eb5299e8085
permissions -rw-r--r--
One debug line forgotten in previous commit.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
149
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     1
import difflib, string
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     2
from django.utils.translation import ugettext as _
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     3
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     4
def isatag(x): return x.startswith("<") and x.endswith(">")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     5
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     6
def text_diff(a, b):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     7
    res = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     8
    a, b = createlist(a), createlist(b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     9
    s = difflib.SequenceMatcher(isatag, a, b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    10
    for e in s.get_opcodes():
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    11
        if e[0] == "replace":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    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>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    13
        elif e[0] == "delete":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    14
            res.append('<del class="diff">'+ ''.join(a[e[1]:e[2]]) + "</del>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    15
        elif e[0] == "insert":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    16
            res.append('<ins class="diff">'+''.join(b[e[3]:e[4]]) + "</ins>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    17
        elif e[0] == "equal":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    18
            res.append(''.join(b[e[3]:e[4]]))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    19
        else: 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    20
            raise "error parsing %s" %(e[0])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    21
    return ''.join(res)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    22
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    23
COLORS = [
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    24
'#FF0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    25
'#EE0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    26
'#DD0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    27
'#CC0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    28
'#BB0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    29
'#AA0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    30
'#990000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    31
'#880000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    32
'#770000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    33
]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    34
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    35
from colorsys import hls_to_rgb, hsv_to_rgb
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    36
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    37
def generatePastelColors(n):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    38
    s = .4
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    39
    v = .99
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    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)]]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    41
    
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    42
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    43
DEFAULT_COLOR = '#D9D9D9'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    44
def get_colors(authors, last_white = False):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    45
    """
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    46
    returns a dict for authors's colors (with last one white if 'last_white')
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    47
    """
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    48
    authors = set(authors)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    49
    #new_colors = list(tuple(COLORS))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    50
    new_colors = generatePastelColors(len(set(authors)))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    51
    res = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    52
    if None in authors or '' in authors:
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    53
        res = [(_(u'unknown'),DEFAULT_COLOR) ]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    54
    res.extend([(author,new_colors.pop()) for author in authors if author])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    55
    #if authors[-1]:
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    56
    #    res[authors[-1]] = '#FFFFFF'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    57
    return res
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    58
    
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    59
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    60
def text_history(versions, authors):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    61
    res = versions[0]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    62
    colors = get_colors(authors)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    63
    for ind in range(len(versions)-1):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    64
        author = authors[ind]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    65
        color = colors.get(author,DEFAULT_COLOR)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    66
        v_2 = versions[ind + 1]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    67
        res = text_diff_add(v_2, res, color)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    68
    return res,colors.items()
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    69
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    70
from cm.utils.html import surrond_text_node
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    71
 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    72
def text_diff_add(a,b, color):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    73
    res = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    74
    a, b = createlist(a), createlist(b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    75
    s = difflib.SequenceMatcher(isatag, a, b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    76
    for e in s.get_opcodes():
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    77
        if e[0] == "replace":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    78
            html_chunk = ''.join(b[e[3]:e[4]])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    79
            new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>')
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    80
            res.append(new_html_chunk)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    81
            #res.append('<font style="background: %s;" class="diff modified">' %color+''.join(b[e[3]:e[4]])+"</font>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    82
        elif e[0] == "delete":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    83
            pass
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    84
        elif e[0] == "insert":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    85
            html_chunk = ''.join(b[e[3]:e[4]])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    86
            new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>') 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    87
            res.append(new_html_chunk)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    88
            #res.append('<font style="background: %s;" class="diff">' %color+''.join(b[e[3]:e[4]]) + "</font>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    89
        elif e[0] == "equal":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    90
            res.append(''.join(b[e[3]:e[4]]))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    91
        else: 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    92
            raise "error parsing %s" %(e[0])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    93
    return ''.join(res)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    94
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    95
def createlist(x, b=0):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    96
    mode = 'char'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    97
    cur = ''
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    98
    out = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    99
    for c in x:
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   100
        if mode == 'tag':
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   101
            if c == '>': 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   102
                if b: cur += ']'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   103
                else: cur += c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   104
                out.append(cur); cur = ''; mode = 'char'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   105
            else: cur += c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   106
        elif mode == 'char':
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   107
            if c == '<': 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   108
                out.append(cur)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   109
                if b: cur = '['
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   110
                else: cur = c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   111
                mode = 'tag'            
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   112
            elif c in string.whitespace: out.append(cur+c); cur = ''
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   113
            else: cur += c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   114
    out.append(cur)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   115
    return filter(lambda x: x is not '', out)
250
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   116
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   117
# 
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   118
251
3eb5299e8085 fix import
raph
parents: 250
diff changeset
   119
from cm.ext.diff_match_patch import diff_match_patch
250
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   120
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   121
class diff_match_patch2(diff_match_patch):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   122
    def diff_prettyHtml_one_way(self, diffs, way=False, mode='red'):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   123
      """Convert a diff array into a pretty HTML report.
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   124
    
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   125
      Args:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   126
        diffs: Array of diff tuples.
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   127
        way: [None, 1, 2]
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   128
        mode: ['ins_del', 'red']
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   129
    
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   130
      Returns:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   131
        HTML representation.
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   132
      """
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   133
      html = []
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   134
      i = 0
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   135
      for (op, data) in diffs:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   136
        text = data #(data.replace("&", "&amp;").replace("<", "&lt;")
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   137
                   #.replace(">", "&gt;").replace("\n", "&para;<BR>"))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   138
        if op == self.DIFF_INSERT and (not way or way==1):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   139
            if mode=='red':
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   140
                html.append('<span class="diffchange-ins">%s</span>' % (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   141
            else:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   142
                html.append('<ins>%s</ins>' % (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   143
                
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   144
        elif op == self.DIFF_DELETE and (not way or way==2):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   145
            if mode=='red':
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   146
                html.append('<span class="diffchange-del">%s</span>'% (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   147
            else:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   148
                html.append('<del>%s</del>'% (text))                
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   149
        elif op == self.DIFF_EQUAL:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   150
          html.append("<SPAN>%s</SPAN>" % (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   151
        if op != self.DIFF_DELETE:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   152
          i += len(data)
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   153
      return "".join(html)