syc-task 0.0.7 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -6,7 +6,8 @@ The application can be installed with
6
6
  $ gem install syc-task
7
7
 
8
8
  == Usage
9
- syctask provides basic task organizer functions as create, update, list and complete a task. Additional functions are to plan tasks you want to accomplish today. If you are not sure in which sequence to conduct the task you can prioritize them with a pair wise comparisson. You can time tasks with start and stop and you can finally extract tasks from a minutes of meetings file. The schedule task will print a graphical timeline of the working day assigning the planned tasks to the timeline. Busy times are marked red. Meetings are listed with associated tasks that are assigned to the meetings.
9
+ syctask provides basic task organizer functions as create, update, list and complete a task. Additional functions are to plan tasks you want to accomplish today. If you are not sure in which sequence to conduct the task you can prioritize them with a pair wise comparisson. You can time tasks with start and stop and you can finally extract tasks from a minutes of meetings file. The schedule task
10
+ command will print a graphical timeline of the working day assigning the planned tasks to the timeline. Busy times are marked red. Meetings are listed with associated tasks that are assigned to the meetings. With the statistics command you can print statistical evaluation of tasks duration and count.
10
11
 
11
12
  ===Create tasks with new
12
13
  Create a new task in the default task directory ~/.tasks
@@ -47,7 +48,8 @@ Invoke plan with a filter
47
48
  Move tasks to another days plan
48
49
  $ syctask plan today --move tomorrow --id 3,5
49
50
 
50
- This will move the tasks with ID 3 and 5 from the today's plan to the tomorrow's plan
51
+ This will move the tasks with ID 3 and 5 from the today's plan to the tomorrow's plan. The duration will be set to the remaining processing time but at least to
52
+ 30 minutes.
51
53
 
52
54
  ===Prioritize tasks
53
55
  Planned tasks can be prioritized in a pair wise comparisson. So each task is
@@ -88,11 +90,21 @@ The plan or schedule command will print the tasks in the specified order
88
90
  3: 9 - My second task
89
91
 
90
92
  If only a part of the tasks is provided the rest of the tasks is appended to
91
- the end of the task plan.
93
+ the end of the task plan. If you specify a position flag the prioritized tasks are added at the provided position.
94
+ $ syctask plan -o 7,9 -p 2
95
+ Tasks
96
+ -----
97
+ 0: 3 - My first task
98
+ 1: 10 - My fourth task
99
+ 2: 7 - My third task
100
+ 3: 9 - My second task
92
101
 
93
102
  ===Create schedule
94
103
  The schedule command will print a graphical schedule with assigning the tasks
95
- added with plan.
104
+ selected with plan. When schedule command is invoked the planned tasks are
105
+ added at or after the current time within the time schedule. Tasks that are done
106
+ and scheduled in the future are not shown. Tasks done and in the past are shown
107
+ with the actual processing time.
96
108
 
97
109
  Create a schedule with working time from 8a.m. to 6p.m. and meetings between
98
110
  9a.m. and 9.30a.m. and 1p.m. and 2.45p.m.
@@ -135,6 +147,24 @@ will print
135
147
  -----
136
148
  0: 1 - My first task
137
149
 
150
+ A task that is re-scheduled with
151
+ $ syctask update 1 -f tomorrow
152
+
153
+ will be shown as done (green) in the schedule and instead of separator - it
154
+ shows ~.
155
+
156
+ Tasks
157
+ ----
158
+ 0: 1 ~ My first task
159
+
160
+ A started task will be indicated by *
161
+
162
+ $ syctask start 1
163
+ $ syctask sche
164
+ Tasks
165
+ -----
166
+ 0: 1 * My first task
167
+
138
168
  ===List tasks
139
169
  List tasks that are not marked as done in short form
140
170
  $ syctask list
@@ -145,6 +175,17 @@ List all tasks in long form
145
175
  Search tasks that match a pattern
146
176
  $ syctask list --id "<10" --follow_up ">2013-02-25" --title "My \w task"
147
177
 
178
+ ===Inspect tasks
179
+ Lists each today's unplanned task and allows to edit, delete, mark as done or
180
+ plan
181
+ $ syctask inspect
182
+ 0016 Create command for inspection
183
+ (e)dit, (d)one, de(l)ete, (p)lan, (c)omplete, (s)kip, (q)uit:
184
+
185
+ ===Edit task
186
+ Edit a task with ID 10 in vi
187
+ $ syctask edit 10
188
+
148
189
  ===Update tasks
149
190
  Except for title and id all values can be updated. Note and tags are not
150
191
  overridden rather supplemented with the update value.
@@ -164,6 +205,36 @@ Delete tasks with ID 8 and 12 from the planned tasks of today. The tasks are
164
205
  only removed from the planned tasks and not physically deleted.
165
206
  $ syctask delete --plan today --id 8,12
166
207
 
