tempo-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +56 -0
  4. data/README.md +326 -0
  5. data/Rakefile +65 -0
  6. data/bin/tempo +477 -0
  7. data/features/arrange.feature +43 -0
  8. data/features/checkout.feature +63 -0
  9. data/features/end.feature +65 -0
  10. data/features/project.feature +246 -0
  11. data/features/report.feature +62 -0
  12. data/features/start.feature +87 -0
  13. data/features/step_definitions/tempo_steps.rb +138 -0
  14. data/features/support/env.rb +26 -0
  15. data/features/tempo.feature +13 -0
  16. data/features/update.feature +69 -0
  17. data/lib/file_record/directory.rb +11 -0
  18. data/lib/file_record/directory_structure/tempo/README.txt +4 -0
  19. data/lib/file_record/directory_structure/tempo/tempo_projects.yaml +6 -0
  20. data/lib/file_record/record.rb +120 -0
  21. data/lib/tempo/controllers/arrange_controller.rb +52 -0
  22. data/lib/tempo/controllers/base.rb +117 -0
  23. data/lib/tempo/controllers/checkout_controller.rb +42 -0
  24. data/lib/tempo/controllers/end_controller.rb +42 -0
  25. data/lib/tempo/controllers/projects_controller.rb +107 -0
  26. data/lib/tempo/controllers/records_controller.rb +21 -0
  27. data/lib/tempo/controllers/report_controller.rb +55 -0
  28. data/lib/tempo/controllers/start_controller.rb +42 -0
  29. data/lib/tempo/controllers/update_controller.rb +78 -0
  30. data/lib/tempo/models/base.rb +176 -0
  31. data/lib/tempo/models/composite.rb +71 -0
  32. data/lib/tempo/models/log.rb +194 -0
  33. data/lib/tempo/models/project.rb +73 -0
  34. data/lib/tempo/models/time_record.rb +235 -0
  35. data/lib/tempo/version.rb +3 -0
  36. data/lib/tempo/views/arrange_view.rb +27 -0
  37. data/lib/tempo/views/base.rb +82 -0
  38. data/lib/tempo/views/formatters/base.rb +30 -0
  39. data/lib/tempo/views/formatters/screen.rb +86 -0
  40. data/lib/tempo/views/projects_view.rb +82 -0
  41. data/lib/tempo/views/report_view.rb +26 -0
  42. data/lib/tempo/views/reporter.rb +70 -0
  43. data/lib/tempo/views/time_record_view.rb +30 -0
  44. data/lib/tempo/views/view_records/base.rb +117 -0
  45. data/lib/tempo/views/view_records/composite.rb +40 -0
  46. data/lib/tempo/views/view_records/log.rb +28 -0
  47. data/lib/tempo/views/view_records/project.rb +32 -0
  48. data/lib/tempo/views/view_records/time_record.rb +48 -0
  49. data/lib/tempo.rb +26 -0
  50. data/lib/time_utilities.rb +30 -0
  51. data/tempo-cli.gemspec +26 -0
  52. data/test/lib/file_record/directory_test.rb +30 -0
  53. data/test/lib/file_record/record_test.rb +106 -0
  54. data/test/lib/tempo/controllers/base_controller_test.rb +60 -0
  55. data/test/lib/tempo/controllers/project_controller_test.rb +24 -0
  56. data/test/lib/tempo/models/base_test.rb +173 -0
  57. data/test/lib/tempo/models/composite_test.rb +76 -0
  58. data/test/lib/tempo/models/log_test.rb +171 -0
  59. data/test/lib/tempo/models/project_test.rb +105 -0
  60. data/test/lib/tempo/models/time_record_test.rb +212 -0
  61. data/test/lib/tempo/views/base_test.rb +31 -0
  62. data/test/lib/tempo/views/formatters/base_test.rb +13 -0
  63. data/test/lib/tempo/views/formatters/screen_test.rb +94 -0
  64. data/test/lib/tempo/views/reporter_test.rb +40 -0
  65. data/test/lib/tempo/views/view_records/base_test.rb +77 -0
  66. data/test/lib/tempo/views/view_records/composite_test.rb +57 -0
  67. data/test/lib/tempo/views/view_records/log_test.rb +28 -0
  68. data/test/lib/tempo/views/view_records/project_test.rb +0 -0
  69. data/test/lib/tempo/views/view_records/time_record_test.rb +0 -0
  70. data/test/support/factories.rb +177 -0
  71. data/test/support/helpers.rb +69 -0
  72. data/test/test_helper.rb +31 -0
  73. metadata +230 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ results.html
