gitwakatime 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +5 -1
- data/README.md +10 -6
- data/gitwakatime.gemspec +2 -0
- data/lib/gitwakatime.rb +55 -2
- data/lib/gitwakatime/action.rb +4 -0
- data/lib/gitwakatime/cli.rb +26 -10
- data/lib/gitwakatime/commit.rb +18 -21
- data/lib/gitwakatime/commited_file.rb +38 -25
- data/lib/gitwakatime/durations.rb +87 -0
- data/lib/gitwakatime/log.rb +1 -0
- data/lib/gitwakatime/mapper.rb +17 -11
- data/lib/gitwakatime/query.rb +17 -12
- data/lib/gitwakatime/timer.rb +23 -20
- data/lib/gitwakatime/version.rb +1 -1
- data/spec/commit_spec.rb +0 -0
- data/spec/commited_file_spec.rb +93 -0
- data/spec/dummy/dot_git/sourcetreeconfig +2 -2
- data/spec/mapper_spec.rb +10 -6
- data/spec/query_spec.rb +17 -7
- data/spec/spec_helper.rb +17 -1
- data/spec/timer_spec.rb +10 -5
- metadata +36 -3
- data/lib/gitwakatime/actions.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46d9e8b02b43ba532f6631155c4beadff25d65e1
|
4
|
+
data.tar.gz: 7ea139ea0454e766c82186a526bc17cad859a542
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: baed9a3f9d7cd015d0148356e657cac46b792ae0b8276c4ad61848ec26cfc700bbfbf0b6b36abff102bf825cedf9723292143aa689033bdcd312ca1fa0bce1e9
|
7
|
+
data.tar.gz: a51aca840239070583c1f81f3326931f72cd023e893850b7e755c8da30308bfb5325f844e387891fca7ca98dffdb8dc0e5a70a0877848120de0291c180e99d5c
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -4,10 +4,8 @@
|
|
4
4
|
[](http://badge.fury.io/rb/gitwakatime)
|
5
5
|
[](https://codeclimate.com/github/rposborne/gitwakatime)
|
6
6
|
|
7
|
-
GitWakaTime is a
|
8
|
-
The
|
9
|
-
|
10
|
-
|
7
|
+
GitWakaTime is a mash up between data obtained through "wakatime" and the data we all create using git.
|
8
|
+
The principal is to capture a baseline of activity for a task and answer the age old question "How much time did I spend on this?" or "What is the minimum amount I can charge for my time"
|
11
9
|
|
12
10
|
## Installation
|
13
11
|
|
@@ -15,15 +13,21 @@ Install the gem:
|
|
15
13
|
|
16
14
|
$ gem install gitwakatime
|
17
15
|
|
18
|
-
|
16
|
+
Run the setup command: (you will need your wakatime api key). Creates a .gitwakatime.yml file on the user's home directory ~/.gitwakatime.yml which will contain your api keys
|
19
17
|
|
20
|
-
Creates a .gitwakatime.yml file on the user's home directory ~/.gitwakatime.yml which will contain your api keys
|
21
18
|
$ gitwakatime init
|
22
19
|
|
20
|
+
|
21
|
+
## Usage
|
23
22
|
Process the current directory
|
24
23
|
|
25
24
|
$ gitwakatime tally
|
26
25
|
|
26
|
+
Hard reset of the local cache database
|
27
|
+
|
28
|
+
$ gitwakatime reset
|
29
|
+
|
30
|
+
|
27
31
|
## Output
|
28
32
|
Total Recorded time 21 hrs 59 mins 59 secs
|
29
33
|
Total Commited Time 18 hrs 50 mins 53 secs
|
data/gitwakatime.gemspec
CHANGED
@@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_runtime_dependency 'chronic_duration', '>=0'
|
26
26
|
spec.add_runtime_dependency 'colorize'
|
27
27
|
spec.add_runtime_dependency 'activesupport'
|
28
|
+
spec.add_runtime_dependency 'sequel'
|
29
|
+
spec.add_runtime_dependency 'sqlite3'
|
28
30
|
spec.add_development_dependency('bundler', ['>= 0'])
|
29
31
|
spec.add_development_dependency 'rake'
|
30
32
|
spec.add_development_dependency 'rspec'
|
data/lib/gitwakatime.rb
CHANGED
@@ -1,5 +1,16 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
if ENV['thor_env'] != 'test'
|
3
|
+
DB = Sequel.connect("sqlite://#{File.join(Dir.home, '.wakatime.sqlite')}")
|
4
|
+
else
|
5
|
+
# Use a in memory db to have a nice clean testing bed.
|
6
|
+
DB = Sequel.sqlite
|
7
|
+
end
|
8
|
+
|
9
|
+
DB.use_timestamp_timezones = false
|
10
|
+
|
1
11
|
require 'gitwakatime/version'
|
2
|
-
require 'gitwakatime/
|
12
|
+
require 'gitwakatime/durations'
|
13
|
+
require 'gitwakatime/action'
|
3
14
|
require 'gitwakatime/commit'
|
4
15
|
require 'gitwakatime/mapper'
|
5
16
|
require 'gitwakatime/query'
|
@@ -7,21 +18,63 @@ require 'gitwakatime/timer'
|
|
7
18
|
require 'gitwakatime/log'
|
8
19
|
require 'gitwakatime/commited_file'
|
9
20
|
require 'gitwakatime/cli'
|
21
|
+
|
10
22
|
# Silence is golden
|
11
23
|
module GitWakaTime
|
12
24
|
class Configuration
|
13
|
-
attr_accessor :api_key, :log_level, :root, :project
|
25
|
+
attr_accessor :api_key, :log_level, :root, :project, :git
|
14
26
|
|
15
27
|
def initialize
|
16
28
|
self.api_key = nil
|
17
29
|
self.log_level = :info
|
18
30
|
end
|
19
31
|
|
32
|
+
def user_name
|
33
|
+
GitWakaTime.config.git.config('user.name')
|
34
|
+
end
|
35
|
+
|
20
36
|
def load_config_yaml
|
21
37
|
yaml = YAML.load_file(File.join(Dir.home, '.wakatime.yml'))
|
22
38
|
self.api_key = yaml[:api_key]
|
23
39
|
self.log_level = yaml[:log_level]
|
24
40
|
end
|
41
|
+
|
42
|
+
def setup_local_db
|
43
|
+
DB.create_table? :commits do
|
44
|
+
primary_key :id
|
45
|
+
String :sha
|
46
|
+
String :parent_sha
|
47
|
+
String :project
|
48
|
+
integer :time_in_seconds, default: 0
|
49
|
+
datetime :date
|
50
|
+
text :message
|
51
|
+
String :author
|
52
|
+
end
|
53
|
+
|
54
|
+
DB.create_table? :commited_files do
|
55
|
+
primary_key :id
|
56
|
+
integer :commit_id
|
57
|
+
String :dependent_sha
|
58
|
+
DateTime :dependent_date
|
59
|
+
integer :time_in_seconds, default: 0
|
60
|
+
String :sha
|
61
|
+
String :name
|
62
|
+
String :project
|
63
|
+
index :dependent_sha
|
64
|
+
index :sha
|
65
|
+
end
|
66
|
+
|
67
|
+
DB.create_table? :actions do
|
68
|
+
primary_key :id
|
69
|
+
String :uuid
|
70
|
+
DateTime :time
|
71
|
+
integer :duration, default: 0
|
72
|
+
String :file
|
73
|
+
String :branch
|
74
|
+
String :project
|
75
|
+
index :uuid, unique: true
|
76
|
+
end
|
77
|
+
end
|
25
78
|
end
|
26
79
|
|
27
80
|
def self.config
|
data/lib/gitwakatime/cli.rb
CHANGED
@@ -18,35 +18,51 @@ module GitWakaTime
|
|
18
18
|
method_option :file, aliases: '-f', default: '.'
|
19
19
|
|
20
20
|
def init
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
unless File.exist?(File.join(Dir.home, '.wakatime.yml'))
|
22
|
+
api_key = ask('What is your wakatime api key? ( Get it here https://wakatime.com/settings):')
|
23
|
+
say('Adding .wakatime.yml to home directory')
|
24
|
+
|
25
|
+
create_file File.join(Dir.home, '.wakatime.yml') do
|
26
|
+
YAML.dump(api_key: api_key, last_commit: nil, log_level: :info)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
reset
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'reset', 'Reset local sqlite db'
|
33
|
+
def reset
|
34
|
+
DB.disconnect
|
35
|
+
|
36
|
+
db_path = File.expand_path(File.join(Dir.home, '.wakatime.sqlite'))
|
37
|
+
FileUtils.rm_r(db_path) if File.exist?(db_path)
|
38
|
+
DB.connect("sqlite://#{db_path}")
|
39
|
+
GitWakaTime.config.setup_local_db
|
26
40
|
end
|
27
41
|
|
28
42
|
desc 'tally', 'Produce time spend for each commit and file in each commit'
|
29
43
|
method_option :file, aliases: '-f', default: '.'
|
30
44
|
method_option :start_on, aliases: '-s', default: nil
|
31
45
|
def tally
|
46
|
+
GitWakaTime.config.setup_local_db
|
32
47
|
path, GitWakaTime.config.root = File.expand_path(options.file)
|
33
48
|
date = Date.parse(options.start_on) if options.start_on
|
34
49
|
date = 1.month.ago.beginning_of_month unless options.start_on
|
35
50
|
GitWakaTime.config.load_config_yaml
|
36
|
-
|
37
|
-
@
|
51
|
+
GitWakaTime.config.git = Git.open(path)
|
52
|
+
@git_map = Mapper.new(start_at: date)
|
53
|
+
@actions = Query.new(Commit.all, File.basename(path)).get
|
38
54
|
|
39
|
-
@timer = Timer.new(
|
55
|
+
@timer = Timer.new(Commit.all, @actions, File.basename(path)).process
|
40
56
|
|
41
57
|
@timer.each do |date, commits|
|
42
58
|
Log.new format('%-40s %-40s'.blue,
|
43
59
|
date,
|
44
|
-
"Total #{ChronicDuration.output commits.map(&:time_in_seconds).reduce(&:+)}"
|
60
|
+
"Total #{ChronicDuration.output commits.map(&:time_in_seconds).compact.reduce(&:+).to_i}"
|
45
61
|
)
|
46
62
|
commits.each do |commit|
|
47
63
|
# Log.new commit.message
|
48
64
|
Log.new commit.to_s
|
49
|
-
commit.
|
65
|
+
commit.commited_files.each { |file| Log.new file.to_s }
|
50
66
|
end
|
51
67
|
end
|
52
68
|
end
|
data/lib/gitwakatime/commit.rb
CHANGED
@@ -1,28 +1,15 @@
|
|
1
1
|
module GitWakaTime
|
2
|
-
class Commit
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
@raw_commit = commit
|
7
|
-
@sha = @raw_commit.sha
|
8
|
-
@date = @raw_commit.date
|
9
|
-
@message = @raw_commit.message
|
10
|
-
@author = @raw_commit.author
|
11
|
-
@time_in_seconds = 0
|
12
|
-
@git = git
|
13
|
-
@load_files = load_files
|
14
|
-
@files = get_files if load_files
|
15
|
-
end
|
16
|
-
|
17
|
-
def inspect
|
18
|
-
[sha[0..8], time_in_seconds]
|
2
|
+
class Commit < Sequel::Model
|
3
|
+
one_to_many :commited_files
|
4
|
+
def after_create
|
5
|
+
get_files
|
19
6
|
end
|
20
7
|
|
21
8
|
def to_s
|
22
9
|
format(' %-8s %8s %-30s %-80s'.green,
|
23
10
|
sha[0..8],
|
24
11
|
date,
|
25
|
-
ChronicDuration.output(time_in_seconds),
|
12
|
+
ChronicDuration.output(time_in_seconds.to_i),
|
26
13
|
message
|
27
14
|
)
|
28
15
|
end
|
@@ -31,13 +18,23 @@ module GitWakaTime
|
|
31
18
|
@files.sort { |f| f.commit.date }.first
|
32
19
|
end
|
33
20
|
|
21
|
+
def time_in_seconds
|
22
|
+
commited_files.collect {|f| f.time_in_seconds}.inject(:+)
|
23
|
+
end
|
24
|
+
|
34
25
|
private
|
35
26
|
|
36
27
|
def get_files
|
28
|
+
@raw_commit = GitWakaTime.config.git.gcommit(sha)
|
37
29
|
# TODO: Assume gap time to lookup time prior to first commit.
|
38
|
-
|
39
|
-
|
40
|
-
|
30
|
+
if @raw_commit.parent
|
31
|
+
update(parent_sha: @raw_commit.parent.sha)
|
32
|
+
|
33
|
+
@raw_commit.diff_parent.stats[:files].keys.map do |file|
|
34
|
+
CommitedFile.find_or_create(commit_id: id, name: file) do |c|
|
35
|
+
c.update(sha: sha, project: project)
|
36
|
+
end
|
37
|
+
end
|
41
38
|
end
|
42
39
|
end
|
43
40
|
end
|
@@ -1,22 +1,18 @@
|
|
1
1
|
module GitWakaTime
|
2
|
-
class CommitedFile
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@time_in_seconds = 0
|
10
|
-
|
11
|
-
@dependent_commit = find_dependent_commit(name)
|
2
|
+
class CommitedFile < Sequel::Model
|
3
|
+
many_to_one :commit
|
4
|
+
# No two committed files should have the same name + dependent_date this
|
5
|
+
# means a split tree, and we should split time between the two, or more, commits.
|
6
|
+
|
7
|
+
def before_create
|
8
|
+
find_dependent_commit(name)
|
12
9
|
end
|
13
10
|
|
14
11
|
def to_s
|
15
|
-
format('
|
16
|
-
(
|
17
|
-
ChronicDuration.output(
|
12
|
+
format(' %-20s %-40s %-100s '.blue,
|
13
|
+
(dependent_sha[0..8] if dependent_sha),
|
14
|
+
ChronicDuration.output(time_in_seconds.to_f),
|
18
15
|
name
|
19
|
-
|
20
16
|
)
|
21
17
|
end
|
22
18
|
|
@@ -24,26 +20,43 @@ module GitWakaTime
|
|
24
20
|
|
25
21
|
def find_dependent_commit(name)
|
26
22
|
i = 1
|
27
|
-
dependent = nil
|
28
|
-
commit = 1
|
29
23
|
|
24
|
+
# Call git log for path, loop through till we find a valid commit or run
|
25
|
+
# out of commits to check
|
26
|
+
commits = load_dependent_commits(name)
|
30
27
|
begin
|
31
|
-
commit =
|
32
|
-
|
28
|
+
commit = commits[i]
|
29
|
+
|
30
|
+
if commit && allowed_commit(commit)
|
31
|
+
|
32
|
+
self.dependent_date = commit.date
|
33
|
+
self.dependent_sha = commit.sha
|
34
|
+
|
35
|
+
# This is the magical fix for the split tree issue
|
36
|
+
# Current though is this will fail if more than 2 split tree files
|
37
|
+
split_tree_file = CommitedFile.where(name: name, dependent_sha: commit.sha).first
|
38
|
+
if split_tree_file && split_tree_file.commit
|
39
|
+
if self.commit.date < split_tree_file.commit.date
|
40
|
+
self.dependent_date = split_tree_file.commit.date
|
41
|
+
elsif self.commit.date > split_tree_file.commit.date
|
42
|
+
split_tree_file.update(dependent_date: commit.date)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
33
47
|
i += 1
|
34
|
-
end until !
|
35
|
-
dependent
|
48
|
+
end until !dependent_sha.nil? || commit.nil?
|
36
49
|
end
|
37
50
|
|
38
51
|
def allowed_commit(commit)
|
39
|
-
return false if commit.
|
40
|
-
return false if commit.author.name !=
|
41
|
-
return false if commit.
|
52
|
+
return false if commit.sha == sha
|
53
|
+
return false if commit.author.name != GitWakaTime.config.user_name
|
54
|
+
return false if commit.parents.size > 1
|
42
55
|
true
|
43
56
|
end
|
44
57
|
|
45
|
-
def
|
46
|
-
|
58
|
+
def load_dependent_commits(name)
|
59
|
+
GitWakaTime.config.git.log.object(sha).path(name)
|
47
60
|
rescue Git::GitExecuteError
|
48
61
|
puts error
|
49
62
|
nil
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module GitWakaTime
|
2
|
+
# Extract Duration Data from Actions for the WAKATIME API
|
3
|
+
class Durations
|
4
|
+
attr_accessor :actions
|
5
|
+
def initialize(args)
|
6
|
+
return @actions = args[:actions] if args[:actions]
|
7
|
+
fail if args[:project].nil?
|
8
|
+
@project = args[:project]
|
9
|
+
@args = args
|
10
|
+
@session = Wakatime::Session.new(api_key: GitWakaTime.config.api_key)
|
11
|
+
@client = Wakatime::Client.new(@session)
|
12
|
+
@actions = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_actions
|
16
|
+
unless cached?(@args)
|
17
|
+
|
18
|
+
Log.new "querying WakaTime actions for #{@project}"
|
19
|
+
time = Benchmark.realtime do
|
20
|
+
@actions = @client.actions(@args)
|
21
|
+
|
22
|
+
# remove returned actions that do not have the project we want
|
23
|
+
@actions = @actions.keep_if do |a|
|
24
|
+
a['project'] == @project
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Log.new "API took #{time}s"
|
29
|
+
persist_actions_localy(@actions)
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def persist_actions_localy(actions)
|
35
|
+
actions.each do |action|
|
36
|
+
|
37
|
+
Action.find_or_create(uuid: action['id']) do |c|
|
38
|
+
action['uuid'] = action['id']
|
39
|
+
action['time'] = Time.at(action['time'])
|
40
|
+
action.delete('id')
|
41
|
+
c.update(action)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def cached?(params)
|
48
|
+
# Check to see if this date range might be stale?
|
49
|
+
max_local_timetamp = Action.max(:time)
|
50
|
+
max_local_timetamp = (Time.parse(max_local_timetamp) + 3.day).to_date if max_local_timetamp
|
51
|
+
!(params[:start].to_date..params[:end].to_date).include?(max_local_timetamp) if max_local_timetamp
|
52
|
+
end
|
53
|
+
|
54
|
+
def actions_to_durations(_project = nil, timeout = 15)
|
55
|
+
durations = []
|
56
|
+
current = nil
|
57
|
+
|
58
|
+
@actions.each do | action |
|
59
|
+
# the first action just sets state and does nothing
|
60
|
+
unless current.nil?
|
61
|
+
|
62
|
+
# get duration since last action
|
63
|
+
duration = action.time.round - current.time.round
|
64
|
+
|
65
|
+
duration = 0.0 if duration < 0
|
66
|
+
|
67
|
+
# duration not logged if greater than the timeout
|
68
|
+
if duration < timeout * 60
|
69
|
+
|
70
|
+
# add duration to current action
|
71
|
+
current.duration = duration
|
72
|
+
|
73
|
+
# save to local db
|
74
|
+
current.save
|
75
|
+
|
76
|
+
# log current action as a duration
|
77
|
+
durations << current
|
78
|
+
end
|
79
|
+
end
|
80
|
+
# set state (re-start the clock)
|
81
|
+
current = action
|
82
|
+
|
83
|
+
end
|
84
|
+
durations
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/gitwakatime/log.rb
CHANGED
data/lib/gitwakatime/mapper.rb
CHANGED
@@ -2,23 +2,29 @@ module GitWakaTime
|
|
2
2
|
# Th
|
3
3
|
class Mapper
|
4
4
|
attr_accessor :commits, :git
|
5
|
-
def initialize(
|
5
|
+
def initialize(commits: 500, start_at: Date.today)
|
6
6
|
Log.new 'Mapping commits for dependent commits'
|
7
7
|
time = Benchmark.realtime do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Git.bare(path)
|
12
|
-
end
|
8
|
+
g = GitWakaTime.config.git
|
9
|
+
project = File.basename(g.dir.path)
|
10
|
+
logs = g.log(commits).since(start_at).until(Date.today)
|
13
11
|
|
14
|
-
|
12
|
+
@commits = logs.map do |git_c|
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
next if git_c.author.name != GitWakaTime.config.user_name
|
15
|
+
Commit.find_or_create(
|
16
|
+
sha: git_c.sha,
|
17
|
+
project: project
|
18
|
+
) do |c|
|
19
|
+
c.update(
|
20
|
+
author: git_c.author.name,
|
21
|
+
message: git_c.message,
|
22
|
+
date: git_c.date
|
23
|
+
)
|
24
|
+
end
|
19
25
|
end.compact
|
20
26
|
end
|
21
|
-
Log.new "Map Completed took #{time}s"
|
27
|
+
Log.new "Map Completed took #{time}s with #{Commit.count}"
|
22
28
|
end
|
23
29
|
end
|
24
30
|
end
|
data/lib/gitwakatime/query.rb
CHANGED
@@ -9,39 +9,44 @@ module GitWakaTime
|
|
9
9
|
@commits = commits
|
10
10
|
@api_limit = 15
|
11
11
|
@project = project
|
12
|
-
@actions = []
|
13
12
|
@requests = time_params
|
14
13
|
end
|
15
14
|
|
16
15
|
def get
|
17
16
|
@requests.each do |params|
|
18
17
|
Log.new "Requesting actions #{params[:start].to_date} to #{params[:end].to_date}".red
|
19
|
-
|
18
|
+
Durations.new(params).load_actions
|
20
19
|
end
|
21
20
|
|
22
|
-
|
21
|
+
Durations.new(actions: actions).actions_to_durations
|
22
|
+
end
|
23
|
+
|
24
|
+
def actions
|
25
|
+
Action.where('time >= ? and time <= ? ', @start_at, @end_at).where(project: @project)
|
23
26
|
end
|
24
27
|
|
25
28
|
def time_params
|
26
29
|
commits = @commits.map(&:date)
|
27
|
-
d_commits =
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
d_commits = CommitedFile.select(:dependent_date).all.map { |f| f.values[:dependent_date] }.compact
|
31
|
+
timestamps = (commits + d_commits.flatten).uniq.sort
|
32
|
+
@start_at = timestamps.first
|
33
|
+
@end_at = timestamps.last
|
34
|
+
# Always have a date range great than 1 as the num request will be 0/1 otherwise
|
35
|
+
num_requests = ((timestamps.last.to_date + 1) - timestamps.first.to_date) / @api_limit
|
32
36
|
i = 0
|
37
|
+
|
33
38
|
request_params = num_requests.to_f.ceil.times.map do
|
34
39
|
|
35
40
|
params = {
|
36
|
-
start: (timestamps.
|
37
|
-
end:
|
38
|
-
project: @project
|
41
|
+
start: (timestamps.first.to_date + (i * @api_limit)).to_time.beginning_of_day,
|
42
|
+
end: (timestamps.first.to_date + ((i + 1) * @api_limit)).to_time.end_of_day,
|
43
|
+
project: @project,
|
44
|
+
show: 'file,branch,project,time,id'
|
39
45
|
}
|
40
46
|
i += 1
|
41
47
|
params
|
42
48
|
|
43
49
|
end
|
44
|
-
|
45
50
|
request_params
|
46
51
|
end
|
47
52
|
end
|
data/lib/gitwakatime/timer.rb
CHANGED
@@ -18,22 +18,25 @@ module GitWakaTime
|
|
18
18
|
|
19
19
|
def total_commited
|
20
20
|
total_commited = ChronicDuration.output(@commits_with_duration
|
21
|
-
.map(&:time_in_seconds)
|
21
|
+
.map(&:time_in_seconds).compact
|
22
22
|
.reduce(:+).to_f)
|
23
|
-
Log.new "Total
|
23
|
+
Log.new "Total Committed Time #{total_commited} ".red
|
24
24
|
end
|
25
25
|
|
26
26
|
def process
|
27
27
|
@commits_with_duration = @commits.each do |commit|
|
28
28
|
|
29
|
-
if
|
30
|
-
commit.
|
29
|
+
if commit.commited_files.count > 0 || commit.parent_sha
|
30
|
+
commit.commited_files.each_with_index do |file, _i|
|
31
31
|
time = sum_actions relevant_actions(commit, file)
|
32
|
-
|
33
|
-
commit.time_in_seconds
|
32
|
+
file.time_in_seconds = time
|
33
|
+
commit.time_in_seconds = time
|
34
|
+
|
35
|
+
file.save
|
34
36
|
end
|
37
|
+
commit.save
|
35
38
|
else
|
36
|
-
|
39
|
+
commit.time_in_seconds = sum_actions(actions_before(@actions_with_durations, commit.date))
|
37
40
|
end
|
38
41
|
end.compact
|
39
42
|
total
|
@@ -45,38 +48,38 @@ module GitWakaTime
|
|
45
48
|
private
|
46
49
|
|
47
50
|
def relevant_actions(commit, file)
|
48
|
-
# The timestamps should be before the expected commit
|
49
|
-
actions = actions_before(commit.date)
|
50
|
-
|
51
51
|
# The file should be the same file as we expect
|
52
52
|
# TODO: Might need to pass root_path down
|
53
|
-
actions =
|
54
|
-
action[
|
53
|
+
actions = @actions_with_durations.select do |action|
|
54
|
+
action[:file].include?(File.join(File.basename(GitWakaTime.config.git.dir.path), file.name))
|
55
55
|
end
|
56
56
|
|
57
|
+
# The timestamps should be before the expected commit
|
58
|
+
actions = actions_before(actions, commit.date)
|
59
|
+
|
57
60
|
# If this file had an earlier commit ensure the actions timestamp
|
58
61
|
# is after that commit
|
59
|
-
if file.
|
60
|
-
actions = actions_after(actions, file.
|
62
|
+
if file.dependent_date
|
63
|
+
actions = actions_after(actions, file.dependent_date)
|
61
64
|
end
|
62
65
|
actions
|
63
66
|
end
|
64
67
|
|
65
|
-
def actions_before(date)
|
66
|
-
|
67
|
-
Time.at(action[
|
68
|
+
def actions_before(actions, date)
|
69
|
+
actions.select do |action|
|
70
|
+
Time.at(action[:time]) <= date
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
74
|
def actions_after(actions, date)
|
72
75
|
actions.select do |action|
|
73
|
-
Time.at(action[
|
76
|
+
Time.at(action[:time]) >= date
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
77
80
|
def sum_actions(actions)
|
78
|
-
actions.map { |action| action[
|
79
|
-
.reduce(:+).
|
81
|
+
actions.map { |action| action[:duration] }
|
82
|
+
.reduce(:+).to_i
|
80
83
|
end
|
81
84
|
end
|
82
85
|
end
|
data/lib/gitwakatime/version.rb
CHANGED
data/spec/commit_spec.rb
ADDED
File without changes
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
# * commit a4c26aeb79acb1f012201fe96e4d68e8d17c75d9 (HEAD, origin/master, origin/HEAD, master)
|
3
|
+
# | Author: rpo <rother@gmail.com>
|
4
|
+
# | Date: Sat Jan 31 15:49:07 2015 -0500
|
5
|
+
# |
|
6
|
+
# | I was edited online using git hub's editor
|
7
|
+
# |
|
8
|
+
# * commit 570f1df0505ed828656eeaf9411ddd6a6068b095
|
9
|
+
# | Author: Russell Osborne <rother@gmail.com>
|
10
|
+
# | Date: Fri Jan 30 00:19:00 2015 -0500
|
11
|
+
# |
|
12
|
+
# | And one more
|
13
|
+
# |
|
14
|
+
# * commit dcd748bd06b8a0f239d779bee4f1eaf1f4aa500d
|
15
|
+
# | Author: Russell Osborne <rother@gmail.com>
|
16
|
+
# | Date: Fri Jan 30 00:01:54 2015 -0500
|
17
|
+
# |
|
18
|
+
# | a final commit on master
|
19
|
+
# |
|
20
|
+
# * commit 57b0d5cdb6da2b2b9ac7e9847716b0c54466d1c6
|
21
|
+
# |\ Merge: e493d6f d642b3c
|
22
|
+
# | | Author: Russell Osborne <rother@gmail.com>
|
23
|
+
# | | Date: Thu Jan 29 22:27:26 2015 -0500
|
24
|
+
# | |
|
25
|
+
# | | Merge branch 'dev'
|
26
|
+
# | |
|
27
|
+
# | * commit d642b3c04c3025655a9c33e32b9d530696dcf7cc
|
28
|
+
# | | Author: Russell Osborne <rother@gmail.com>
|
29
|
+
# | | Date: Thu Jan 29 22:26:05 2015 -0500
|
30
|
+
# | |
|
31
|
+
# | | another commit on dev.
|
32
|
+
# | |
|
33
|
+
# * | commit e493d6f2ab2a702fa7f9c168b852a3b44c524f08
|
34
|
+
# |/ Author: Russell Osborne <rother@gmail.com>
|
35
|
+
# | Date: Thu Jan 29 22:26:20 2015 -0500 or 1422570380
|
36
|
+
# |
|
37
|
+
# | conflicting commit on master.
|
38
|
+
# |
|
39
|
+
# * commit 4c1ea35f9a811a0ef79da15ec85f25fce4c446ba
|
40
|
+
# | Author: Russell Osborne <rother@gmail.com>
|
41
|
+
# | Date: Thu Jan 29 22:25:08 2015 -0500 or 1422570308
|
42
|
+
# |
|
43
|
+
# | commit on dev branch
|
44
|
+
# |
|
45
|
+
# * commit 2254dd56976b5f32a2289438842e42a35a18ff86
|
46
|
+
# | Author: Russell Osborne <rother@gmail.com>
|
47
|
+
# | Date: Thu Jan 29 21:49:31 2015 -0500
|
48
|
+
# |
|
49
|
+
# | testing
|
50
|
+
# |
|
51
|
+
# * commit 052ff8c0e8c7cd39880d1536f4e27cc554e698f6
|
52
|
+
# Author: Russell Osborne <rother@gmail.com>
|
53
|
+
# Date: Thu Jan 29 21:49:12 2015 -0500
|
54
|
+
|
55
|
+
# created readme
|
56
|
+
describe 'description' do
|
57
|
+
let (:git) { Git.open(@wdir) }
|
58
|
+
|
59
|
+
before do
|
60
|
+
GitWakaTime::Commit.dataset.destroy
|
61
|
+
GitWakaTime::Commit.dataset.destroy
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'can be created ' do
|
65
|
+
GitWakaTime.config.git = git
|
66
|
+
|
67
|
+
first_commit = GitWakaTime::Commit.find_or_create(
|
68
|
+
sha: 'e493d6f2ab2a702fa7f9c168b852a3b44c524f08',
|
69
|
+
author: 'Russell Osborne',
|
70
|
+
message: 'conflicting commit on blah.',
|
71
|
+
project: git.repo.to_s,
|
72
|
+
date: Time.at(1_422_570_380)
|
73
|
+
)
|
74
|
+
|
75
|
+
expect(first_commit.commited_files.first.dependent_sha).to eql('4c1ea35f9a811a0ef79da15ec85f25fce4c446ba')
|
76
|
+
expect(first_commit.commited_files.first.dependent_date.utc.to_s).to eql('2015-01-30 03:25:08 UTC')
|
77
|
+
|
78
|
+
second_commit = GitWakaTime::Commit.find_or_create(
|
79
|
+
sha: 'd642b3c04c3025655a9c33e32b9d530696dcf7cc',
|
80
|
+
author: 'Russell Osborne',
|
81
|
+
message: 'conflicting commit on master.',
|
82
|
+
project: git.repo.to_s,
|
83
|
+
date: 'Thu Jan 29 22:26:05 2015 -0500'
|
84
|
+
)
|
85
|
+
|
86
|
+
expect(second_commit.commited_files.first.dependent_sha).to eql('4c1ea35f9a811a0ef79da15ec85f25fce4c446ba')
|
87
|
+
expect(
|
88
|
+
GitWakaTime::Commit.find(id: first_commit.id).commited_files.first.dependent_date.utc.to_s
|
89
|
+
).to eql('2015-01-30 03:25:08 UTC')
|
90
|
+
expect(second_commit.commited_files.first.dependent_date.utc.to_s).to eql(Time.at(1_422_570_380).utc.to_s)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/mapper_spec.rb
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'gitwakatime'
|
3
|
-
require 'gitwakatime/mapper'
|
4
2
|
|
5
3
|
describe 'description' do
|
4
|
+
let (:git) { Git.open(@wdir) }
|
5
|
+
before(:each) do
|
6
|
+
GitWakaTime.config.git = git
|
7
|
+
GitWakaTime::Mapper.new(start_at: Date.new(2015, 1, 28))
|
8
|
+
end
|
9
|
+
|
6
10
|
it 'can be run on dummy' do
|
7
|
-
expect(GitWakaTime::
|
11
|
+
expect(GitWakaTime::Commit.all.size).to eq 8 # 9ths is lonely
|
8
12
|
end
|
9
13
|
it 'can be run on dummy' do
|
10
|
-
expect(GitWakaTime::
|
14
|
+
expect(GitWakaTime::Commit.order(:date).first.message).to eq 'created readme'
|
11
15
|
end
|
12
16
|
|
13
17
|
it 'maps files dependent commits' do
|
14
|
-
expect(GitWakaTime::
|
18
|
+
expect(GitWakaTime::Commit.all.first.commited_files.first.dependent_sha).to eq 'dcd748bd06b8a0f239d779bee4f1eaf1f4aa500d'
|
15
19
|
end
|
16
20
|
|
17
21
|
it 'maps files dependent commits' do
|
18
|
-
expect(GitWakaTime::
|
22
|
+
expect(GitWakaTime::Commit.all.select { |c| c.sha == 'dcd748bd06b8a0f239d779bee4f1eaf1f4aa500d' }.first.commited_files.first.dependent_sha).to eq '2254dd56976b5f32a2289438842e42a35a18ff86'
|
19
23
|
end
|
20
24
|
end
|
data/spec/query_spec.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'gitwakatime'
|
3
2
|
|
4
3
|
describe 'description' do
|
5
|
-
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
GitWakaTime.config.git = Git.open(@wdir)
|
7
|
+
GitWakaTime::Mapper.new(start_at: Date.new(2015, 1, 24))
|
8
|
+
end
|
6
9
|
|
7
10
|
before do
|
8
11
|
stub_request(:get, 'https://wakatime.com/api/v1/actions')
|
@@ -12,11 +15,18 @@ describe 'description' do
|
|
12
15
|
|
13
16
|
it 'can be run on dummy' do
|
14
17
|
|
15
|
-
|
18
|
+
actions = GitWakaTime::Query.new(GitWakaTime::Commit.all, File.basename(@wdir)).get
|
19
|
+
|
20
|
+
expect(actions).to be_a Array
|
21
|
+
expect(actions.size).to eq 6 # 9ths is lonely
|
22
|
+
expect(actions.last).to be_a GitWakaTime::Action
|
23
|
+
expect(actions.last.branch).to eq 'master'
|
24
|
+
end
|
25
|
+
it 'produces valid search for api' do
|
26
|
+
actions = GitWakaTime::Query.new(GitWakaTime::Commit.all, File.basename(@wdir)).time_params
|
16
27
|
|
17
|
-
expect(
|
18
|
-
expect(
|
19
|
-
expect(
|
20
|
-
expect(timer.last.branch).to eq 'master'
|
28
|
+
expect(actions).to be_a Array
|
29
|
+
expect(actions.first[:start].to_date).to eq Date.new(2015, 01, 30)
|
30
|
+
expect(actions.first[:end].to_date).to eq Date.new(2015, 02, 14)
|
21
31
|
end
|
22
32
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,12 +4,29 @@
|
|
4
4
|
# loaded once.
|
5
5
|
#
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
ENV['thor_env'] = 'test'
|
8
|
+
ENV['waka_log'] = 'false'
|
9
|
+
|
10
|
+
require 'gitwakatime'
|
11
|
+
|
7
12
|
RSpec.configure do |config|
|
8
13
|
config.run_all_when_everything_filtered = true
|
9
14
|
config.filter_run :focus
|
10
15
|
config.order = 'random'
|
11
16
|
config.before(:all) do
|
12
17
|
@wdir = set_file_paths
|
18
|
+
GitWakaTime.config.setup_local_db
|
19
|
+
GitWakaTime::Commit.new.columns
|
20
|
+
GitWakaTime::CommitedFile.new.columns
|
21
|
+
GitWakaTime::Action.new.columns
|
22
|
+
end
|
23
|
+
|
24
|
+
config.before(:each) do
|
25
|
+
GitWakaTime::Commit.truncate
|
26
|
+
GitWakaTime::CommitedFile.truncate
|
27
|
+
GitWakaTime::Action.truncate
|
28
|
+
|
29
|
+
expect(GitWakaTime.config).to receive('user_name').and_return('Russell Osborne').at_least(:once)
|
13
30
|
end
|
14
31
|
|
15
32
|
config.after(:all) do
|
@@ -37,6 +54,5 @@ def create_temp_repo(clone_path)
|
|
37
54
|
Dir.chdir(tmp_path) do
|
38
55
|
FileUtils.mv('dot_git', '.git')
|
39
56
|
end
|
40
|
-
puts tmp_path
|
41
57
|
tmp_path
|
42
58
|
end
|
data/spec/timer_spec.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'gitwakatime'
|
3
2
|
|
4
3
|
describe 'description' do
|
5
|
-
let (:
|
4
|
+
let (:git) { Git.open(@wdir) }
|
6
5
|
|
7
6
|
before do
|
8
7
|
stub_request(:get, 'https://wakatime.com/api/v1/actions')
|
@@ -11,8 +10,14 @@ describe 'description' do
|
|
11
10
|
end
|
12
11
|
|
13
12
|
it 'can be run on dummy' do
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
GitWakaTime.config.git = git
|
14
|
+
GitWakaTime::Mapper.new(start_at: Date.new(2015, 1, 24))
|
15
|
+
actions = GitWakaTime::Query.new(GitWakaTime::Commit.all, File.basename(@wdir)).get
|
16
|
+
timer = GitWakaTime::Timer.new(GitWakaTime::Commit.all, actions, File.basename(@wdir)).process
|
17
|
+
|
18
|
+
# # UTC breaks actions of 1 day
|
19
|
+
# expect(timer.size).to eq 1
|
20
|
+
# With 8 relevant commits
|
21
|
+
expect(timer[timer.keys.first].size).to eq 8
|
17
22
|
end
|
18
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitwakatime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Russell Osborne
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git
|
@@ -108,6 +108,34 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sequel
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sqlite3
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
111
139
|
- !ruby/object:Gem::Dependency
|
112
140
|
name: bundler
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,15 +211,18 @@ files:
|
|
183
211
|
- bin/gitwakatime
|
184
212
|
- gitwakatime.gemspec
|
185
213
|
- lib/gitwakatime.rb
|
186
|
-
- lib/gitwakatime/
|
214
|
+
- lib/gitwakatime/action.rb
|
187
215
|
- lib/gitwakatime/cli.rb
|
188
216
|
- lib/gitwakatime/commit.rb
|
189
217
|
- lib/gitwakatime/commited_file.rb
|
218
|
+
- lib/gitwakatime/durations.rb
|
190
219
|
- lib/gitwakatime/log.rb
|
191
220
|
- lib/gitwakatime/mapper.rb
|
192
221
|
- lib/gitwakatime/query.rb
|
193
222
|
- lib/gitwakatime/timer.rb
|
194
223
|
- lib/gitwakatime/version.rb
|
224
|
+
- spec/commit_spec.rb
|
225
|
+
- spec/commited_file_spec.rb
|
195
226
|
- spec/dummy/dot_git/FETCH_HEAD
|
196
227
|
- spec/dummy/dot_git/HEAD
|
197
228
|
- spec/dummy/dot_git/config
|
@@ -271,6 +302,8 @@ specification_version: 4
|
|
271
302
|
summary: A Tool that will compile git data with wakatime data to establish time per
|
272
303
|
commit
|
273
304
|
test_files:
|
305
|
+
- spec/commit_spec.rb
|
306
|
+
- spec/commited_file_spec.rb
|
274
307
|
- spec/dummy/dot_git/FETCH_HEAD
|
275
308
|
- spec/dummy/dot_git/HEAD
|
276
309
|
- spec/dummy/dot_git/config
|
data/lib/gitwakatime/actions.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
module GitWakaTime
|
2
|
-
# Extract Duration Data from Actions for the WAKATIME API
|
3
|
-
class Actions
|
4
|
-
attr_accessor :actions
|
5
|
-
def initialize(args)
|
6
|
-
return @actions = args[:actions] if args[:actions]
|
7
|
-
fail if args[:project].nil?
|
8
|
-
@project = args[:project]
|
9
|
-
@args = args
|
10
|
-
@session = Wakatime::Session.new(api_key: GitWakaTime.config.api_key)
|
11
|
-
@client = Wakatime::Client.new(@session)
|
12
|
-
load_actions
|
13
|
-
end
|
14
|
-
|
15
|
-
def load_actions
|
16
|
-
Log.new "querying WakaTime actions for #{@project}"
|
17
|
-
time = Benchmark.realtime do
|
18
|
-
@actions = @client.actions(@args)
|
19
|
-
# remove returned actions that do not have the project we want
|
20
|
-
@actions = @actions.keep_if do |a|
|
21
|
-
a['project'] == @project
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
Log.new "API took #{time}s"
|
26
|
-
@actions
|
27
|
-
end
|
28
|
-
|
29
|
-
def actions_to_durations(_project = nil, timeout = 15)
|
30
|
-
durations = []
|
31
|
-
current = []
|
32
|
-
@actions.each do | action |
|
33
|
-
# the first action just sets state and does nothing
|
34
|
-
unless current.empty?
|
35
|
-
|
36
|
-
# get duration since last action
|
37
|
-
duration = action.time.round - current['time'].round
|
38
|
-
|
39
|
-
duration = 0.0 if duration < 0
|
40
|
-
|
41
|
-
# duration not logged if greater than the timeout
|
42
|
-
if duration < timeout * 60
|
43
|
-
|
44
|
-
# add duration to current action
|
45
|
-
current['duration'] = duration
|
46
|
-
|
47
|
-
# log current action as a duration
|
48
|
-
durations << current
|
49
|
-
end
|
50
|
-
end
|
51
|
-
# set state (re-start the clock)
|
52
|
-
current = action
|
53
|
-
current.delete('id')
|
54
|
-
|
55
|
-
end
|
56
|
-
durations
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|