208
+ ===Settings
209
+ The settings command allows to define default values for task directory and to create general purpose tasks that can be used for tracking and later statistical evaluation.
210
+
211
+ Create general purpose tasks for phone and talk
212
+ $ syctask setting --general PHONE,TALK
213
+
214
+ List all settings
215
+ $ syctask setting --list
216
+
217
+ ===Info
218
+ Info searches for the location of a task and lists all task directories
219
+
220
+ Search for task with id 102
221
+ $ syctask info --id 102
222
+
223
+ List all task directories
224
+ $ syctask info --taskdir
225
+
226
+ ===Statistics
227
+ Shows statistics for work and meeting times as well as for task processing
228
+
229
+ Evaluate the complete log file
230
+ $ syctask statistics
231
+
232
+ Evaluate work times, meetings and tasks between 2013-01-01 and 2013-04-14
233
+ $ syctask statistics 2013-01-01 2013-04-14
234
+
235
+ Evaluate yesterday and today
236
+ $ syctask statistics yesterday today
237
+
167
238
  ===Task directory and project directory
168
239
  The global options --taskdir and --project determine where the command finds
169
240
  or creates the tasks. The default task directory is ~/.tasks, so if no task
@@ -182,6 +253,7 @@ In the table the relation of commands to --taskdir and --project are listed.
182
253
  delete x x deletes the tasks in taskdir/project
183
254
  done x x marks tasks in taskdir/project as done
184
255
  help - -
256
+ inspect x x lists task to edit, done, delete, plan
185
257
  list x x lists tasks in taskdir/project
186
258
  new x x creates tasks in taskdir/project
187
259
  plan x x retrieves tasks to plan from taskdir/projekt
@@ -189,22 +261,46 @@ In the table the relation of commands to --taskdir and --project are listed.
189
261
  scan x x creates scanned tasks in taskdir/project
190
262
  schedule - - schedules the planned tasks (see plan)
191
263
  start - - starts task from planned tasks (see plan)
264
+ statistics - - shows statistics of time and count
192
265
  stop - - stops task from planned task
193
266
  update x x updates task in taskdir/project
194
267
 
195
268
  ===Files
196
269
 
270
+ * ID
271
+ id file contains the last issued id.
272
+
273
+ * IDS
274
+ ids file contains all issued ids.
275
+
197
276
  * Task files
198
277
  The tasks are named ID.task where ID is any Integer as 10.task. The files are
199
278
  saved as YAML files and can be edited directly.
200
279
 
201
280
  * Planned tasks files
202
- The planned tasks are save to YYYY-MM-DD_planned_tasks in the default task
203
- directory. Each task is saved with the tasks directory and the ID.
281
+ The planned tasks are save to YYYY-MM-DD_planned_tasks in syctask's system
282
+ directory. Each task is saved with the task's directory and the ID.
204
283
 
205
284
  * Schedule files
206
- The schedule is saved to YYYY-MM-DD_schedule in the default task directory. The
207
- files are saved as YAML files and can be changed manually.
285
+ The schedule is saved to YYYY-MM-DD_time_schedule in the default task directory.The files are saved as YAML files and can be changed manually.
286
+
287
+ * Log file
288
+ Creating schedule and task processings is logged to tasks.log. For example when a task is started and stopped this is action is saved to tasks.log.
289
+
290
+ * Tracked file
291
+ A started task is saved to tracked_tasks. A semaphore file is created with
292
+ ID.track when the task ID is started. When the task is stopped the semaphore
293
+ file is deleted.
294
+
295
+ * General purpose tasks
296
+ With syctask setting -g PHONE so called general purpose tasks can be created.
297
+ These tasks can be used for time tracking and later statistic evaluation to
298
+ determine the amount of disturbences e.g. by phone. These tasks are saved to
299
+ default_tasks. The general purpose tasks itself are also saved to the
300
+ .syc/syctask directory as regular task files.
301
+
302
+ * Default task dir
303
+ The default task that is used e.g. with list is saved to default_tasks_dir. This can be set with the setting command.
208
304
 
209
305
  ==Working with syctask
210
306
  To work with syctask and get the most out of it there is to follow a certain
@@ -212,8 +308,8 @@ process.
212
308
 
213
309
  ===Creating a schedule
214
310
  ==== View tasks
215
- In the morning before I start to work I scan my tasks with syctask list to get
216
- an overview of my open tasks.
311
+ In the morning before I start to work I scan my tasks with syctask list or
312
+ syctask inspect to get an overview of my open tasks.
217
313
  $ syctask list
218
314
 
219
315
  ==== Plan tasks
@@ -235,14 +331,20 @@ I assign the topics I want to discuss in the meetings to the meetings with
235
331
  syctask schedule -a "A:1,3,6;B:3,5"
236
332
 
237
333
  ==== Start a task
238
- To begin I start the first task in the schedule with syctask start 0 (where 0 is the sequence of the planned tasks, the ID is different e.g. 23)
239
- $ syctask start 0
334
+ To begin I start the first task in the schedule with syctask start -p ID (where ID is the ID of the planned (-p) tasks)
335
+ $ syctask start -p 10
240
336
 
