trak 0.0.3 → 0.0.4

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.
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