git-timecost/lib/timecost/cli.rb

220 lines
5 KiB
Ruby

module TimeCost
class CLI
def initialize
# FIXME: accept multiple authors
@config = {
:author_filter_enable => false,
:author_filter => ".*?",
:date_filter_enable => false,
:date_filter => [],
:branches_filter_enable => true,
:input_dump => [],
:output_dump => nil,
:range_granularity => 0.5, # in decimal hours
:verbose => false
}
@rangelist = {}
@authorlist = nil
end
def parse_cmdline args
options = OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename $0} [options]"
opts.on_tail("-v","--verbose", "Run verbosely") do |v|
@config[:verbose] = true
end
opts.on_tail("-h","--help", "Show this help") do
puts opts
exit 0
end
opts.on("-i","--input FILE", "Set input dump file") do |file|
@config[:input_dump] << file
end
opts.on("-o","--output FILE", "Set output dump file") do |file|
@config[:output_dump] = file
end
opts.on("--before DATE", "Keep only commits before DATE") do |date|
puts "set date filter to <= #{date}"
@config[:date_filter] << lambda { |other|
return (other <= DateTime.parse(date))
}
@config[:date_filter_enable] = true
end
opts.on("--after DATE", "Keep only commits after DATE") do |date|
puts "set date filter to >= #{date}"
@config[:date_filter] << lambda { |other|
return (other >= DateTime.parse(date))
}
@config[:date_filter_enable] = true
end
opts.on("-t","--time TIME", "Keep only commits on last TIME days") do |time|
puts "set time filter to latest #{time} days"
@config[:date_filter] = DateTime.now - time.to_f;
puts "set date filter to date = #{@config[:date_filter]}"
@config[:date_filter_enable] = true
end
opts.on("-a","--author AUTHOR", "Keep only commits by AUTHOR") do |author|
puts "set author filter to #{author}"
@config[:author_filter] = author
@config[:author_filter_enable] = true
end
opts.on_tail("--all", "Collect from all branches and refs") do
@config[:branches_filter_enable] = false
end
# overlap :
#
opts.on("-s","--scotch GRANULARITY", "Use GRANULARITY (decimal hours) to merge ranges") do |granularity|
puts "set scotch to #{granularity}"
@config[:range_granularity] = granularity.to_f
end
end
options.parse! args
end
def analyze_git
# git log
# foreach, create time range (before) + logs
cmd = [
"git", "log",
"--date=iso",
"--no-patch"
]
if not @config[:branches_filter_enable] then
cmd << "--all"
end
cmd.concat ["--", "."]
process = IO.popen cmd
@rangelist = {}
commit = nil
loop do
line = process.gets
break if line.nil?
# utf-8 fix ?
# line.encode!( line.encoding, "binary", :invalid => :replace, :undef => :replace)
line.strip!
case line
when /^commit (.*)$/ then
id = $1
# merge ranges & push
unless commit.nil? then
range = Range.new commit, granularity: @config[:range_granularity]
if not @rangelist.include? commit.author then
@rangelist[commit.author] = RangeList.new
end
@rangelist[commit.author].add range
end
commit = Commit.new id
# puts "commit #{id}"
when /^Author:\s*(.*?)\s*$/ then
unless commit.nil? then
commit.author = $1
if @config[:author_filter_enable] and
(not commit.author =~ /#{@config[:author_filter]}/) then
commit = nil
# reject
end
end
when /^Date:\s*(.*?)\s*$/ then
unless commit.nil? then
commit.date = $1
# reject if a some filter does not validate date
filter_keep = true
filters = @config[:date_filter]
filters.each do |f|
filter_keep &= f.call(DateTime.parse(commit.date))
end
if not filter_keep then
commit = nil
end
end
when /^\s*$/ then
# skip
else
# add as note
unless commit.nil? then
commit.note = if commit.note.nil? then line
else commit.note + "\n" + line
end
end
end
end
end
def analyze_dumps
#read ranges
@rangelist = RangeList.new
@config[:input_dump].each do |filename|
rangelist = YAML::load(File.open(filename,"r"))
rangelist.each do |range|
@rangelist.add range
end
end
end
def analyze
if @config[:input_dump].empty? then
analyze_git
else
analyze_dumps
end
end
def export
return if @config[:output_dump].nil?
puts "Exporting to %s" % @config[:output_dump]
File.open(@config[:output_dump], "w") do |file|
file.puts YAML::dump(@rangelist)
end
end
def report
return if not @config[:output_dump].nil?
@rangelist.each do |author,rangelist|
rangelist.each do |range|
puts range.to_s(!@config[:author_filter_enable]) + "\n"
end
end
total = 0
@rangelist.each do |author,rangelist|
puts "SUB-TOTAL for %s: %.2f hours\n" % [author, rangelist.sum]
total += rangelist.sum
end
puts "TOTAL: %.2f hours" % total
end
end
end