241
337
  ==== End a task
242
338
  To end the task I invoke
243
339
  $ syctask stop
244
340
  This will stop the last started task
245
341
 
342
+ ==== Re-schedule a task
343
+ If I cannot finish a task than I update the task with a new follow-up date
344
+ $ syctask update 23 -f tomorrow
345
+
346
+ The task will be shown in the today's schedule as done.
347
+
246
348
  ==== Complete a task
247
349
  When the task is done I call
248
350
  $ syctask done 23
@@ -259,7 +361,7 @@ directory with the ID and save the files in this directory. If there is an
259
361
  existing directory I link to the file from the ID directory
260
362
 
261
363
  ==Supported platform
262
- syc-task has been tested with 1.9.3
364
+ syc-task has been tested with 1.9.3. It also works in Windows using Cygwin.
263
365
 
264
366
  ==Add TAB-completion to syctask
265
367
  To activate bash's TAB-completion following lines have to be added to ~/.bashrc
@@ -308,9 +410,9 @@ To print to Dell-B1160w-Mono the following command can be used
308
410
 
309
411
  $ syctask schedule | lpr -P Dell-B1160w-Mono
310
412
 
311
- ==Notes
413
+ ==Release Notes
312
414
  Version 0.0.1
313
- new, update, list and done is implemented.
415
+ Implementation of new, update, list and done commands.
314
416
 
315
417
  Version 0.0.4
316
418
  * delete: deleting tasks or remove tasks from a task plan
@@ -334,6 +436,48 @@ Version 0.0.6
334
436
  Version 0.0.7
335
437
  * updated rdoc
336
438
 
439
+ Version 0.1.15
440
+ * IDs are now unique independent of the task or project directory. After
441
+ upgrading from a version 0.0.7 or older the user asked whether to re-index
442
+ the tasks. It is adviced to tar the tasks before re-indexing with
443
+ $ tar cvfz tasks.tar.gz .tasks other_task_directories
444
+ * start will now show a timer in the upper right corner of the screen when
445
+ started with the -t (--timer) flag.
446
+ $ syctask start 10 -t
447
+ In order to use the task timer ncurses has to be installed as the task timer
448
+ uses tput from the ncurses library.
449
+ * The schedule has a heading with the schedule's date and the working time
450
+ * Planned tasks are now added at or after the current time if they are not done
451
+ yet. Done tasks are shown in the past with the actual processing time. Tasks
452
+ done before the start of the schedule are not shown in the schedule.
453
+ * Meetings that are at the current time are indicated with a *. Active tasks
454
+ are indicated with a star, re-scheduled tasks are indicated with a ~.
455
+ * Assigning tasks to meetings in a schedule is now done with the task ID
456
+ * Statistics show statistics about work time, meeting times, general purpose
457
+ tasks and task processing. Total, min, max and average time and count is
458
+ listed. If you have used version 0.0.7 it is adviced to delete tasks.log that
459
+ lives in ~/.tasks before upgrading or in ~/.syc/syctask after upgrading.
460
+ Otherwise the statistic results seem odd.
461
+ * Meeting time in time line now shows correct duration
462
+ * Info command searches for the location of a task and lists all task
463
+ task directories with the tasks contained.
464
+ * Plan move command sets the duration to the remaining processing time but at
465
+ least to 15 minutes
466
+ * With the setting command the default task directory can be set and general
467
+ purpose tasks can be created. A general purpose task can be used for tracking
468
+ to analyse how much time for phone calls is occupied.
469
+ setting -l list all general purpose tasks and the default task directory
470
+ * Prio command now takes a position flag together with the order flag to
471
+ determine where to insert the newly ordered tasks
472
+ * All commands that take an ID as argument (done, edit, start, update) look up
473
+ the task file associated to the id in the ids file. If it is found the
474
+ provided task directory is not considered for the task file. If the id is not
475
+ contained in the ids file the task is looked up in the provided directory
476
+ * Inspect command allows to list each today's unplanned task to edit, delete,
477
+ mark as done or plan
478
+ * Update command now has a duration flag to set the task's duration
479
+
480
+ ==Tests
337
481
  The test files live in the folder test and start with test_.
338
482
 
339
483
  There is a rake file available to run all tests