3
+ html/*
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Load gems from the gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ tempo (0.1.0)
5
+ chronic (~> 0.10.2)
6
+ gli (= 2.6.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ansi (1.4.3)
12
+ aruba (0.5.3)
13
+ childprocess (>= 0.3.6)
14
+ cucumber (>= 1.1.1)
15
+ rspec-expectations (>= 2.7.0)
16
+ builder (3.2.2)
17
+ childprocess (0.3.9)
18
+ ffi (~> 1.0, >= 1.0.11)
19
+ chronic (0.10.2)
20
+ coderay (1.0.9)
21
+ cucumber (1.3.2)
22
+ builder (>= 2.1.2)
23
+ diff-lcs (>= 1.1.3)
24
+ gherkin (~> 2.12.0)
25
+ multi_json (~> 1.3)
26
+ diff-lcs (1.2.4)
27
+ ffi (1.9.0)
28
+ gherkin (2.12.0)
29
+ multi_json (~> 1.3)
30
+ gli (2.6.1)
31
+ json (1.8.0)
32
+ method_source (0.8.1)
33
+ multi_json (1.7.7)
34
+ pry (0.9.12.2)
35
+ coderay (~> 1.0.5)
36
+ method_source (~> 0.8)
37
+ slop (~> 3.4)
38
+ rake (10.1.0)
39
+ rdoc (4.0.1)
40
+ json (~> 1.4)
41
+ rspec-expectations (2.13.0)
42
+ diff-lcs (>= 1.1.3, < 2.0)
43
+ slop (3.4.5)
44
+ turn (0.9.6)
45
+ ansi
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ aruba
52
+ pry (~> 0.9.12.2)
53
+ rake
54
+ rdoc
55
+ tempo!
56
+ turn (~> 0.9.6)
data/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # tempo
2
+
3
+ A command line interface for time tracking by project.
4
+
5
+ ## Methodology
6
+
7
+ ### Track time by Project
8
+
9
+ Tempo tracks time against projects. Projects can be nested, and tagged, and each time entry can have an additional description. Time reports are produced by day.
10
+
11
+ Future enhancements will include reports by project and time totals by day or by project.
12
+
13
+ ### Records
14
+
15
+ All records are produced in YAML files, at the root user directory. These reports can be edited by hand, but keep in mind that any invalid data could cause problems when it is read back into the app. Make sure time formatting is valid and that they don't overlap, and that all ids are unique per page.
16
+
17
+ Each day's time records are designed to work independently. When adding or updating time records, only the records for the days in question are loaded into the app. The most recent day's records will also always be read in, to assure no running records are created earlier than existing records.
18
+
19
+ ### Features
20
+
21
+ #### command line assistance
22
+
23
+ run `tempo help` for a list of commands
24
+
25
+ run `tempo [command] --help` for help on any command
26
+
27
+ #### syntax
28
+
29
+ All commands and command options can be abbreviated to the first letter:
30
+
31
+ ##### example
32
+
33
+ # these two commands are the same:
34
+ $ tempo project --list
35
+ $ tempo p -l
36
+
37
+ #### fuzzy matching
38
+
39
+ Commands that manage the projects will use fuzzy matching for the project name. This means if you have a project called "put on my big boy pants" you will be able to checkout this project by running `tempo checkout pants`. If you have a situation where fuzzy matching causes errors because it matches with more than one project, you can always use the --exact flag, and pass in an exact match.
40
+
41
+ #### Ids
42
+
43
+ Every project and time record has an id which can also be used to match against. Running commands with the global --id flag will include ids in the output.
44
+ Time record ids are on a per-day basis, and begin again with an id of 1 on the next day.
45
+
46
+ ## Commands
47
+
48
+ Before you begin using tempo, you must have at least one project. Projects can be tagged, and organized as sub-projects. To view your projects, look at ~/tempo/tempo_projects.yaml
49
+
50
+ ### project
51
+ #### Manage the Projects that timed tasks are assigned to
52
+
53
+ tempo [global options] p [command options] "name of the project"
54
+
55
+ ##### command options
56
+
57
+ --add - Add a Project
58
+ --delete=title - Delete a Project
59
+ --exact - Exact Match
60
+ --id - List by or perform command by project Id
61
+ --list - List Projects
62
+ --tag=tagword - Tag a Project
63
+ --untag=tagword - Untag a Project
64
+
65
+
66
+ Manage the Projects that timed tasks are assigned to. By default, lists the current project, or with --list tag, lists all projects. The active project will be marked by an asterisk.
67
+
68
+ If supplied with arguments and no command options, a new project will be added.
69
+
70
+ ##### examples:
71
+
72
+ # list current project
73
+ $ tempo project
74
+
75
+ # list all projects
76
+ $ tempo project --list
77
+
78
+ # adding a new project titled 'star gazing'
79
+ $ tempo project star gazing
80
+
81
+ # adding a new project with a tag
82
+ $ tempo project --add star gazing --tag cellar
83
+
84
+ # untagging and tagging an existing project
85
+ $ tempo project star gazing --untag cellar --tag stellar
86
+
87
+ ### checkout
88
+ #### Checkout a project to track time against
89
+
90
+ tempo [global options] checkout [command options] "name of the project"
91
+
92
+ ##### command options
93
+
94
+ --add=title - Add and Checkout a new project
95
+ --exact - use an exact match for project title
96
+ --id - checkout by project id
97
+
98
+ Only one project is active at a time, and this project will be assigned to all new time records. You can checkout existing projects, or use this command to create a new active project
99
+
100
+ #### examples:
101
+
102
+ # checking out an existing project titled 'star gazing'
103
+ $ tempo checkout star gazing
104
+
105
+ # adding and checking out a new project titled 'star gazing'
106
+ $ tempo checkout --add star gazing
107
+
108
+ ### arrange
109
+ #### Arrange project hierarchy
110
+
111
+ tempo [global options] arrange [command options] [parent project] : child project
112
+
113
+ ##### command options
114
+
115
+ --exact - use an exact match for project title
116
+ --id - checkout by project id
117
+
118
+ Arrange projects into a parent/child hierarchy
119
+
120
+ New projects are added as root projects by default. Use arrange to arrange existing projects as child or root projects. If no parent project is supplied (to the left of the semicolon) the project will become a root project.
121
+
122
+ ##### examples:
123
+
124
+ # Arrange an existing project as a root project
125
+ $ tempo arrange : aquascaping
126
+
127
+ # Arrange an existing project as a child project
128
+ # (aquascaping will be the parent of coral pruning)
129
+ $ tempo arrange aquascaping : coral pruning
130
+
131
+ ### start
132
+ #### Start a time entry
133
+
134
+ tempo [global options] start [command options] description
135
+
136
+ ##### command options
137
+ --at=time
138
+ --end=time
139
+
140
+ Starts a new time entry, and closes out any running time entries.
141
+
142
+ You can also add a description of the time entry.
143
+
144
+ To start a time entry at a time other than the current, pass it in as an argument to the --at flag. This will accept a number of human readable formats, multiple word time formats should be enclosed in quotes
145
+
146
+ ##### examples:
147
+
148
+ # start a new time record at the current time with no description
149
+ $ tempo start
150
+
151
+ # start and end a previous time record with a description
152
+ $ tempo start learning how to do the Hustle --at 'yesterday at 8:00 in the evening' --end 'yesterday at 11:59pm'
153
+
154
+ ### end
155
+ #### End a time entry
156
+
157
+ tempo [global options] end [command options] time out, defaults to now
158
+
159
+ ##### command options
160
+ --at=time
161
+
162
+ Closes out any running time entries.
163
+
164
+ You can optionally add a description of the time entry, which will overwrite the current description.
165
+
166
+ To end a time entry at a time other than the current, pass it in as an argument to the --at flag. This will accept a number of human readable formats, multiple word time formats should be enclosed in quotes.
167
+
168
+ ##### examples:
169
+
170
+ # creating an entry at 4:55pm, then ending it 5 minutes later and changing the description
171
+ $ tempo start --at 16:55 practicing the human torch trick
172
+ $ tempo end --at 'today at 5:00pm' learning how to stop, drop, and roll
173
+
174
+ ### update
175
+ #### Update a time entry
176
+
177
+ tempo [global options] update [command options] [description]
178
+
179
+ ##### command options
180
+
181
+ --delete - Delete a Time Entry
182
+ --end=time - Update the End Time
183
+ --id=number - Select by Id
184
+ --on=date - Select On Date
185
+ --project - Update to the active project
186
+ --start=time - Update the Start Time
187
+
188
+ update the project, start time, or end time for a time entry.
189
+
190
+ Defaults to the current time entry, or most recently completed time entry if none are running. Optionally, you can supply an id for a time entry on the current day, or a day and id to update previous time entries.
191
+
192
+ The description, if passed in, will be used to replace the existing description. You can also change the start or end time, or delete the entire time entry.
193
+
194
+ ##### examples:
195
+
196
+ # update description for the last time entry
197
+ $ tempo update practicing banjo
198
+
199
+ # update the description for yesterday's entry with id of 2
200
+ $ tempo update --id 2 --on 'yesterday' practicing banjo
201
+
202
+ # update the start time for the last time entry
203
+ $ tempo update --start 7:00
204
+
205
+ # update the end time and description
206
+ $ tempo update --end '8:00pm' practicing banjo
207
+
208
+ # delete the last time entry
209
+ $ tempo update --delete
210
+
211
+ # update the description, start, and end time for a previous day's entry
212
+ $ tempo update --on 1/17/14 --start "1/17/14 8am" --end "1/17/14 10am" practicing banjo
213
+
214
+ ### report
215
+ #### Report time entries
216
+
217
+ tempo [global options] report [command options] [day]
218
+
219
+ ##### command options
220
+
221
+ --from=time - begin time records on this date
222
+ --to=time - end time records on this date
223
+
224
+ Reports time entries for a day or a period of days. By default, lists the last recorded day's entries. To list a different day, supply a day as the args. To list a series of records, include a --from and --to value
225
+
226
+ ##### examples:
227
+
228
+ # report current day's entries
229
+ $ tempo report
230
+
231
+ # report a different day
232
+ $ tempo report 11/24/14
233
+
234
+ # report the past week
235
+ $ tempo report -f 'a week ago'
236
+
237
+ # report a period of days
238
+ $ tempo report -f 'last monday' -t 'last friday'
239
+
240
+ ## Assumptions and Limitations
241
+
242
+ ### Time periods
243
+
244
+ All records are rounded to the nearest minute. Time records are organized by day, and close out on the day they were started.
245
+
246
+ Tempo uses [Chronic](https://github.com/mojombo/chronic) to parse time formats, which adds a lot of convenience functions such as "yesterday at 5:00".
247
+ Chronic does have some limitations and inconsistencies as well, please see the [https://github.com/mojombo/chronic/issues](list of known issues) if you find problems with time parsing.
248
+
249
+ ### No overlapping projects
250
+
251
+ It is assumed only one time record can exist for any given time. Overlapping time entries will result in an error.
252
+
253
+ ### Only one running project
254
+
255
+ One entry can be running at any given time, and it must be the most recent entry. Older entries will be closed out when a new one is started, and if the entry is running on a previous day, it will be closed out on the last minute of that day.
256
+
257
+ ## Tempo Development
258
+
259
+ Project development is on-going and in my spare time. Any error reporting, pull requests, and suggestions welcome.
260
+
261
+ ### Planned features
262
+
263
+ #### Files
264
+
265
+ * add a config file with global options.
266
+ * add an option to change the location of the tempo file structure (for syncing with Dropbox, for instance).
267
+
268
+ #### Projects
269
+
270
+ * add an archive capability, which hides inactive projects from project lists. This can be handled with an tag keyword 'archived'.
271
+ * allow adding a new project and listing all projects in the same command.
272
+
273
+ #### Start
274
+
275
+ * Allow start to use a project other than the current one.
276
+
277
+ #### Resume
278
+
279
+ * Add a command to start the last time entry running again.
280
+ * Add a command to start a new time entry with the same details as a previous one.
281
+
282
+ #### Time format enhancements
283
+
284
+ * Updating time record start or end time using `--on` should not need the full date repeated for `--start` or `--end`.
285
+ * invalid times should be retried with the current (or most logical) date. Example: if '9:00' doesn't work, try 'today at 9:00'
286
+
287
+ #### Reporting
288
+
289
+ * Recording output to file
290
+ * Reporting by project
291
+ * Composite view records (grouped time records by day or project), to allow nested list reporting
292
+ * Total hours by project or day
293
+ * Alternative formats: JSON, HTML
294
+
295
+ ### Running in development
296
+
297
+ You need to use `bundle exec bin/tempo` to run in development
298
+
299
+ ### Testing
300
+
301
+ rakes default behavior is to run unit tests and all cucumber features
302
+
303
+ #### run unit tests only
304
+ run `bundle exec rake test` to run unit tests only
305
+
306
+ #### run only some cucumber features
307
+
308
+ add @focus before specific features and run `bundle exec rake features focus`
309
+
310
+ #### cleanup
311
+
312
+ Testing creates a test directory at ~/testing/
313
+
314
+ You can cleanup the testing directories with `bundle exec rake clean`
315
+
316
+ ### Other command line time tracking apps:
317
+
318
+ This is a very opinionated time tracking app, you may find others that suit your working method better:
319
+
320
+ If you would prefer to keep your data in a database, or run independant time sheets by project, here is a utility that uses SQLite:
321
+
322
+ [timetrap](https://github.com/samg/timetrap)
323
+
324
+ Another one worth checking out for a lightweight time tracker:
325
+
326
+ [t_time_tracker](https://github.com/christiangenco/t_time_tracker)
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+
8
+ Rake::RDocTask.new do |rd|
9
+ rd.rdoc_files.include("lib/**/*.rb","bin/**/*")
10
+ rd.title = 'tempo'
11
+ end
12
+
13
+ spec = eval(File.read('tempo-cli.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'features/results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += ' --tags ~@pending'
23
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
24
+ t.cucumber_opts = opts
25
+ t.fork = false
26
+ end
27
+
28
+ desc 'Run features tagged with focus (@focus)'
29
+ Cucumber::Rake::Task.new('cucumber:focus') do |t|
30
+ tag_opts = ' --tags ~@pending'
31
+ tag_opts += ' --tags @focus'
32
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
33
+ t.fork = false
34
+ end
35
+
36
+ task :cucumber => :features
37
+
38
+ task :tests_setup do
39
+ @ORIGINAL_HOME = ENV['HOME']
40
+ ENV['HOME'] = ENV['HOME'] + "/testing"
41
+ Dir.mkdir(ENV['HOME'], 0700) unless File.exists?(ENV['HOME'])
42
+ dir = File.join(Dir.home,"tempo")
43
+ Dir.mkdir(dir, 0700) unless File.exists?(dir)
44
+ end
45
+
46
+ task :tests_teardown do
47
+ ENV['HOME'] = @ORIGINAL_HOME
48
+ dir = File.join(Dir.home, "testing")
49
+ FileUtils.rm_r dir if File.exists?(dir)
50
+ end
51
+
52
+ require 'rake/testtask'
53
+ Rake::TestTask.new do |t|
54
+ t.name = "run_tests"
55
+ t.libs << "test"
56
+ t.test_files = FileList['test/**/*_test.rb']
57
+ end
58
+
59
+ task :test => [:tests_setup, :run_tests, :tests_teardown ]
60
+
61
+ task :clean => [:tests_setup, :tests_teardown]
62
+
63
+ task :default => [:tests_setup, :run_tests, :features, :tests_teardown]
64
+
65
+ task 'features:focus' => [:tests_setup, :run_tests, 'cucumber:focus', :tests_teardown]