Saturday, October 17, 2009

Client Subversion com Python

Existem vários clients e plugins disponíveis para acessar e manipular o respositório do subversion. Uma categoria de clients chamada Subversion Language Bindings permite o acesso ao svn via API, com suporte a linguagens como Java, Python, C++, veja mais detalhes aqui.

Abaixo um exemplo de código em Python usando o extensão pysvn que faz uso desse mecanismo e monta um relatório da quantidade de recursos manipulados agrupados por Desenvolvedor/Autor e por Data.

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import sys, pysvn, re
from datetime import date
import datetime
 
def repos(work_path):
    "Get the svn repository url"
    info = pysvn.Client().info(work_path)
 
    if info.repos:
        return info.repos
 
    # special case - checked out the trunk
    if info.url.endswith("/trunk"):
        return re.sub(r"/trunk$", "", info.url)
 
    # default to the current dir's url
    return info.url
 
def arg(index):
    # find argument, if doesnt exists return None
    args = sys.argv[1:]
    try:
        return args[index]
    except IndexError:
        return None
 
def dayBeforeToday(d):
    try:
        diff = datetime.timedelta(days=int(d))
        return date.today() - diff
    except:
        return None
 
def query(repos, author, days):
    print "get in: "+repos
 
    logs = []
    for l in pysvn.Client().log(repos, discover_changed_paths=True):
        logs.append({"author": l["author"], "date": date.fromtimestamp(l["date"]), "changed_count": len(l["changed_paths"])})
 
    if not (author in [None, "-", "." ]):
        logs = list(filter_logs(logs, lambda k, v: k == "author" and v == author))
 
    dt = dayBeforeToday(days)
    if not (dt in [None, "-", ".""0"]):
        logs = list(filter_logs(logs, lambda k, v: k == "date" and v >= dt))
 
    return logs
 
def filter_logs(logs, where=lambda k, v: True):
    #apply filter in log results
    for l in logs:
        for k, v in l.items():
            if where(k, v):
                yield l
 
def format2cols(items):
    if len(items) == 0:
        return "\n"
    max_width = max(len(str(x)) for (x,y) in items)
    template = "%%%ds %%s" % max_width
    return "\n".join(template % (x, y) for (x, y) in items)
 
def sort_uniq_c(seq):
    "Duplicate unix's 'sort | uniq -c | sort -nr'"
    count_first = ((y,x) for (x,y) in histogram(seq, ["author", "date"]).items())
    return format2cols(sorted(count_first, reverse=True))
 
def histogram(logs, groupBy):
    result = {}
    for l in logs:
        k = ""
        for s in groupBy:
            k += str(l[s])+" "
        result.setdefault(k,0)
        result[k] += l["changed_count"]
 
    return result
 
if __name__ == "__main__":
    _author = arg(1)
    _days = arg(2)
    print sort_uniq_c(query(repos(work_path = "."), _author, _days))

Na realidade usei esse script python como base, nesse script além dos filtros por Autor e/ou Dias a contagem é realizada por resources no(s) commit, a saída é agrupada por autor e data de commit.