data/bin/console_timer ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'rainbow'
5
+
6
+ # ConsoleTimer prints a task and the lead time at the upper right corner of the
7
+ # schreen. Invokation example:
8
+ # * Create a semaphore like id.track
9
+ # * Console time in a new ruby process
10
+ # semaphore = File.expand_path("~/.syc/syctask/#{id}.track"
11
+ # FileUtils.touch semaphore
12
+ # system "ruby lib/sycutil/console_timer.rb 60 10 semaphore"
13
+ # This will start the ConsoleTimer with a lead time of 1 minute for task 10.
14
+ # To stop the timer the semaphore has to be deleted
15
+ # FileUtils.rm semaphore
16
+ class ConsoleTimer
17
+
18
+ # Create a new ConsoleTimer with the time to count down, the task's ID and a
19
+ # semaphore. The semaphore is a file named id.track where id is equal to the
20
+ # provided id. The semaphore is checked for existence. If the semaphore is
21
+ # deleted than ConsoleTimer is stopped.
22
+ def initialize(time, id, semaphore)
23
+ @time = time.to_i
24
+ @id = id
25
+ @start = Time.now
26
+ @semaphore = semaphore
27
+ end
28
+
29
+ # Starts the timer. The timer is run as long the semaphore is available
30
+ def start
31
+ track = true
32
+ while track
33
+ sleep 1
34
+ output
35
+ track = File.exists? @semaphore
36
+ end
37
+ exit 0
38
+ end
39
+
40
+ # Prints the id and the lead time of the currently tracked task. As long as
41
+ # the provided time is greater than 0 the time is printed in green, otherwise
42
+ # red
43
+ def output
44
+ color = :green
45
+ difference = @time - (Time.now - @start).round
46
+ if difference < 0
47
+ difference = difference.abs
48
+ color = :red
49
+ end
50
+ seconds = difference % 60
51
+ minutes = difference / 60 % 60
52
+ hours = difference / 60 / 60 % 60
53
+ count_down = sprintf("%d: %02d:%02d:%02d", @id, hours, minutes, seconds)
54
+ size = count_down.size
55
+ count_down = count_down.color(color)
56
+ command = "tput sc;"+
57
+ "tput cup 0 $(($(tput cols) - #{size}));"+
58
+ "echo #{count_down};tput rc"
59
+ system command
60
+ end
61
+
62
+ end
63
+
64
+ # Expects to receive parameters duration, id and semaphore
65
+ if ARGV.size == 3
66
+ duration = ARGV.shift
67
+ id = ARGV.shift
68
+ semaphore = ARGV.shift
69
+ timer = ConsoleTimer.new(duration, id, semaphore)
70
+ timer.start
71
+ else
72
+ exit -1
73
+ end
74
+
75
+
data/bin/syctask CHANGED
@@ -3,13 +3,17 @@ require 'gli'
3
3
  require 'syctask'
4
4
  include GLI::App
5
5
  include Syctime
6
+ include Syctask
6
7
 
7
8
  program_desc 'A simple task manager'
8
9
 
9
10
  version Syctask::VERSION
10
11
 
12
+ @settings = Syctask::Settings.new
13
+ @general_purpose_tasks = '|' + @settings.read_tasks.keys.join('|')
14
+
11
15
  desc 'The directory where tasks are saved to'
12
- default_value File.expand_path('~/.tasks')
16
+ default_value Syctask::WORK_DIR
13
17
  arg_name 'TASK_DIR'
14
18
  flag [:t,:taskdir]
15
19
 
@@ -17,6 +21,106 @@ desc 'Project name where tasks are saved'
17
21
  arg_name 'PROJECT'
18
22
  flag [:p, :project]
19
23
 
24
+ desc 'Settings for syctask'
25
+ command :settings do |c|
26
+
27
+ c.desc 'Define general purpose tasks to be used with the start command'
28
+ c.arg_name 'TASK'
29
+ c.flag [:g, :general], :must_match => /^\w+(?:,\w+)*/
30
+
31
+ c.desc 'Define the default task directory. System default is ~/.tasks'
32
+ c.arg_name 'DIR'
33
+ c.flag [:t, :taskdir]
34
+
35
+ c.desc 'List all settings'
36
+ c.switch [:l, :list]
37
+
38
+ c.action do |global_options,options,args|
39
+ settings = Syctask::Settings.new
40
+ filter = [:general, :taskdir, :list]
41
+ options.keep_if {|key, value| filter.find_index(key)}
42
+ if options[:general]
43
+ settings.tasks(options[:general])
44
+ end
45
+
46
+ if options[:taskdir]
47
+ set_taskdir = true
48
+ puts options[:taskdir]
49
+ dir = File.expand_path(options[:taskdir]) unless options[:taskdir].empty?
50
+ if dir
51
+ unless File.exists? dir
52
+ puts sprintf("Directory %s doesn't exist!", dir).color(:red)
53
+ print sprintf("%s ", "Create it (Y/n)?").color(:red)
54
+ set_taskdir = gets.chomp == 'Y'
55
+ if set_taskdir
56
+ FileUtils.mkdir_p dir
57
+ puts sprintf("--> Created directory %s", File.expand_path(dir)).
58
+ color(:green)
59
+ end
60
+ end
61
+ if set_taskdir
62
+ File.write(Syctask::DEFAULT_TASKS_DIR, dir)
63
+ puts sprintf("--> Set %s as default task directory", dir).
64
+ color(:green)
65
+ end
66
+ end
67
+ end
68
+
69
+ if options[:list]
70
+ puts sprintf("Default task directory: #{global_options[:taskdir]}").
71
+ color(:green)
72
+ general_purpose_tasks = settings.read_tasks
73
+ puts sprintf("%d general purpose tasks", general_purpose_tasks.size).
74
+ color(:green)
75
+ general_purpose_tasks.each do |key, value|
76
+ puts sprintf(" + %s (%s)", key, value).color(:green)
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ desc 'Information about tasks and task directories'
85
+ command :info do |c|
86
+
87
+ c.desc 'Directory where to start searching the task'
88
+ c.arg_name 'DIR'
89
+ c.flag [:d, :dir]
90
+
91
+ c.desc 'Find task with the given ID'
92
+ c.arg_name 'ID'
93
+ c.flag [:i, :id], :must_match => /^\d+/
94
+
95
+ c.desc 'Show task directories'
96
+ c.switch [:t, :taskdir]
97
+
98
+ c.action do |global_options,options,args|
99
+ dir = options[:dir]
100
+ dir ||= "~"
101
+ dir = File.expand_path(dir)
102
+ help_now! "Directory #{dir} does not exists" unless File.exists? dir
103
+ id = options[:id]
104
+ if id
105
+ tasks = Syctask::get_files(dir, "#{id}.task")
106
+ tasks.each {|task| puts sprintf("%s", task).color(:green)}
107
+ puts sprintf("--> No task with ID %s found", id).
108
+ color(:red) if tasks.empty?
109
+ end
110
+ if options[:taskdir]
111
+ dirs = Syctask::get_task_dirs_and_count(dir)
112
+ task_count = 0
113
+ dirs.each do |key,value|
114
+ puts "#{key} (#{value})"
115
+ task_count += value
116
+ end
117
+ puts sprintf("--> %d task directories with %d tasks found",
118
+ dirs.length, task_count).color(:green)
119
+ end
120
+ end
121
+
122
+ end
123
+
20
124
  desc 'Create a new task'
21
125
  arg_name 'TASK_TITLE'
22
126
  command :new do |c|
@@ -27,11 +131,11 @@ command :new do |c|
27
131
 
28
132
  c.desc 'Follow-up date'
29
133
  c.arg_name 'FOLLOW-UP'
30
- c.flag [:f, :follow_up], :must_match => /\d{4}-\d{2}-\d{2}/
134
+ c.flag [:f, :follow_up], :must_match => /today|tomorrow|\d{4}-\d{2}-\d{2}/
31
135
 
32
136
  c.desc 'Due date'
33
137
  c.arg_name 'DUE'
34
- c.flag [:d, :due_date], :must_match => /\d{4}-\d{2}-\d{2}/
138
+ c.flag [:d, :due_date], :must_match => /today|tomorrow|\d{4}-\d{2}-\d{2}/
35
139
 
36
140
  c.desc 'Description of the task'
37
141
  c.arg_name 'DESCRIPTION'
@@ -46,6 +150,8 @@ command :new do |c|
46
150
  c.flag [:t, :tags], :must_match => /^\w+(?:,\w+)*/
47
151
 
48
152
  c.action do |global_options,options,args|
153
+ options[:follow_up] = extract_time(options[:f]) if options[:f]
154
+ options[:due_date] = extract_time(options[:d]) if options[:d]
49
155
  filter = [:tags, :description, :prio, :due_date, :follow_up,
50
156
  :note]
51
157
  options.keep_if {|key, value| filter.find_index(key)}
@@ -55,6 +161,7 @@ command :new do |c|
55
161
  end
56
162
  task_numbers = nil
57
163
  args.each do |title|
164
+ title.gsub!("\n","")
58
165
  task_number = @service.create(global_options[:t], options, title)
59
166
  add_task_to_plan @service.read(global_options[:t], task_number)
60
167
  if task_numbers.nil?
@@ -178,33 +285,50 @@ command :list do |c|
178
285
  end
179
286
  end
180
287
 
181
- desc 'Show planned tasks'
182
- arg_name 'DATE'
183
- command :showplan do |c|
288
+ desc 'Show statistics for time, count and optionally tags'
289
+ arg_name '[FROM[ TO]]'
290
+ command :statistics do |c|
184
291
 
185
- c.desc 'Print complete task'
186
- c.switch [:c, :complete]
292
+ c.desc 'Show statistics for tags'
293
+ c.switch [:t, :tags]
187
294
 
188
295
  c.action do |global_options,options,args|
