trak 0.0.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.
@@ -0,0 +1,90 @@
1
+ # Change log
2
+
3
+ ## v0.0.0 (2012-04-25)
4
+
5
+ Tracker is now known as trak, and it's a RubyGem!
6
+
7
+ Changed:
8
+
9
+ * Trak has now been ported from Perl to Ruby. The main reason for this
10
+ was for the ability to support the awesome gem Chronic for natural
11
+ language date parsing in an upcoming release. But beyond that,
12
+ RubyGems is fantastic for packaging and distribution and the plan is
13
+ to make this code seriously more modular over the next little while
14
+ (the code is a joke at the moment :O).
15
+
16
+ ## v0.4 (2011-10-17)
17
+
18
+ Added:
19
+
20
+ * If the EDITOR environment variable is set, this is now used by default
21
+ to edit a log file with `track -e`.
22
+
23
+ * The current time is displayed at the end of the report, like so:
24
+
25
+ # Today's logged work
26
+ ...
27
+ Hours logged until 9:30 AM (since 9:15 AM). Currently 10:13 AM.
28
+
29
+ ## v0.3
30
+
31
+ Added:
32
+
33
+ * Report now calculates the total time spent on each task throughout the
34
+ day
35
+ * Time can now be logged as either minutes or hours. The time argument
36
+ can take the format `##<denom>`, where `##` is the amount of time
37
+ spent, and `<denom>` signifies hours (`h/hr/hour/hours`) or minutes
38
+ (`m/min/minute/minutes`).
39
+
40
+ Changed:
41
+
42
+ * Report formatting improvements
43
+
44
+ Fixed:
45
+
46
+ * Time wrap-around bug where if time logged passed midnight AM/PM would
47
+ display incorrectly.
48
+
49
+ ## v0.2.1
50
+
51
+ Changed:
52
+
53
+ * Change log is now found in CHANGELOG.md rather than README.md.
54
+
55
+ Fixed:
56
+
57
+ * Fixed a bug where invoking with no arguments would create an entry in
58
+ the log with no time or message. No arguments is a synonym for `-l` or
59
+ `-r`.
60
+
61
+ ## v0.2
62
+
63
+ Added:
64
+
65
+ * Tracker now automatically calculates the time you started your day
66
+ based on your first log for that day. This is stored in the time log
67
+ after the date, and used to calculate how far into the day you have
68
+ tracked time.
69
+
70
+ Changed:
71
+
72
+ * Everything after the first argument (the amount of time) is now
73
+ considered to be the task name (i.e., the task is no longer truncated
74
+ to the first word).
75
+ * Formatting improvements
76
+
77
+ ## v0.1
78
+
79
+ Added:
80
+
81
+ * Tracker.pl -l will now tell you what time you've logged time until.
82
+ For example, if your start time is 8:00 AM and you've logged 3 hours,
83
+ it will output the following:
84
+
85
+ Hours logged until 11:00 AM (since 8:00 AM).
86
+
87
+ Changed:
88
+
89
+ * `-l` and `-r` switches are now synonyms
90
+ * Formatting improvements
@@ -0,0 +1,107 @@
1
+ # Trak: track chunks of time from the command line
2
+
3
+ Trak, v0.0.0 (Apr 25, 2012)
4
+ Written by Adam Sharp
5
+
6
+ ## Notice
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
12
+ [Chronic](https://github.com/mojombo/chronic) gem for natural language
13
+ date parsing in the pipeline.
14
+
15
+ It's now structured as a RubyGem and should hopefully be available on
16
+ RubyGems soon.
17
+
18
+ Stay tuned.
19
+
20
+ ## Description
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.
24
+
25
+ Work logs are stored in `/Users/yourusername/Documents/Tracker/` with
26
+ the format `YEAR-MONTH-DAY-time-log.txt`.
27
+
28
+ An example work log that trak will create:
29
+
30
+ 2011-09-01 9:00
31
+ 30: nap
32
+ 45: procrastinate
33
+ 30: uni
34
+ 120: trak
35
+
36
+ ## Installation
37
+
38
+ Check out this repository and then create a symlink to bin/trak
39
+ somewhere in your path:
40
+
41
+ $ ln -s <repo>/bin/trak /usr/bin/trak
42
+
43
+ ## Usage
44
+
45
+ trak [-d|--date DATE] ##<denom> <description> # => data entry
46
+ trak [-d|--date DATE] [-r|-l] # => reporting
47
+ trak [-d|--date DATE] -e # => manually edit time log
48
+
49
+ Where:
50
+
51
+ * `##` is a decimal signifying how much time has been spent.
52
+ * `<denom>` is either hours (`h/hr/hour/hours`) or minutes
53
+ (`m/min/minute/minutes`). `<denom>` is optional and if ommitted,
54
+ Tracker will interpret the time entered as minutes.
55
+ * `<description>` is a string containing a brief description of the
56
+ activity.
57
+ * `DATE` is a string of the format `YYYY-MM-DD` which represents any
58
+ date. This effects any of Tracker's modes, i.e., insertion, editing or
59
+ reporting.
60
+
61
+ ### Descriptions
62
+
63
+ You can use either
64
+
65
+ $ trak 30 "Foo bar"
66
+
67
+ or
68
+
69
+ $ trak 30 Foo bar
70
+
71
+ as everything after the first argument is considered the name of the
72
+ task.
73
+
74
+ ### Entering time
75
+
76
+ These are all valid commands:
77
+
78
+ $ trak 1h Write trak documentation # => 1 hour
79
+ $ trak 30min Rewrite trak documentation # => 30 minutes
80
+ $ trak 4hours Refactor trak # => 4 hours
81
+ $ trak 15 Lunch # => 15 minutes
82
+
83
+ ## To do
84
+
85
+ * Proper handling of incorrectly formatted dates
86
+ * Write a report that gives a weekly breakdown given a date in that week
87
+ * Add a `-h` usage/help switch
88
+ * Make the personal time search more configurable by putting keywords to
89
+ search in an array
90
+ * Have the different types of reports, and keywords for those reports,
91
+ completely stored in a configuration file. The last category in the
92
+ file is the default report. Because keywords for custom reports would
93
+ work on a whitelist system, everything that doesn't match goes into
94
+ the default. For example:
95
+
96
+ # Personal
97
+ lunch
98
+ uni
99
+ news
100
+ # Default
101
+ Work
102
+
103
+ * Have the keywords in the configuration file actually be regexes. When
104
+ reading the config file, any empty lines or whitespace are ignored.
105
+ * Give an estimate of completion time, with a configurable default for
106
+ the length of the work day. Also take into account the default length
107
+ of lunch break (configurable) if lunch hasn't yet been logged.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trak'
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # command:
4
+ # $ Tracker.pl 30 "I did stuff"
5
+ # $ Tracker.pl 1h "I did twice as much stuff"
6
+ #
7
+ # format:
8
+ # - First arg is how much time spent
9
+ # - Second arg is a description
10
+
11
+ require 'trollop'
12
+ require 'debugger'
13
+
14
+ require "trak/tracker_util"
15
+
16
+ # place where data is stored
17
+ datadir = "#{ENV['HOME']}/Documents/Tracker/"
18
+ %x[mkdir -p #{datadir}]
19
+
20
+ # define command line options
21
+ opts = Trollop::options do
22
+ opt :report, "Reporting mode", :short => "-l"
23
+ opt :edit, "Edit mode"
24
+ opt :date, "The date", :type => String, :short => "-d"
25
+ opt :debug, "Debugging mode", :short => "-i"
26
+ end
27
+
28
+ $g_opts = opts
29
+ def debug(steps = 1)
30
+ debugger if $g_opts[:debug]
31
+ end
32
+
33
+ # all valid options have been processed, so figure out which mode
34
+ # we're in...
35
+ #
36
+ # if we found a -r or -l option, ignore everything else
37
+ if opts[:report]
38
+ MODE = 'report'
39
+ # now check if the user wants edit mode
40
+ elsif opts[:edit]
41
+ MODE = 'edit'
42
+ # if there are still unprocessed args (that didn't look like switches),
43
+ # we're in insert mode
44
+ elsif ARGV.length > 0
45
+ MODE = 'insert'
46
+ # if all else fails, there were probably no args to begin with, so we're
47
+ # in report mode
48
+ else
49
+ MODE = 'report'
50
+ end
51
+
52
+ today = Time.now.strftime '%F'
53
+
54
+ # did the user supply a date argument that isn't today?
55
+ if opts[:date] && opts[:date] != today
56
+ fdate = opts[:date]
57
+ # otherwise use today's date, formatted, and set date_arg to be false
58
+ else
59
+ fdate = today
60
+ opts[:date] = nil
61
+ end
62
+
63
+ # set the output file name
64
+ filename = "#{datadir}#{fdate}-time-log.txt"
65
+
66
+ if MODE == 'report'
67
+ if File.exist? filename
68
+ # open the file and get it as an array
69
+ begin
70
+ file = File.open(filename).readlines.map &:chomp
71
+ rescue
72
+ STDERR.puts "#{__FILE__}: #{$!}"
73
+ exit 1
74
+ end
75
+
76
+ # The keys for each hash are the titles of the various tasks logged.
77
+ # The values are the total time spent on the task.
78
+ work = {}
79
+ personal = {}
80
+
81
+ # find the start time for the day we're reporting on
82
+ startTime = file.first.split[1]
83
+
84
+ # process each line of the file
85
+ file[1..file.size].each do |line|
86
+ minutes, text = line.split(': ')
87
+ unless text =~ /personal|uni|lunch|home/
88
+ work[text] = 0 unless work.include? text
89
+ work[text] += minutes.to_i
90
+ else
91
+ personal[text] = 0 unless personal.include? text
92
+ personal[text] += minutes.to_i
93
+ end
94
+ end
95
+
96
+ # print the report
97
+ if opts[:date]
98
+ puts "# Logged work for #{fdate}"
99
+ else
100
+ puts "# Today's logged work"
101
+ end
102
+
103
+ workTotal = TrackerUtil::printSubReport(work, "Work")
104
+ personalTotal = TrackerUtil::printSubReport(personal, "Personal")
105
+
106
+ newTimeString = TrackerUtil::to12HourTime(TrackerUtil::newTimeWithMinutes(startTime, workTotal + personalTotal))
107
+ puts "Hours logged until #{newTimeString} (since #{TrackerUtil::to12HourTime(startTime)}). "
108
+
109
+ # if we're reporting for today, print the current time
110
+ puts "Currently #{TrackerUtil::to12HourTime(TrackerUtil::currentTimeFormatted())}." unless opts[:date]
111
+ else
112
+ if opts[:date]
113
+ STDERR.puts "No time log for #{fdate}. Track some time first."
114
+ else
115
+ STDERR.puts "No time log for today. Track some time first.\n"
116
+ end
117
+ end
118
+
119
+ elsif MODE == 'edit'
120
+ if File.exist? filename
121
+ if ENV['EDITOR']
122
+ exec "#{ENV['EDITOR']} #{filename}"
123
+ else
124
+ exec "open", filename
125
+ end
126
+ exit
127
+ else
128
+ STDERR.puts "#{__FILE__}: #{filename} does not exist or unable to open."
129
+ exit 1
130
+ end
131
+
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
+ STDERR.puts "Timelog update cancelled."
139
+ exit 1
140
+ end
141
+ end
142
+
143
+ # process arguments
144
+ debug
145
+ minutes = TrackerUtil::processTimeArgument ARGV.shift
146
+ message = ARGV.join(" ")
147
+
148
+ # open the output file
149
+ first_time = !File.exist?(filename)
150
+ # debug
151
+ begin
152
+ File.open filename, 'a', :autoclose => true do |file|
153
+ if first_time
154
+ currentTimeInMinutes = TrackerUtil::timeToMinutes(TrackerUtil::currentTimeFormatted)
155
+ startTime = TrackerUtil::minutesToTime((currentTimeInMinutes - minutes).round_to_nearest 15)
156
+ file.puts "#{fdate} #{startTime}"
157
+ end
158
+ file.puts "#{minutes}: #{message}"
159
+ end
160
+ rescue
161
+ STDERR.puts "Couldn't open #{filename}: #{$!}"
162
+ exit 1
163
+ end
164
+
165
+ else
166
+ STDERR.puts "Couldn't determine the correct mode (I was given '#{MODE}'): #{$!}"
167
+ exit 1
168
+ end
@@ -0,0 +1,16 @@
1
+ # https://github.com/rails/rails/blob/2a371368c91789a4d689d6a84eb20b238c37678a/activesupport/lib/active_support/core_ext/object/blank.rb#L91
2
+ class String
3
+ # 0x3000: fullwidth whitespace
4
+ NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
5
+
6
+ # A string is blank if it's empty or contains whitespaces only:
7
+ #
8
+ # "".blank? # => true
9
+ # " ".blank? # => true
10
+ # " ".blank? # => true
11
+ # " something here ".blank? # => false
12
+ #
13
+ def blank?
14
+ self !~ NON_WHITESPACE_REGEXP
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ class Integer
2
+ def round_to_nearest(integer = 1)
3
+ if integer == 1
4
+ self.round
5
+ else
6
+ ((self.to_f / integer).round * integer).to_i
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,103 @@
1
+ require "trak/blank"
2
+ require "trak/round_to_nearest"
3
+
4
+ module TrackerUtil
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
+ def self.currentTimeFormatted
62
+ Time.now.strftime(TIME_FORMAT_24HOUR)
63
+ end
64
+
65
+ # expects a single argument - the time argument in the format ##m or ##h
66
+ # if argument has no m/h qualifier, assume m
67
+ # returns a number of minutes
68
+ def self.processTimeArgument(time_string)
69
+ if time_string =~ /^(\d*\.?\d+)((m|min|minute|minutes)|(h|hr|hour|hours))?$/i
70
+ time = $1.to_i
71
+ modifier = $2
72
+ minutes = (modifier =~ /h.*/) ? time * 60 : time
73
+
74
+ # check enough time has been logged
75
+ if minutes < 15
76
+ STDERR.puts "You must log at least 15 minutes."
77
+ exit 1
78
+ end
79
+
80
+ minutes.round_to_nearest 15
81
+ else
82
+ STDERR.puts "Incorrectly formatted argument."
83
+ exit 1
84
+ end
85
+ end
86
+
87
+ # expects a time string formatted HH24:MM
88
+ def self.timeToMinutes(time)
89
+ hours, minutes = time.split(':')
90
+ hours*60 + minutes
91
+ end
92
+
93
+ # expects an integer
94
+ def self.minutesToTime(minutes)
95
+ time_with_hours_minutes(minutes / 60, minutes % 60).sprintf TIME_FORMAT_24HOUR
96
+ end
97
+
98
+ # expects an integer which is the amount of minutes logged
99
+ def self.startTimeInMinutes(minutes)
100
+ currentTimeInMinutes = timeToMinutes(currentTimeFormatted).round_to_nearest 15
101
+ currentTimeInMinutes - minutes
102
+ end
103
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Sharp
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: trollop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.16'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.16'
30
+ description: ! 'Problem: when tracking time, I don''t want to have to
31
+
32
+ start and stop a timer. Trak is a tool that lets me say "I just spent
33
+
34
+ 15 minutes working on email", instead of "I''m starting to email
35
+
36
+ now...whoops! I forgot to tell the computer I stopped." Then later in
37
+
38
+ the day when you spend some more time emailing, you don''t have to keep
39
+
40
+ the total time you''ve spent for the day in your head. When you tell trak
41
+
42
+ to report on your time spent for the day, it tallies each task and gives
43
+
44
+ you a breakdown.'
45
+ email:
46
+ - adsharp@me.com
47
+ executables:
48
+ - trak
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/trak/blank.rb
53
+ - lib/trak/round_to_nearest.rb
54
+ - lib/trak/tracker_util.rb
55
+ - lib/trak.rb
56
+ - bin/trak
57
+ - CHANGELOG.md
58
+ - README.md
59
+ homepage: http://github.com/sharplet/trak
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.21
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: A command line tool for tracking chunks of time
83
+ test_files: []