#!/usr/bin/env python
# -*- mode:Python; tab-width: 3 -*-
"""Gets some statistics about authors from a CVS repository.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: cvs-author-statistics.py 7614 2006-12-02 18:13:42Z gradha $
"""
__author__ = "Grzegorz Adam Hankiewicz"
__date__ = "$Date: 2006-12-02 11:13:42 -0700 (Sat, 02 Dec 2006) $"
__version__ = "$Revision: 7614 $"
__email__ = "gradha@users.sourceforge.net"
__credits__ = ""
import time
import popen2
import re
import string
import sys
HUMAN_VERSION = "0.1.2"
CVS_DATE_FORMAT = "%Y/%m/%d %H:%M:%S"
SECONDS_IN_A_DAY = 60 * 60 * 24
CVS_REGEX = re.compile(r"date: (?P<date>[^;]+);\s+author: (?P<name>[^;]+);")
def usage_information(exit_code = 0, binary_name = "cvs-author-statistics.py"):
"""Prints usage information and terminates execution."""
print """Usage: %s [-hv]
-h, --help
Print this help screen.
-v, --version
Print version number and exit.
-i xxx, --ignore xxx
Comma separated list of people ignored in the statistic.
Usage examples:
%s -i anoncvs_pisg
""" % (binary_name, binary_name)
sys.exit(exit_code)
def process_command_line(argv = None):
"""Extracts from argv the options and returns them in a tuple.
This function is a command line wrapper against main_process,
it returns a tuple which you can `apply' calling main_process. If
something in the command line is missing, the program will exit
with a hopefully helpfull message.
args should be a list with the full command line. If it is None
or empty, the arguments will be extracted from sys.argv. The
correct format of the accepted command line is documented by
usage_information.
"""
import getopt
if not argv:
argv = sys.argv
short_options = "hvi:f:"
long_options = ["help", "version", "ignore=", "file="]
try:
opts, args = getopt.getopt(argv[1:], short_options, long_options)
except getopt.error, msg:
print "Error processing command line: %s\n" % msg
usage_information(2)
ignored_users = {}
file_output = None
for option, value in opts:
if option in ("-h", "--help"):
usage_information()
elif option in ("-v", "--version"):
print HUMAN_VERSION
sys.exit(0)
elif option in ("-i", "--ignore"):
for user in string.split(value, ","):
ignored_users[user] = 1
elif option in ("-f", "--file"):
file_output = value
ignored_users = ignored_users.keys()
return ignored_users, file_output
def get_time_information_from_cvs(ignored_users):
"""Retrieves author information from CVS as a dictionary."""
child_stdout, child_stdin = popen2.popen2(["cvs", "log"])
child_stdin.close()
line = child_stdout.readline()
authors = {}
while line:
match = CVS_REGEX.match(line)
if match and match.group("name") not in ignored_users:
date_in_seconds = time.mktime(
time.strptime(match.group("date"), CVS_DATE_FORMAT))
name = match.group("name")
try:
old_date = authors[name]
if old_date < date_in_seconds:
authors[name] = date_in_seconds
except KeyError:
authors[name] = date_in_seconds
line = child_stdout.readline()
child_stdout.close()
return authors
def main_process(ignored_users, file_output):
"""Shows how long ago each user touched CVS.
Pass in ignored users the list of usernames to ignore from the
CVS log. file_output can be a string with the filename of where to
put the results, otherwise data will be dumped on standard output.
As an extra bonus, if the data to output is void, no file will be
created.
"""
authors = get_time_information_from_cvs(ignored_users)
max_length = 0
for name in authors.keys():
max_length = max(max_length, len(name))
current_time = time.time()
names = authors.keys()
names.sort()
output_lines = []
for name in names:
difference = current_time - authors[name]
if difference < 0:
output_lines.append("%s touched CVS in a future timezone"
"\n" % (string.ljust(name, max_length)))
else:
days = difference / SECONDS_IN_A_DAY
months, days = divmod(days, 30)
output_lines.append("%s touched CVS %02d months and %02d days "
"ago\n" % (string.ljust(name, max_length), months, days))
if output_lines:
out = sys.stdout
try:
if file_output:
out = open(file_output, "wt")
except IOError, msg:
print "Error opening %s\n%s" % (file_output, msg)
out.writelines(output_lines)
# Note we are lazy and don't close the output file.
if __name__ == "__main__":
"""Entry point of the script."""
ignored_users, file_output = process_command_line()
main_process(ignored_users, file_output)