189
- case args[0]
190
- when 'today'
191
- date = Time.now.strftime("%Y-%m-%d")
192
- when 'tomorrow'
193
- date = (Time.now + (60 * 60 * 24)).strftime("%Y-%m-%d")
194
- else
195
- if args[0] and args[0].match(/\d{4}-\d{2}-\d{2}/)
196
- date = args[0]
197
- elsif args[0].nil?
198
- date = Time.now.strftime("%Y-%m-%d")
296
+ from = extract_time(args[0],true) if args[0]
297
+ from ||= ""
298
+ to = extract_time(args[1],true) if args[1]
299
+ to = from unless to
300
+ stats = Syctask::Statistics.new
301
+ STDOUT.puts stats.report(Syctask::TASKS_LOG, from, to)
302
+ puts "tags not implemented yet" if options[:tags]
303
+ end
304
+
305
+ end
306
+
307
+ desc 'Edit task in editor'
308
+ arg_name 'ID'
309
+ command :edit do |c|
310
+
311
+ c.action do |global_options,options,args|
312
+ task = @service.read(global_options[:taskdir], args[0])
313
+ if task
314
+ task_file = "#{task.dir}/#{task.id}.task"
315
+ if File.exists? task_file
316
+ system "vi #{task_file}" if File.exists? task_file
317
+ else
318
+ puts sprintf("--> Task %s doesn't exist", task_file).color(:red)
199
319
  end
320
+ else
321
+ puts sprintf("--> Task with ID %s doesnt't exist", args[0]).color(:red)
200
322
  end
201
- count = 0
202
- @planner.get_tasks(date).each do |task|
203
- task.print_pretty(options[:complete])
204
- count += 1
205
- end
206
- STDOUT.puts sprintf("--> planned %d tasks for %s", count, date).
207
- color(:green)
323
+ end
324
+
325
+ end
326
+
327
+ desc 'Inspect tasks and edit, delete and mark as done'
328
+ command :inspect do |c|
329
+
330
+ c.action do |global_options,options,args|
331
+ @planner.inspect_tasks(@service.find(global_options[:t], options, false))
208
332
  end
209
333
 
210
334
  end
@@ -223,7 +347,7 @@ command :plan do |c|
223
347
 
224
348
  c.desc 'Move planned task to another day'
225
349
  c.arg_name 'DATE'
226
- c.flag [:m, :move], :must_match => /today|tomorrow|\d{4}-\d{2}-\d{2}/
350
+ c.flag [:m, :move],:must_match => /yesterday|today|tomorrow|\d{4}-\d{2}-\d{2}/
227
351
 
228
352
  c.desc 'Filter for ID'
229
353
  c.arg_name 'ID1,ID2,ID3|[<|=|>]ID'
@@ -376,13 +500,19 @@ command :prio do |c|
376
500
  c.arg_name 'ID1,ID2,ID3,...'
377
501
  c.flag [:o, :order], :must_match => /^\d+(?:,\d+)*/
378
502
 
503
+ c.desc 'Put task at specified position'
504
+ c.arg_name 'POS'
505
+ c.flag [:p, :position], :must_match => /first|FIRST|last|LAST|\d+/
506
+
379
507
  c.action do |global_options,options,args|
380
508
  time = extract_time(args[0])
381
509
  planner = Syctask::TaskPlanner.new
382
510
  if options[:order]
383
- ordered, rest = planner.order_tasks(time, options[:order].split(","))
384
- puts sprintf("--> put %d task%s into new order and %d appended to end",
385
- ordered, "#{'s' if ordered > 1}", rest).color(:green)
511
+ options[:position] ||= 0
512
+ ordered, rest, pos = planner.order_tasks(time, options[:order].split(","),
513
+ options[:position])
514
+ puts sprintf("--> put %d task%s out of %s into new order at position %d",
515
+ ordered, "#{'s' if ordered > 1}", rest, pos).color(:green)
386
516
  else
387
517
  planner.prioritize_tasks(time)
388
518
  end
@@ -390,43 +520,52 @@ command :prio do |c|
390
520
  end
391
521
 
392
522
  desc 'Start tracking a task'
393
- arg_name 'TASK_NUMBER|PLAN|TALK|PHONE|EMAIL'
523
+ arg_name "TASK_ID#{@general_purpose_tasks}"
394
524
  command :start do |c|
395
525
 
526
+ c.desc 'Show task timer'
527
+ c.switch [:t, :timer]
528
+
396
529
  c.desc 'List currently tracked task'
397
530
  c.switch [:l, :list]
398
531
 
399
- c.desc 'Number of planned task'
532
+ c.desc 'ID of planned task'
400
533
  c.switch [:p, :plan]
401
534
 
402
535
  c.action do |global_options,options,args|
536
+ if args.empty? and not options[:l] and not options[:p]
537
+ help_now! sprintf("%s", "no arguments and options given").color(:red)
538
+ end
403
539
  tracker = Syctask::TaskTracker.new
404
540
  if args[0]
405
541
  if options[:plan]
406
- task = @planner.get_tasks[args[0].to_i]
542
+ task = @planner.get_tasks(Time.now.strftime("%Y-%m-%d"),
543
+ {id: args[0]})[0]
407
544
  else
408
545
  task = @service.read(global_options[:taskdir], args[0])
409
546
  end
410
547
  help_now! sprintf("%s",
411
548
  "no task with id #{args[0]}").color(:red) unless task
