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.

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.

No comments: