src/cm/utils/diff.py
author rbernard
Fri, 12 Feb 2010 21:59:18 +0100
changeset 173 c1aa800029d6
parent 149 0f2c5744b39b
child 250 cae2de810f77
permissions -rw-r--r--
renaming variable

import difflib, string
from django.utils.translation import ugettext as _

def isatag(x): return x.startswith("<") and x.endswith(">")

def text_diff(a, b):
    res = []
    a, b = createlist(a), createlist(b)
    s = difflib.SequenceMatcher(isatag, a, b)
    for e in s.get_opcodes():
        if e[0] == "replace":
            res.append('<del class="diff modified">'+''.join(a[e[1]:e[2]]) + '</del><ins class="diff modified">'+''.join(b[e[3]:e[4]])+"</ins>")
        elif e[0] == "delete":
            res.append('<del class="diff">'+ ''.join(a[e[1]:e[2]]) + "</del>")
        elif e[0] == "insert":
            res.append('<ins class="diff">'+''.join(b[e[3]:e[4]]) + "</ins>")
        elif e[0] == "equal":
            res.append(''.join(b[e[3]:e[4]]))
        else: 
            raise "error parsing %s" %(e[0])
    return ''.join(res)

COLORS = [
'#FF0000',
'#EE0000',
'#DD0000',
'#CC0000',
'#BB0000',
'#AA0000',
'#990000',
'#880000',
'#770000',
]

from colorsys import hls_to_rgb, hsv_to_rgb

def generatePastelColors(n):
    s = .4
    v = .99
    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)]]
    

DEFAULT_COLOR = '#D9D9D9'
def get_colors(authors, last_white = False):
    """
    returns a dict for authors's colors (with last one white if 'last_white')
    """
    authors = set(authors)
    #new_colors = list(tuple(COLORS))
    new_colors = generatePastelColors(len(set(authors)))
    res = []
    if None in authors or '' in authors:
        res = [(_(u'unknown'),DEFAULT_COLOR) ]
    res.extend([(author,new_colors.pop()) for author in authors if author])
    #if authors[-1]:
    #    res[authors[-1]] = '#FFFFFF'
    return res
    

def text_history(versions, authors):
    res = versions[0]
    colors = get_colors(authors)
    for ind in range(len(versions)-1):
        author = authors[ind]
        color = colors.get(author,DEFAULT_COLOR)
        v_2 = versions[ind + 1]
        res = text_diff_add(v_2, res, color)
    return res,colors.items()

from cm.utils.html import surrond_text_node
 
def text_diff_add(a,b, color):
    res = []
    a, b = createlist(a), createlist(b)
    s = difflib.SequenceMatcher(isatag, a, b)
    for e in s.get_opcodes():
        if e[0] == "replace":
            html_chunk = ''.join(b[e[3]:e[4]])
            new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>')
            res.append(new_html_chunk)
            #res.append('<font style="background: %s;" class="diff modified">' %color+''.join(b[e[3]:e[4]])+"</font>")
        elif e[0] == "delete":
            pass
        elif e[0] == "insert":
            html_chunk = ''.join(b[e[3]:e[4]])
            new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>') 
            res.append(new_html_chunk)
            #res.append('<font style="background: %s;" class="diff">' %color+''.join(b[e[3]:e[4]]) + "</font>")
        elif e[0] == "equal":
            res.append(''.join(b[e[3]:e[4]]))
        else: 
            raise "error parsing %s" %(e[0])
    return ''.join(res)

def createlist(x, b=0):
    mode = 'char'
    cur = ''
    out = []
    for c in x:
        if mode == 'tag':
            if c == '>': 
                if b: cur += ']'
                else: cur += c
                out.append(cur); cur = ''; mode = 'char'
            else: cur += c
        elif mode == 'char':
            if c == '<': 
                out.append(cur)
                if b: cur = '['
                else: cur = c
                mode = 'tag'            
            elif c in string.whitespace: out.append(cur+c); cur = ''
            else: cur += c
    out.append(cur)
    return filter(lambda x: x is not '', out)