trak 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/CHANGELOG.md +15 -0
  2. data/README.md +8 -34
  3. data/bin/trak +178 -0
  4. data/lib/trak.rb +78 -147
  5. metadata +2 -3
  6. data/lib/trak/trak.rb +0 -92
@@ -1,5 +1,20 @@
1
1
  # Change log
2
2
 
3
+ ## v0.0.4 (2012-05-01)
4
+
5
+ Changed:
6
+
7
+ * (ca1da2d): Use TRAK_DEBUG environment var instead of --debug
8
+ * (d00089a): Moved todos into github issue tracker
9
+ * (5cb50db): Fix for report format
10
+
11
+ Closed:
12
+
13
+ * (a6669ca): #11 - Refactor command-line specific code into bin/trak +
14
+ refactor Trak module
15
+ * (f5fd613): Add -h|--help switch. Fixes #6
16
+ * (a113869): Added version info. Fixes #7
17
+
3
18
  ## v0.0.3 (2012-04-30)
4
19
 
5
20
  Changed:
data/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Trak: track chunks of time from the command line
2
2
 
3
- Trak, v0.0.3 (Apr 30, 2012)
3
+ Trak, v0.0.4 (May 1, 2012)
4
4
  Written by Adam Sharp
5
5
 
6
6
  ## Notice
7
7
 
