rietta/git_time_extractor

View on GitHub
lib/git_time_extractor.rb

Summary

Maintainability
A
25 mins
Test Coverage
#
# Extract Reasonable Developer Time Records from a GIT Repository's Commit Log
#
# This is inspired by a RAKE task publicly posted by Sharad at
# http://www.tatvartha.com/2010/01/generating-time-entry-from-git-log/.
# However, it has been adapted to run without Rails from the command line.
#
# Portions (C) 2014 Rietta Inc. and licensed under the terms of the BSD license.
#
class GitTimeExtractor
  VERSION = '0.3.2'

  require 'autoload'
  require 'set'

  attr_accessor :options, :summary, :authors

  def initialize( opts = {
                          project: "Untitled",
                          path_to_repo: "./",
                          output_file: "-",
                          initial_effort_mins: 30,
                          merge_effort_mins: 30,
                          session_duration_hrs: 3,
                          max_commits: 1000
                        } )
    @options = opts
    @authors_hash = Hash.new
    @authors      = Array.new
  end

  def path_to_git_repo
    options[:path_to_repo]
  end

  def path_to_output_file
    options[:output_file]
  end

  def project_name
    options[:project]
  end

  def load_git_log_entries(path_to_git_repo)
    # Open the GIT Repository for Reading
    logger = Logger.new(STDOUT)
    logger.level = Logger::WARN
    g = Git.open(path_to_git_repo, :log => logger)
    logs = g.log(@options[:max_commits])
    return logs.entries.reverse
  end

  # Scan through the commit log entries and split into each author's accounting
  def distribute_entries_to_authors(log_entries)
    # Go through the GIT commit records and construct the time
    log_entries.each_with_index do |commit, index|
      key = commit.author.email
       if @authors_hash[key].nil?
         @authors_hash[key] = Author.new(@options)
       end
       @authors_hash[key].add_commit(commit)
    end # log_entries.each_with_index

    if @authors_hash
      @authors_hash.each do |key, value|
        if value
          value.tabulate_days
          @authors << value
          #puts "DEBUG: Looking at #{key}, #{value}, with #{value.rows.count} commits"
        end
      end
    end

    #puts "DEBUG: #{@authors.count} authors"

    return @authors
  end # distribute_entries_to_authors

  def prepare_rows_for_csv
    rows = Array.new
    rows << header_row_template()
    @authors.each do |author|
      author.rows.each do |author_row|
        rows << author_row
      end
    end
    return rows
  end

  #
  # Go through the GIT commit log, to compute the elapsed working time of each committing developer, based
  # on a few assumptions:
  #
  # (1) A series of commits within a 3 hour window are part of the same development session
  # (2) A single commit (or the first commit of the session) is considered to represent 30 minutes of work time
  # (3) The more frequent a developer commits to the repository while working, the more accurate the time report will be
  #
  #
  def process_git_log_into_time
    distribute_entries_to_authors( load_git_log_entries( path_to_git_repo ) )
    return prepare_rows_for_csv
  end # process_git_log_into_time


  def prepare_rows_for_summary_csv
    rows = Array.new
    rows << summary_header_row_template()
    commits = 0
    hours = 0
    authors = Array.new
    @authors.each do |author|
      commits += author.total_commits()
      hours += author.total_working_hours()
      authors << author.commits[0].author.name + " (" + author.commits[0].author.email + ")"
    end
    rows << [
      hours,
      commits,
      authors.join(";")
    ]
    return rows
  end

  #
  # create a summary of the computed times
  #
  def process_git_log_into_summary
    distribute_entries_to_authors( load_git_log_entries( path_to_git_repo ) )
    return prepare_rows_for_summary_csv
  end # process_git_log_into_time


  def prepare_rows_for_author_summary_csv
    rows = Array.new
    rows << author_summary_header_row_template()
    commits = 0
    hours = 0
    authors = Array.new
    @authors.each do |author|
      rows << [
        author.total_commits(),
        author.total_working_hours(),
        author.commits[0].author.name,
        author.commits[0].author.email,
      ]
    end
    return rows
  end

  #
  # create a summary per author
  #
  def process_git_log_into_author_summary
    distribute_entries_to_authors( load_git_log_entries( path_to_git_repo ) )
    return prepare_rows_for_author_summary_csv
  end # process_git_log_into_time

  def process
    if options[:project_total]
      process_git_log_into_summary
    elsif options[:compact]
      process_git_log_into_author_summary
    else
      process_git_log_into_time
    end
  end

  #####################################
  private

  def header_row_template
    [
      'Date',
      'Git Commits Count',
      'Pivotal Stories Count',
      'Minutes',
      'Hours',
      'Person',
      'Email',
      'Project',
      'Notes',
      'Pivotal Stories',
      'Week Number',
      'Year'
    ]
  end # header_row_template

  def author_summary_header_row_template
    [
      'Total Git Commits Count',
      'Total Hours',
      'Person',
      'Email',
      # 'From Date',
      # 'To Date',
    ]
  end # summary_header_row_template

  def summary_header_row_template
    [
      # 'From Date',
      # 'To Date',
      'Total Git Commits Count',
      'Total Hours',
      'Collaborators',
    ]
  end # summary_header_row_template

end # class GitTimeExtractor