412
- started, stopped = tracker.start(task)
549
+ started, stopped = tracker.start(task, options[:timer])
550
+ if stopped
551
+ puts sprintf("--> stopped %s",
552
+ "#{stopped.id} - #{stopped.title}").color(:yellow)
553
+ puts sprintf(" %s", "#{string_for_seconds(stopped.lead_time)}").
554
+ color(:yellow)
555
+ end
413
556
  if started
414
- puts sprintf("--> tracking %s",
557
+ puts sprintf("--> started %s",
415
558
  "#{task.id} - #{task.title}").color(:green)
416
559
  else
417
- puts sprintf("--> allready tracking %s",
560
+ puts sprintf("--> allready started %s",
418
561
  "#{task.id} - #{task.title}").color(:red)
419
562
  end
420
- if stopped
421
- puts sprintf("--> stopped %s",
422
- "#{stopped.id} - #{stopped.title}").color(:green)
423
- puts sprintf(" %s", "#{string_for_seconds(stopped.lead_time)}").color(:green)
424
- end
425
- end
563
+ end
426
564
  if options[:list]
427
565
  task = tracker.tracked_task
428
566
  puts sprintf("%s", "--> no task tracked").color(:red) unless task
429
- puts sprintf("%s", "--> #{task.id} - #{task.title}").color(:green) if task
567
+ puts sprintf("%s", "--> #{task.id} - #{task.title}").
568
+ color(:green) if task
430
569
  end
431
570
  end
432
571
  end
@@ -438,7 +577,8 @@ command :stop do |c|
438
577
  task = tracker.stop
439
578
  if task
440
579
  puts sprintf("--> stopped %s", "#{task.id} - #{task.title}").color(:green)
441
- puts sprintf(" %4s", "#{string_for_seconds(task.lead_time)}").color(:green)
580
+ puts sprintf(" %4s", "#{string_for_seconds(task.lead_time)}").
581
+ color(:green)
442
582
  else
443
583
  puts sprintf("--> %s", "no task tracked").color(:red)
444
584
  end
@@ -446,7 +586,7 @@ command :stop do |c|
446
586
  end
447
587
 
448
588
  desc 'Update the task'
449
- arg_name 'TASK_NUMBER'
589
+ arg_name 'ID'
450
590
  command :update do |c|
451
591
  c.desc 'Priority of the task, 1 highes priority'
452
592
  c.arg_name 'PRIO'
@@ -454,11 +594,15 @@ command :update do |c|
454
594
 
455
595
  c.desc 'Follow-up date'
456
596
  c.arg_name 'FOLLOW-UP'
457
- c.flag [:f, :follow_up], :must_match => /\d{4}-\d{2}-\d{2}/
597
+ c.flag [:f, :follow_up], :must_match => /today|tomorrow|\d{4}-\d{2}-\d{2}/
458
598
 
459
599
  c.desc 'Due date'
460
600
  c.arg_name 'DUE'
461
- c.flag [:d, :due_date], :must_match => /\d{4}-\d{2}-\d{2}/
601
+ c.flag [:d, :due_date], :must_match => /today|tomorrow|\d{4}-\d{2}-\d{2}/
602
+
603
+ c.desc 'Duration'
604
+ c.arg_name 'DURATION'
605
+ c.flag :duration, :must_match => /\d+/
462
606
 
463
607
  c.desc 'Description of the task'
464
608
  c.arg_name 'DESCRIPTION'
@@ -473,8 +617,10 @@ command :update do |c|
473
617
  c.flag [:t, :tags], :must_match => /^\w+(?:,\w+)*/
474
618
 
475
619
  c.action do |global_options,options,args|
476
- help_now! "TASK_NUMBER required" if args.empty?
477
- filter = [:tags, :description, :prio, :due_date, :follow_up,
620
+ help_now! "ID required" if args.empty?
621
+ options[:follow_up] = extract_time(options[:f]) if options[:f]
622
+ options[:due_date] = extract_time(options[:d]) if options[:d]
623
+ filter = [:tags, :description, :prio, :due_date, :follow_up, :duration,
478
624
  :note]
479
625
  options.keep_if {|key, value| filter.find_index(key) and value != nil}
480
626
 
@@ -482,15 +628,15 @@ command :update do |c|
482
628
 
483
629
  add_task_to_plan @service.read(global_options[:t], args[0]) if success
484
630
 
485
- STDOUT.puts sprintf("--> sucessfully updated task with TASK_NUMBER %s ",
631
+ STDOUT.puts sprintf("--> updated task %s ",
486
632
  args[0]).color(:green) if success
487
- STDOUT.puts sprintf("--> could not update task with TASK_NUMBER %s ",
633
+ STDOUT.puts sprintf("--> could not update task %s ",
488
634
  args[0]).color(:red) unless success
489
635
  end
490
636
  end
491
637
 
492
638
  desc 'Mark task as done'
493
- arg_name 'TASK_NUMBER'
639
+ arg_name 'ID'
494
640
  command :done do |c|
495
641
  c.desc 'Print task after marked as done'
496
642
  c.switch [:p, :print]
@@ -503,12 +649,12 @@ command :done do |c|
503
649
 
504
650
  c.action do |global_options,options,args|
505
651
  help_now! sprintf("%s",
506
- 'TASK_NUMBER is required').color(:red) if args.empty?
652
+ 'ID is required').color(:red) if args.empty?
507
653
  task = @service.read(global_options[:t], args[0])
508
- exit_now!("Task with TASK_NUMBER #{args[0]} does not exist") unless task
654
+ exit_now!("Task with ID #{args[0]} does not exist") unless task
509
655
  task.done(options[:note])
510
- @service.save(global_options[:t], task)
511
- STDOUT.puts sprintf("Marked task with TASK_NUMBER %d as done", args[0]).color(:green)
656
+ @service.save(task.dir, task)
657
+ STDOUT.puts sprintf("--> Marked task %d as done", args[0]).color(:green)
512
658
  task.print_pretty(options[:c]) if options[:p]
513
659
  end
514
660
  end
@@ -520,15 +666,33 @@ pre do |global,command,options,args|
520
666
  # Use skips_pre before a command to skip this block
521
667
  # on that command only
522
668
 
669
+ proceed = true
670
+
671
+ Syctask::check_environment
672
+
523
673
  @service = Syctask::TaskService.new
524
674
  @planner = Syctask::TaskPlanner.new
525
675
 
526
- dir = File.expand_path(global[:t])
527
- dir += "/" + global[:p] if global[:p]
528
- global[:taskdir] = global[:t] = dir.squeeze("/")
529
-
676
+ if command.name == :start and not args[0].nil? and @settings.
677
+ read_tasks[args[0].upcase]
678
+ global[:taskdir] = Syctask::SYC_DIR
679
+ args[0] = @settings.read_tasks[args[0].upcase].to_s
680
+ else
681
+ dir = File.expand_path(global[:t])
682
+ dir += "/" + global[:p] if global[:p]
683
+ global[:taskdir] = global[:t] = dir.squeeze("/")
684
+ end
530
685
 
531
- true
686
+ if command.name == :new and not File.exists? global[:taskdir]
687
+ puts sprintf("Directory %s doesn't exist!", global[:taskdir]).color(:red)
688
+ print sprintf("%s", "Create it (Y/n)? ").color(:red)
689
+ proceed = STDIN.gets.chomp == 'Y'
690
+ if proceed
691
+ FileUtils.mkdir_p global[:taskdir]
692
+ end
693
+ end
694
+
695
+ proceed
532
696
  end
533
697
 
534
698
  post do |global,command,options,args|
@@ -540,27 +704,41 @@ end
540
704
  on_error do |exception|
541
705
  # Error logic here
542
706
  # return false to skip default error handling
543
- true
707
+ if exception.to_s == "exit" and exception.status == 0
708
+ false
709
+ else
710
+ true
711
+ end
544
712
  end
545
713
 
546
- # Extracts the time out of a time string. Accepts 'today', 'tomorrow' or a date
547
- # in the form 'YYYY-MM-DD'. Returns the date contained in the time_string
548
- def extract_time(time_string)
714
+ # Extracts the time out of a time string. Accepts 'today', 'tomorrow',
715
+ # 'yesterday' or a date in the form 'YYYY-MM-DD'. Returns the date contained in
716
+ # the time_string or if time = true in a Time object
717
+ def extract_time(time_string,time=false)
549
718
  time_string = 'today' if time_string.nil?
550
719
  case time_string.downcase
551
720
  when 'today'
552
- date = Time.now.strftime("%Y-%m-%d")
721
+ date = Time.now
722
+ date = date.strftime("%Y-%m-%d") unless time
553
723
  when 'tomorrow'
554
- date = (Time.now + (60 * 60 * 24)).strftime("%Y-%m-%d")
724
+ date = Time.now + 60 * 60 * 24
725
+ date = date.strftime("%Y-%m-%d") unless time
726
+ when 'yesterday'
727
+ date = Time.now - (60 * 60 * 24)
728
+ date = date.strftime("%Y-%m-%d") unless time
555
729
  else
556
730
  if time_string.match(/\d{4}-\d{2}-\d{2}/)
557
731
  date = time_string
732
+ date = Time.xmlschema("#{time_string}T00:00:00") if time
558
733
  elsif nil
559
- date = Time.now.strftime("%Y-%m-%d")
734
+ date = Time.now
735
+ date = date.strftime("%Y-%m-%d") unless time
560
736
  else
561
- help_now! "Arguments may be 'today', 'tomorrow', YYYY-MM-DD or <RETURN>"
737
+ help_now! "Arguments may be 'yesterday', 'today', 'tomorrow', YYYY-MM-DD"+
738
+ " or <RETURN>"
562
739
  end
563
740
  end
741
+ date
564
742
  end
565
743
 
566
744
  # Add task to task plan