8
- Trak was recently (i.e., last week) a Perl script. It has been ported to
9
- Ruby, but the code really looks like it's taken a beating and is
10
- definitely NOT what I want it to ultimately look like. Much more
11
- ruby-fying to happen yet, as well as support for the excellent
8
+ Trak was recently a Perl script. It has been ported to Ruby, but the
9
+ code really looks like it's taken a beating and is definitely NOT what I
10
+ want it to ultimately look like. Much more ruby-fying to happen yet, as
11
+ well as support for the excellent
12
12
  [Chronic](https://github.com/mojombo/chronic) gem for natural language
13
13
  date parsing in the pipeline.
14
14
 
@@ -19,8 +19,8 @@ Stay tuned.
19
19
 
20
20
  ## Description
21
21
 
22
- Trak is a utility for Mac OS X that allows you to quickly make a record
23
- of how much time you've spent on various tasks throughout the day.
22
+ Trak is a utility that allows you to quickly make a record of how much
23
+ time you've spent on various tasks throughout the day.
24
24
 
25
25
  Work logs are stored in `/Users/yourusername/Documents/Tracker/` with
26
26
  the format `YEAR-MONTH-DAY-time-log.txt`.
@@ -62,7 +62,7 @@ Where:
62
62
  You can use either
63
63
 
64
64
  $ trak 30 "Foo bar"
65
-
65
+
66
66
  or
67
67
 
68
68
  $ trak 30 Foo bar
@@ -78,29 +78,3 @@ These are all valid commands:
78
78
  $ trak 30min Rewrite trak documentation # => 30 minutes
79
79
  $ trak 4hours Refactor trak # => 4 hours
80
80
  $ trak 15 Lunch # => 15 minutes
81
-
82
- ## To do
83
-
84
- * Proper handling of incorrectly formatted dates
85
- * Write a report that gives a weekly breakdown given a date in that week
86
- * Add a `-h` usage/help switch
87
- * Make the personal time search more configurable by putting keywords to
88
- search in an array
89
- * Have the different types of reports, and keywords for those reports,
90
- completely stored in a configuration file. The last category in the
91
- file is the default report. Because keywords for custom reports would
92
- work on a whitelist system, everything that doesn't match goes into
93
- the default. For example:
94
-
95
- # Personal
96
- lunch
97
- uni
98
- news
99
- # Default
100
- Work
101
-
102
- * Have the keywords in the configuration file actually be regexes. When
103
- reading the config file, any empty lines or whitespace are ignored.
104
- * Give an estimate of completion time, with a configurable default for
105
- the length of the work day. Also take into account the default length
106
- of lunch break (configurable) if lunch hasn't yet been logged.
data/bin/trak CHANGED
@@ -1,2 +1,180 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'trollop'
2
3
  require 'trak'
4
+ require 'trak/exit'
5
+ require 'trak/core_ext/time'
6
+
7
+ # place where data is stored
8
+ datadir = "#{ENV['HOME']}/Documents/Tracker/"
9
+ %x[mkdir -p #{datadir}]
10
+
11
+ # define command line options
12
+ opts = Trollop::options do
13
+ version "trak version 0.0.4"
14
+ banner <<-BANNER
15
+ Trak: log chunks of time from the command line
16
+ Usage:
17
+ trak [-d|--date] <time> <message> # tracking mode
18
+ trak [-d|--date] [-r|--report] # reporting mode
19
+ trak [-d|--date] -e|--edit # opens time log in EDITOR
20
+
21
+ Tracking time:
22
+ The <time> argument is the amount of time spent on the task, in
23
+ minutes. Append "h" for hours, e.g., 1.5h. Examples:
24
+
25
+ $ trak 15 email # minimum 15 minutes
26
+ $ trak 25 lunch # rounded to nearest 15m, i.e. 30
27
+ $ trak 1.5h bug 2345 # everything after <time> is the message
28
+
29
+ Reporting:
30
+ The -r|--report option is optional, just typing `trak` with no
31
+ arguments will cause Trak to print a report for today. Use -d|--date
32
+ to print the report for another day.
33
+
34
+ Dates:
35
+ Trak currently accepts dates in the format YYYY-MM-DD. All modes
36
+ accept the date argument (defaults to today).
37
+
38
+ See http://github.com/sharplet/trak for more information.
39
+
40
+ Options:
41
+ BANNER
42
+ opt :date, "Specify the date of the log to work with",
43
+ :type => String, :short => "-d"
44
+ opt :report, "Print a report for the specified date", :short => "-l"
45
+ opt :edit, "Open the log for the specified date in EDITOR"
46
+ end
47
+
48
+ # all valid options have been processed, so figure out which mode
49
+ # we're in...
50
+ #
51
+ # if we found a -r or -l option, ignore everything else
52
+ Trak::breakpoint
53
+ if opts[:report]
54
+ MODE = 'report'
55
+ # now check if the user wants edit mode
56
+ elsif opts[:edit]
57
+ MODE = 'edit'
58
+ # if there are still unprocessed args (that didn't look like switches),
59
+ # we're in insert mode
60
+ elsif ARGV.length > 0
61
+ MODE = 'insert'
62
+ # if all else fails, there were probably no args to begin with, so we're
63
+ # in report mode
64
+ else
65
+ MODE = 'report'
66
+ end
67
+
68
+ today = Time.now.strftime '%F'
69
+
70
+ # did the user supply a date argument that isn't today?
71
+ if opts[:date] && opts[:date] != today
72
+ fdate = opts[:date]
73
+ # otherwise use today's date, formatted, and set date_arg to be false
74
+ else
75
+ fdate = today
76
+ opts[:date] = nil
77
+ end
78
+
79
+ # set the output file name
80
+ filename = "#{datadir}#{fdate}-time-log.txt"
81
+
82
+ if MODE == 'report'
83
+ if File.exist? filename
84
+ # open the file and get it as an array
85
+ begin
86
+ file = File.open(filename).readlines.map &:chomp
87
+ rescue
88
+ Exit::exit_err "#{__FILE__}: #{$!}"
89
+ end
90
+
91
+ # The keys for each hash are the titles of the various tasks logged.
92
+ # The values are the total time spent on the task.
93
+ work = {}
94
+ personal = {}
95
+
96
+ # find the start time for the day we're reporting on
97
+ startTime = file.first.split[1]
98
+
99
+ # process each line of the file
100
+ file[1..file.size].each do |line|
101
+ minutes, text = line.split(': ')
102
+ unless text =~ /personal|uni|lunch|home/
103
+ work[text] = 0 unless work.include? text
104
+ work[text] += minutes.to_i
105
+ else
106
+ personal[text] = 0 unless personal.include? text
107
+ personal[text] += minutes.to_i
108
+ end
109
+ end
110
+
111
+ # print the report
112
+ if opts[:date]
113
+ puts "# Logged work for #{fdate}"
114
+ else
115
+ puts "# Today's logged work"
116
+ end
117
+
118
+ workTotal = Trak::printSubReport(work, "Work")
119
+ personalTotal = Trak::printSubReport(personal, "Personal")
120
+
121
+ newTimeString = Trak::to12HourTime(Trak::newTimeWithMinutes(startTime, workTotal + personalTotal))
122
+ print "Hours logged until #{newTimeString} (since #{Trak::to12HourTime(startTime)}). "
123
+
124
+ # if we're reporting for today, print the current time
125
+ puts (!opts[:date]) ? "Currently #{Trak::to12HourTime(Time.now.strftime(Trak::TIME_FORMAT_24HOUR))}." : nil
126
+ else
127
+ if opts[:date]
128
+ STDERR.puts "No time log for #{fdate}. Track some time first."
129
+ else
130
+ STDERR.puts "No time log for today. Track some time first.\n"
131
+ end
132
+ end
133
+
134
+ elsif MODE == 'edit'
135
+ if File.exist? filename
136
+ if ENV['EDITOR']
137
+ exec "#{ENV['EDITOR']} #{filename}"
138
+ else
139
+ exec "open", filename
140
+ end
141
+ exit
142
+ else
143
+ Exit::exit_err "#{__FILE__}: #{filename} does not exist or unable to open."
144
+ end
145
+
146
+ elsif MODE == 'insert'
147
+ if opts[:date]
148
+ puts "WARNING: Adding time to a day other than today is not recommended."
149
+ print "Continue? (y/n) "
150
+ input = STDIN.readline.chomp
151
+ unless input =~ /^y(es)?/i
152
+ Exit::exit_err "Timelog update cancelled."
153
+ end
154
+ end
155
+
156
+ # process arguments
157
+ Trak::breakpoint
158
+ minutes = Trak::processTimeArgument ARGV.shift
159
+ message = ARGV.join(" ")
160
+
161
+ # open the output file
162
+ first_time = !File.exist?(filename)
163
+ Trak::breakpoint
164
+ begin
165
+ File.open filename, 'a', :autoclose => true do |file|
166
+ if first_time
167
+ Trak::breakpoint
168
+ currentTimeInMinutes = Time.now.to_minutes
169
+ startTime = Trak::minutesToTime((currentTimeInMinutes - minutes).round_to_nearest 15)
170
+ file.puts "#{fdate} #{startTime}"
171
+ end
172
+ file.puts "#{minutes}: #{message}"
173
+ end
174
+ rescue
175
+ Exit::exit_err "Couldn't open #{filename}: #{$!}"
176
+ end
177
+
178
+ else
179
+ Exit::exit_err "Couldn't determine the correct mode (I was given '#{MODE}'): #{$!}"
180
+ end
@@ -1,166 +1,97 @@
1
- #!/usr/bin/env ruby
1
+ module Trak
2
+ require 'trak/core_ext/blank'
3
+ require 'trak/core_ext/round_to_nearest'
4
+ require 'debugger'
2
5
 
3
- # command:
4
- # $ trak 30 work on trak
5
- # $ trak 1h work really hard on trak
6
- #
7
- # format:
8
- # - First arg is how much time spent
9
- # - Second arg is a description
6
+ TIME_FORMAT_12HOUR = "%l:%M %p"
7
+ TIME_FORMAT_24HOUR = "%k:%M"
10
8
 
11
- require 'trollop'
12
- require 'debugger'
13
-
14
- require 'trak/trak'
15
- require 'trak/exit'
16
- require 'trak/core_ext/time'
17
-
18
- # place where data is stored
19
- datadir = "#{ENV['HOME']}/Documents/Tracker/"
20
- %x[mkdir -p #{datadir}]
21
-
22
- # define command line options
23
- opts = Trollop::options do
24
- opt :report, "Reporting mode", :short => "-l"
25
- opt :edit, "Edit mode"
26
- opt :date, "The date", :type => String, :short => "-d"
27
- opt :debug, "Debugging mode", :short => "-i"
28
- end
29
-
30
- $g_opts = opts
31
- def debug(steps = 1)
32
- debugger(steps) if $g_opts[:debug]
33
- end
34
-
35
- # all valid options have been processed, so figure out which mode
36
- # we're in...
37
- #
38
- # if we found a -r or -l option, ignore everything else
39
- if opts[:report]
40
- MODE = 'report'
41
- # now check if the user wants edit mode
42
- elsif opts[:edit]
43
- MODE = 'edit'
44
- # if there are still unprocessed args (that didn't look like switches),
45
- # we're in insert mode
46
- elsif ARGV.length > 0
47
- MODE = 'insert'
48
- # if all else fails, there were probably no args to begin with, so we're
49
- # in report mode
50
- else
51
- MODE = 'report'
52
- end
53
-
54
- today = Time.now.strftime '%F'
9
+ def self.breakpoint(steps = 1)
10
+ debugger(steps) if ENV['TRAK_DEBUG'] == "1"
11
+ end
55
12
 
56
- # did the user supply a date argument that isn't today?
57
- if opts[:date] && opts[:date] != today
58
- fdate = opts[:date]
59
- # otherwise use today's date, formatted, and set date_arg to be false
60
- else
61
- fdate = today
62
- opts[:date] = nil
63
- end
13
+ # expects a hash of tasks mapped to time spent, and a sub-report name
14
+ # (e.g., work, personal)
15
+ # prints a formatted sub-report
16
+ # returns the total hours worked
17
+ def self.printSubReport(report_hash, report_title)
18
+ total = 0
19
+ unless report_hash.empty?
20
+ count = 0
21
+ report_out = ""
22
+ report_hash.each do |title, minutes|
23
+ total += minutes.to_i
24
+ count += 1
25
+ report_out += "=> #{timeString(minutes)}: #{title}"
26
+ report_out += "\n" unless count == report_hash.size
27
+ end
28
+ puts "# #{report_title} time (#{timeString(total)})"
29
+ puts report_out
30
+ end
31
+ total
32
+ end
64
33
 
65
- # set the output file name
66
- filename = "#{datadir}#{fdate}-time-log.txt"
34
+ def self.time_with_hours_minutes(*hm)
35
+ dmy = Time.now.to_a[3..5].reverse
36
+ Time.new(*dmy, *hm)
37
+ end
67
38
 
68
- if MODE == 'report'
69
- if File.exist? filename
70
- # open the file and get it as an array
71
- begin
72
- file = File.open(filename).readlines.map &:chomp
73
- rescue
74
- Exit::exit_err "#{__FILE__}: #{$!}"
75
- end
76
-
77
- # The keys for each hash are the titles of the various tasks logged.
78
- # The values are the total time spent on the task.
79
- work = {}
80
- personal = {}
81
-
82
- # find the start time for the day we're reporting on
83
- startTime = file.first.split[1]
84
-
85
- # process each line of the file
86
- file[1..file.size].each do |line|
87
- minutes, text = line.split(': ')
88
- unless text =~ /personal|uni|lunch|home/
89
- work[text] = 0 unless work.include? text
90
- work[text] += minutes.to_i
91
- else
92
- personal[text] = 0 unless personal.include? text
93
- personal[text] += minutes.to_i
39
+ # expects a number of minutes
40
+ # if less than 60 returns the number with an "m"
41
+ # otherwise converts to hours and adds an "h"
42
+ def self.timeString(minutes)
43
+ if minutes >= 60
44
+ hours = minutes/60.0
45
+ if hours % 1 == 0
46
+ hours = hours.to_i
94
47
  end
95
- end
96
-
97
- # print the report
98
- if opts[:date]
99
- puts "# Logged work for #{fdate}"
48
+ "#{hours}h"
100
49
  else
101
- puts "# Today's logged work"
102
- end
103
-
104
- workTotal = Trak::printSubReport(work, "Work")
105
- personalTotal = Trak::printSubReport(personal, "Personal")
106
-
107
- newTimeString = Trak::to12HourTime(Trak::newTimeWithMinutes(startTime, workTotal + personalTotal))
108
- puts "Hours logged until #{newTimeString} (since #{Trak::to12HourTime(startTime)}). "
109
-
110
- # if we're reporting for today, print the current time
111
- puts "Currently #{Trak::to12HourTime(Time.now.strftime(Trak::TIME_FORMAT_24HOUR))}." unless opts[:date]
112
- else
113
- if opts[:date]
114
- STDERR.puts "No time log for #{fdate}. Track some time first."
115
- else
116
- STDERR.puts "No time log for today. Track some time first.\n"
50
+ "#{minutes}m"
117
51
  end
118
52
  end
119
53
 
120
- elsif MODE == 'edit'
121
- if File.exist? filename
122
- if ENV['EDITOR']
123
- exec "#{ENV['EDITOR']} #{filename}"
124
- else
125
- exec "open", filename
126
- end
127
- exit
128
- else
129
- Exit::exit_err "#{__FILE__}: #{filename} does not exist or unable to open."
54
+ def self.newTimeWithMinutes(start_time, minutes)
55
+ hm = start_time.split ':'
56
+ Time.at(time_with_hours_minutes(*hm).to_i + minutes.to_i*60).strftime(TIME_FORMAT_24HOUR).strip
130
57
  end
131
58
 
132
- elsif MODE == 'insert'
133
- if opts[:date]
134
- puts "WARNING: Adding time to a day other than today is not recommended."
135
- print "Continue? (y/n) "
136
- input = STDIN.readline.chomp
137
- unless input =~ /^y(es)?/i
138
- Exit::exit_err "Timelog update cancelled."
59
+ def self.to12HourTime(time)
60
+ unless time.blank?
61
+ hm = time.split ':'
62
+ time_with_hours_minutes(*hm).strftime(TIME_FORMAT_12HOUR).strip
139
63
  end
140
64
  end
141
-
142
- # process arguments
143
- debug
144
- minutes = Trak::processTimeArgument ARGV.shift
145
- message = ARGV.join(" ")
146
-
147
- # open the output file
148
- first_time = !File.exist?(filename)
149
- # debug
150
- begin
151
- File.open filename, 'a', :autoclose => true do |file|
152
- if first_time
153
- debug
154
- currentTimeInMinutes = Time.now.to_minutes
155
- startTime = Trak::minutesToTime((currentTimeInMinutes - minutes).round_to_nearest 15)
156
- file.puts "#{fdate} #{startTime}"
65
+
66
+ # expects a single argument - the time argument in the format ##m or ##h
67
+ # if argument has no m/h qualifier, assume m
68
+ # returns a number of minutes
69
+ def self.processTimeArgument(time_string)
70
+ if time_string =~ /^(\d*\.?\d+)((m|min|minute|minutes)|(h|hr|hour|hours))?$/i
71
+ time = $1.to_f
72
+ modifier = $2
73
+ minutes = (modifier =~ /h.*/) ? time * 60 : time
74
+
75
+ # check enough time has been logged
76
+ if minutes < 15
77
+ STDERR.puts "You must log at least 15 minutes."
78
+ exit 1
157
79
  end
158
- file.puts "#{minutes}: #{message}"
80
+
81
+ minutes.round_to_nearest 15
82
+ else
83
+ STDERR.puts "Incorrectly formatted argument."
84
+ exit 1
159
85
  end
160
- rescue
161
- Exit::exit_err "Couldn't open #{filename}: #{$!}"
162
86
  end
163
87
 
164
- else
165
- Exit::exit_err "Couldn't determine the correct mode (I was given '#{MODE}'): #{$!}"
88
+ # expects an integer
89
+ def self.minutesToTime(minutes)
90
+ time_with_hours_minutes(minutes / 60, minutes % 60).strftime(TIME_FORMAT_24HOUR).strip
91
+ end
92
+
93
+ # expects an integer which is the amount of minutes logged
94
+ def self.startTimeInMinutes(minutes)
95
+ Time.now.to_minutes.round_to_nearest(15) - minutes
96
+ end
166
97
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trak
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-30 00:00:00.000000000 Z
12
+ date: 2012-05-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
@@ -53,7 +53,6 @@ files:
53
53
  - lib/trak/core_ext/round_to_nearest.rb
54
54
  - lib/trak/core_ext/time.rb
55
55
  - lib/trak/exit.rb
56
- - lib/trak/trak.rb
57
56
  - lib/trak.rb
58
57
  - bin/trak
59
58
  - CHANGELOG.md
@@ -1,92 +0,0 @@
1
- require "trak/core_ext/blank"
2
- require "trak/core_ext/round_to_nearest"
3
-
4
- module Trak
5
- TIME_FORMAT_12HOUR = "%l:%M %p"
6
- TIME_FORMAT_24HOUR = "%k:%M"
7
-
8
- # expects a hash of tasks mapped to time spent, and a sub-report name
9
- # (e.g., work, personal)
10
- # prints a formatted sub-report
11
- # returns the total hours worked
12
- def self.printSubReport(report_hash, report_title)
13
- total = 0
14
- unless report_hash.empty?
15
- count = 0
16
- report_out = ""
17
- report_hash.each do |title, minutes|
18
- total += minutes.to_i
19
- count += 1
20
- report_out += "=> #{timeString(minutes)}: #{title}"
21
- report_out += "\n" unless count == report_hash.size
22
- end
23
- puts "# #{report_title} time (#{timeString(total)})"
24
- puts report_out
25
- end
26
- total
27
- end
28
-
29
- def self.time_with_hours_minutes(*hm)
30
- dmy = Time.now.to_a[3..5].reverse
31
- Time.new(*dmy, *hm)
32
- end
33
-
34
- # expects a number of minutes
35
- # if less than 60 returns the number with an "m"
36
- # otherwise converts to hours and adds an "h"
37
- def self.timeString(minutes)
38
- if minutes >= 60
39
- hours = minutes/60.0
40
- if hours % 1 == 0
41
- hours = hours.to_i
42
- end
43
- "#{hours}h"
44
- else
45
- "#{minutes}m"
46
- end
47
- end
48
-
49
- def self.newTimeWithMinutes(start_time, minutes)
50
- hm = start_time.split ':'
51
- Time.at(time_with_hours_minutes(*hm).to_i + minutes.to_i*60).strftime(TIME_FORMAT_24HOUR).strip
52
- end
53
-
54
- def self.to12HourTime(time)
55
- unless time.blank?
56
- hm = time.split ':'
57
- time_with_hours_minutes(*hm).strftime(TIME_FORMAT_12HOUR).strip
58
- end
59
- end
60
-
61
- # expects a single argument - the time argument in the format ##m or ##h
62
- # if argument has no m/h qualifier, assume m
63
- # returns a number of minutes
64
- def self.processTimeArgument(time_string)
65
- if time_string =~ /^(\d*\.?\d+)((m|min|minute|minutes)|(h|hr|hour|hours))?$/i
66
- time = $1.to_f
67
- modifier = $2
68
- minutes = (modifier =~ /h.*/) ? time * 60 : time
69
-
70
- # check enough time has been logged
71
- if minutes < 15
72
- STDERR.puts "You must log at least 15 minutes."
73
- exit 1
74
- end
75
-
76
- minutes.round_to_nearest 15
77
- else
78
- STDERR.puts "Incorrectly formatted argument."
79
- exit 1
80
- end
81
- end
82
-
83
- # expects an integer
84
- def self.minutesToTime(minutes)
85
- time_with_hours_minutes(minutes / 60, minutes % 60).strftime(TIME_FORMAT_24HOUR).strip
86
- end
87
-
88
- # expects an integer which is the amount of minutes logged
89
- def self.startTimeInMinutes(minutes)
90
- Time.now.to_minutes.round_to_nearest(15) - minutes
91
- end
92
- end