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 +159 -15
- data/bin/console_timer +75 -0
- data/bin/syctask +246 -68
- data/lib/syctask.rb +4 -1
- data/lib/syctask/environment.rb +427 -2
- data/lib/syctask/schedule.rb +84 -21
- data/lib/syctask/settings.rb +43 -0
- data/lib/syctask/statistics.rb +196 -0
- data/lib/syctask/task.rb +58 -2
- data/lib/syctask/task_planner.rb +94 -13
- data/lib/syctask/task_scheduler.rb +5 -1
- data/lib/syctask/task_service.rb +55 -15
- data/lib/syctask/task_tracker.rb +27 -17
- data/lib/syctask/times.rb +29 -0
- data/lib/syctask/version.rb +1 -1
- data/lib/syctime/time_util.rb +46 -7
- data/lib/sycutil/console_timer.rb +75 -0
- metadata +215 -136
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require_relative 'environment.rb'
|
3
|
+
|
4
|
+
# Syctask module implements functions for managing tasks
|
5
|
+
module Syctask
|
6
|
+
|
7
|
+
# Creates settings for syctask that are saved to files and retrieved when
|
8
|
+
# syctask is started
|
9
|
+
class Settings
|
10
|
+
|
11
|
+
# Creates general purpose tasks that can be tracked with syctask start. The
|
12
|
+
# general purpose files are saved to a file default_tasks in the syctask
|
13
|
+
# system directory
|
14
|
+
def tasks(tasks)
|
15
|
+
service = Syctask::TaskService.new
|
16
|
+
if File.exists? Syctask::DEFAULT_TASKS
|
17
|
+
general = YAML.load_file(Syctask::DEFAULT_TASKS)
|
18
|
+
else
|
19
|
+
general = {}
|
20
|
+
end
|
21
|
+
tasks.split(',').each do |task|
|
22
|
+
index = general.keys.find_index(task.upcase)
|
23
|
+
general[task.upcase] = service.create(Syctask::SYC_DIR,
|
24
|
+
{},
|
25
|
+
task.upcase) unless index
|
26
|
+
end
|
27
|
+
File.open(Syctask::DEFAULT_TASKS, 'w') {|f| YAML.dump(general, f)}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Retrieves the general purpose files from the default_tasks file in the
|
31
|
+
# syctask system directory
|
32
|
+
def read_tasks
|
33
|
+
if File.exists? Syctask::DEFAULT_TASKS and not \
|
34
|
+
File.read(Syctask::DEFAULT_TASKS).empty?
|
35
|
+
YAML.load_file(Syctask::DEFAULT_TASKS)
|
36
|
+
else
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'rainbow'
|
2
|
+
require_relative '../syctime/time_util.rb'
|
3
|
+
require_relative 'settings.rb'
|
4
|
+
|
5
|
+
include Syctime
|
6
|
+
|
7
|
+
module Syctask
|
8
|
+
|
9
|
+
# Creates statistics about the work and meeting times as well about the task
|
10
|
+
# processing
|
11
|
+
class Statistics
|
12
|
+
|
13
|
+
# Initializes the Statistics object with the general purpose tassk
|
14
|
+
def initialize
|
15
|
+
settings = Settings::new
|
16
|
+
tasks = settings.read_tasks
|
17
|
+
@general_purpose_tasks = tasks.nil? ? [] : tasks.keys
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a statistics report
|
21
|
+
def report(file, from="", to=from)
|
22
|
+
|
23
|
+
from, to, time_log, count_log = logs(file, from, to)
|
24
|
+
working_days = time_log["work"].count.to_s if time_log["work"]
|
25
|
+
working_days ||= "0"
|
26
|
+
value_size = {key: 0, total: 0, min: 0, max: 0, average: 0}
|
27
|
+
report_lines = {}
|
28
|
+
report = sprintf("%s to %s", "#{from.strftime("%Y-%m-%d")}".bright,
|
29
|
+
"#{to.strftime("%Y-%m-%d")}".bright) +
|
30
|
+
sprintf(" (%s working days)\n", working_days.bright) +
|
31
|
+
sprintf("%24s", "Total".bright) +
|
32
|
+
sprintf("%26s", "Min ".bright) +
|
33
|
+
sprintf("%26s", "Max ".bright) +
|
34
|
+
sprintf("%29s", "Average\n".bright)
|
35
|
+
report << sprintf("%s\n", "Time".bright)
|
36
|
+
time_log.each do |key,value|
|
37
|
+
total, min, max, average = stats(value)
|
38
|
+
total = Syctime::separated_time_string(total, ":")
|
39
|
+
min = Syctime::separated_time_string(min, ":")
|
40
|
+
max = Syctime::separated_time_string(max, ":")
|
41
|
+
average = Syctime::separated_time_string(average, ":")
|
42
|
+
set_max_value_sizes(key, total, min, max, average, value_size)
|
43
|
+
report_lines[key] = [total, min, max, average]
|
44
|
+
end
|
45
|
+
|
46
|
+
report << create_report_line_strings(report_lines, value_size)
|
47
|
+
|
48
|
+
report_lines = {}
|
49
|
+
value_size = {key: 0, total: 0, min: 0, max: 0, average: 0}
|
50
|
+
|
51
|
+
report << sprintf("%s\n", "Count".bright)
|
52
|
+
count_log.each do |key,value|
|
53
|
+
total, min, max, average = stats_count(value)
|
54
|
+
set_max_value_sizes(key, total, min, max, average, value_size)
|
55
|
+
report_lines[key] = [total, min, max, average]
|
56
|
+
end
|
57
|
+
|
58
|
+
report << create_report_line_strings(report_lines, value_size)
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates report line strings
|
63
|
+
def create_report_line_strings(lines, value_size)
|
64
|
+
report = ""
|
65
|
+
lines.each do |key, value|
|
66
|
+
total = value[0]
|
67
|
+
min = value[1]
|
68
|
+
max = value[2]
|
69
|
+
average = value[3]
|
70
|
+
report << report_line(key, total, min, max, average, value_size)
|
71
|
+
end
|
72
|
+
report
|
73
|
+
end
|
74
|
+
|
75
|
+
# Determines the max string size of the values
|
76
|
+
def set_max_value_sizes(key, total, min, max, average, value_size)
|
77
|
+
value_size[:key] = [value_size[:key], key.size].max
|
78
|
+
value_size[:total] = [value_size[:total], total.size].max
|
79
|
+
value_size[:min] = [value_size[:min], min.size].max
|
80
|
+
value_size[:max] = [value_size[:max], max.size].max
|
81
|
+
value_size[:average] = [value_size[:average], average.size].max
|
82
|
+
end
|
83
|
+
|
84
|
+
# Creates a report line for the report
|
85
|
+
def report_line(key, total, min, max, average, sizes={})
|
86
|
+
color = :green if key == 'task'
|
87
|
+
color = :yellow if key == 'unplanned' or @general_purpose_tasks.
|
88
|
+
find_index(key)
|
89
|
+
key = key[0..8]
|
90
|
+
report = sprintf(" %s#{' '*(10-key.size)}", key).color(color)
|
91
|
+
report << sprintf("%#{sizes[:total]}s#{' '*(10-sizes[:total]+8)}", total).
|
92
|
+
color(color)
|
93
|
+
report << sprintf("%#{sizes[:min]}s#{' '*(10-sizes[:min]+8)}", min).
|
94
|
+
color(color)
|
95
|
+
report << sprintf("%#{sizes[:max]}s#{' '*(10-sizes[:max]+8)}", max).
|
96
|
+
color(color)
|
97
|
+
report << sprintf("%#{sizes[:average]}s\n", average).color(color)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Calculates the average of a task processing, work or meeting time
|
101
|
+
def average(data)
|
102
|
+
sum = 0
|
103
|
+
data.each do |d|
|
104
|
+
sum += time_for_string(d[1]) - time_for_string(d[0])
|
105
|
+
end
|
106
|
+
sum / data.size
|
107
|
+
end
|
108
|
+
|
109
|
+
# Calculates the minimum duration of task processing, work or meeting time
|
110
|
+
def min(data)
|
111
|
+
diffs = []
|
112
|
+
data.each do |d|
|
113
|
+
diffs << time_for_string(d[1]) - time_for_string(d[0])
|
114
|
+
end
|
115
|
+
diffs.min
|
116
|
+
end
|
117
|
+
|
118
|
+
# Calculates the maximum duration of task processing, work or meeting time
|
119
|
+
def max(data)
|
120
|
+
diffs = []
|
121
|
+
data.each do |d|
|
122
|
+
diffs << time_for_string(d[1]) - time_for_string(d[0])
|
123
|
+
end
|
124
|
+
diffs.max
|
125
|
+
end
|
126
|
+
|
127
|
+
# Calculates total, min, max and average time of task processing, work or
|
128
|
+
# meeting time
|
129
|
+
def stats(data)
|
130
|
+
diffs = []
|
131
|
+
data.each do |d|
|
132
|
+
diffs << time_for_string(d[1]) - time_for_string(d[0])
|
133
|
+
end
|
134
|
+
[diffs.inject(:+), diffs.min, diffs.max, diffs.inject(:+) / diffs.size]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Calculates the total, min, max and average count of task processing,
|
138
|
+
# creation, update, done, delete
|
139
|
+
def stats_count(data)
|
140
|
+
count = []
|
141
|
+
data.each do |key,value|
|
142
|
+
count << value.to_i
|
143
|
+
end
|
144
|
+
[count.inject(:+), count.min, count.max, count.inject(:+) / count.size]
|
145
|
+
end
|
146
|
+
|
147
|
+
# Retrieves the log entries from the log file
|
148
|
+
def logs(file, from="", to=from)
|
149
|
+
times = []
|
150
|
+
time_data = {}
|
151
|
+
time_types = %w{work meeting task}
|
152
|
+
time_types << @general_purpose_tasks
|
153
|
+
time_types.flatten!
|
154
|
+
count_data = {}
|
155
|
+
count_types = %w{meeting task create done update delete}
|
156
|
+
count_types << @general_purpose_tasks
|
157
|
+
count_types.flatten!
|
158
|
+
IO.readlines(file).each do |line|
|
159
|
+
values = line.split(";")
|
160
|
+
time = time_for_string(values[4])
|
161
|
+
times << time
|
162
|
+
next if values[0] == "start"
|
163
|
+
unless from == ""
|
164
|
+
next unless Syctime::date_between?(time, from, to)
|
165
|
+
end
|
166
|
+
values[0] = values[3] if @general_purpose_tasks.find_index(values[3])
|
167
|
+
values[0] = "task" if values[0] == "stop"
|
168
|
+
if count_types.find_index(values[0])
|
169
|
+
time = time.strftime("%Y-%m-%d")
|
170
|
+
count_data[values[0]] = {} unless count_data[values[0]]
|
171
|
+
count_data[values[0]][time] = 0 unless count_data[values[0]][time]
|
172
|
+
count_data[values[0]][time] += 1
|
173
|
+
if @general_purpose_tasks.find_index(values[0])
|
174
|
+
count_data['unplanned'] = {} unless count_data['unplanned']
|
175
|
+
count_data['unplanned'][time] = 0 unless \
|
176
|
+
count_data['unplanned'][time]
|
177
|
+
count_data['unplanned'][time] += 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
if time_types.find_index(values[0])
|
181
|
+
time_data[values[0]] = [] unless time_data[values[0]]
|
182
|
+
time_data[values[0]] << [values[4],values[5]]
|
183
|
+
if @general_purpose_tasks.find_index(values[0])
|
184
|
+
time_data['unplanned'] = [] unless time_data['unplanned']
|
185
|
+
time_data['unplanned'] << [values[4],values[5]]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
from = times.min if from == ""
|
190
|
+
to = times.max if to == ""
|
191
|
+
[from, to, time_data, count_data]
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
data/lib/syctask/task.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'rainbow'
|
3
3
|
require_relative 'evaluator'
|
4
|
+
require_relative 'environment.rb'
|
5
|
+
require_relative 'task_tracker.rb'
|
4
6
|
|
5
7
|
# Syctask provides functions for managing tasks in a task list
|
6
8
|
module Syctask
|
@@ -25,9 +27,11 @@ module Syctask
|
|
25
27
|
# ID of the task
|
26
28
|
attr_reader :id
|
27
29
|
# Duration specifies the planned time for processing the task
|
28
|
-
|
30
|
+
attr_reader :duration
|
31
|
+
# Remaining time is the duration subtracted by the lead time since last plan
|
32
|
+
attr_reader :remaining
|
29
33
|
# Lead time is the time this task has been processed
|
30
|
-
|
34
|
+
attr_reader :lead_time
|
31
35
|
# Creation date
|
32
36
|
attr_reader :creation_date
|
33
37
|
# Update date
|
@@ -45,6 +49,13 @@ module Syctask
|
|
45
49
|
@options = options
|
46
50
|
@options[:note] =
|
47
51
|
"#{@creation_date}\n#{@options[:note]}\n" if @options[:note]
|
52
|
+
if @options[:follow_up] or @options[:due_date]
|
53
|
+
@duration = 2 * 15 * 60
|
54
|
+
@remaining = 2 * 15 * 60
|
55
|
+
else
|
56
|
+
@duration = 0
|
57
|
+
@remaining = 0
|
58
|
+
end
|
48
59
|
@id = id
|
49
60
|
end
|
50
61
|
|
@@ -69,6 +80,11 @@ module Syctask
|
|
69
80
|
# supplemented with the new values and not overridden.
|
70
81
|
def update(options)
|
71
82
|
@update_date = Time.now.strftime("%Y-%m-%d - %H:%M:%S")
|
83
|
+
if options[:duration]
|
84
|
+
set_duration(options.delete(:duration).to_i * 15 * 60)
|
85
|
+
elsif options[:follow_up] or options[:due_date]
|
86
|
+
set_duration(2 * 15 * 60) if @duration.nil?
|
87
|
+
end
|
72
88
|
options.keys.each do |key|
|
73
89
|
new_value = options[key]
|
74
90
|
|
@@ -95,6 +111,28 @@ module Syctask
|
|
95
111
|
!@updated_date.nil?
|
96
112
|
end
|
97
113
|
|
114
|
+
# Sets the duration that this task is planned for processing. Assigns to
|
115
|
+
# remaining the duration time
|
116
|
+
def set_duration(duration)
|
117
|
+
@duration = duration
|
118
|
+
@remaining = duration
|
119
|
+
end
|
120
|
+
|
121
|
+
# Updates the lead time. Adds the lead time to @lead_time and calculates
|
122
|
+
# @remaining
|
123
|
+
def update_lead_time(lead_time)
|
124
|
+
if @lead_time
|
125
|
+
@lead_time += lead_time
|
126
|
+
else
|
127
|
+
@lead_time = lead_time
|
128
|
+
end
|
129
|
+
if @remaining
|
130
|
+
@remaining -= lead_time
|
131
|
+
else
|
132
|
+
@remaining = @duration.to_i - lead_time
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
98
136
|
# Marks the task as done. When done than the done date is set. Optionally a
|
99
137
|
# note can be provided.
|
100
138
|
def done(note="")
|
@@ -102,6 +140,7 @@ module Syctask
|
|
102
140
|
if note
|
103
141
|
options[:note] = "#{@done_date}\n#{note}\n#{@options[:note]}"
|
104
142
|
end
|
143
|
+
Syctask::log_task("done", self)
|
105
144
|
end
|
106
145
|
|
107
146
|
# Checks if this task is done. Returns true if done otherwise false
|
@@ -109,6 +148,23 @@ module Syctask
|
|
109
148
|
!@done_date.nil?
|
110
149
|
end
|
111
150
|
|
151
|
+
# Checks if task is scheduled for today. Returns true if follow up or due
|
152
|
+
# date is today otherwise false.
|
153
|
+
def today?
|
154
|
+
evaluator = Evaluator.new
|
155
|
+
today = Time.now.strftime("%Y-%m-%d")
|
156
|
+
evaluator.compare_dates(@options[:follow_up], today) or \
|
157
|
+
evaluator.compare_dates(@options[:due_date], today)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Checks whether the task is currently tracked. Returns true if so otherwise
|
161
|
+
# false
|
162
|
+
def tracked?
|
163
|
+
tracker = Syctask::TaskTracker.new
|
164
|
+
task = tracker.tracked_task
|
165
|
+
task.nil? ? false : task == self
|
166
|
+
end
|
167
|
+
|
112
168
|
# Compares the provided elements in the filter with the correspondent
|
113
169
|
# elements in the task. When all comparissons match than true is returned.
|
114
170
|
# If one comparisson does not match false is returned. If filter is empty
|
data/lib/syctask/task_planner.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'rainbow'
|
2
3
|
require_relative '../sycutil/console.rb'
|
3
4
|
require_relative 'task_service.rb'
|
5
|
+
require_relative 'environment.rb'
|
4
6
|
|
5
7
|
module Syctask
|
6
8
|
# String that is prompted during planning
|
7
9
|
PROMPT_STRING = '(a)dd, (c)omplete, (s)kip, (q)uit: '
|
10
|
+
# String that is prompted during inspect
|
11
|
+
INSPECT_STRING = '(e)dit, (d)one, de(l)ete, (p)lan, (c)omplete, (s)kip, '+
|
12
|
+
'(q)uit: '
|
8
13
|
# String that is prompted during prioritization
|
9
14
|
PRIORITIZE_STRING = 'Task 1 has (h)igher or (l)ower priority, or (q)uit: '
|
10
15
|
|
@@ -12,7 +17,7 @@ module Syctask
|
|
12
17
|
# be prioritized to determine the most to the least important tasks.
|
13
18
|
class TaskPlanner
|
14
19
|
# The task where the planned tasks are saved to
|
15
|
-
WORK_DIR = File.expand_path("~/.tasks")
|
20
|
+
WORK_DIR = Syctask::SYC_DIR #File.expand_path("~/.tasks")
|
16
21
|
|
17
22
|
# Creates a new TaskPlanner
|
18
23
|
def initialize
|
@@ -41,9 +46,71 @@ module Syctask
|
|
41
46
|
choice = @console.prompt PROMPT_STRING
|
42
47
|
case choice
|
43
48
|
when 'a'
|
44
|
-
|
45
|
-
duration
|
46
|
-
|
49
|
+
duration = 0
|
50
|
+
until duration > 0
|
51
|
+
print "Duration (1 = 15 minutes, RETURN defaults to 30 minutes): "
|
52
|
+
answer = gets.chomp
|
53
|
+
duration = answer.empty? ? 2 : answer.to_i
|
54
|
+
end
|
55
|
+
task.set_duration(units_to_time(duration))
|
56
|
+
task.options[:follow_up] = date
|
57
|
+
@service.save(task.dir, task)
|
58
|
+
planned << task
|
59
|
+
count += 1
|
60
|
+
when 'c'
|
61
|
+
re_display = true
|
62
|
+
redo
|
63
|
+
when 's'
|
64
|
+
#do nothing
|
65
|
+
when 'q'
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
save_tasks(planned)
|
70
|
+
count
|
71
|
+
end
|
72
|
+
|
73
|
+
# Inspect allows to edit, delete and mark tasks as done
|
74
|
+
def inspect_tasks(tasks, date=Time.now.strftime("%Y-%m-%d"))
|
75
|
+
already_planned = self.get_tasks(date)
|
76
|
+
count = 0
|
77
|
+
re_display = false
|
78
|
+
planned = []
|
79
|
+
tasks.each do |task|
|
80
|
+
next if already_planned.find_index {|t| t == task}
|
81
|
+
unless re_display
|
82
|
+
task.print_pretty
|
83
|
+
else
|
84
|
+
task.print_pretty(true)
|
85
|
+
re_display = false
|
86
|
+
end
|
87
|
+
choice = @console.prompt INSPECT_STRING
|
88
|
+
case choice
|
89
|
+
when 'e'
|
90
|
+
task_file = "#{task.dir}/#{task.id}.task"
|
91
|
+
system "vi #{task_file}" if File.exists? task_file
|
92
|
+
redo
|
93
|
+
when 'd'
|
94
|
+
puts "Enter a note or hit <RETURN>"
|
95
|
+
note = gets.chomp
|
96
|
+
task.done(note)
|
97
|
+
@service.save(task.dir, task)
|
98
|
+
STDOUT.puts sprintf("--> Marked task %d as done",
|
99
|
+
task.id).color(:green)
|
100
|
+
when 'l'
|
101
|
+
print "Confirm delete task (Y/n)? "
|
102
|
+
answer = gets.chomp
|
103
|
+
count = @service.delete(task.dir, {id: task.id.to_s}) if answer == "Y"
|
104
|
+
puts sprintf("--> Deleted %d task%s",
|
105
|
+
count, count == 1 ? "" : "s").color(:green)
|
106
|
+
when 'p'
|
107
|
+
duration = 0
|
108
|
+
until duration > 0
|
109
|
+
print "Duration (1 = 15 minutes, RETURN defaults to 30 minutes): "
|
110
|
+
answer = gets.chomp
|
111
|
+
duration = answer.empty? ? 2 : answer.to_i
|
112
|
+
end
|
113
|
+
task.set_duration(units_to_time(duration))
|
47
114
|
task.options[:follow_up] = date
|
48
115
|
@service.save(task.dir, task)
|
49
116
|
planned << task
|
@@ -61,19 +128,25 @@ module Syctask
|
|
61
128
|
count
|
62
129
|
end
|
63
130
|
|
64
|
-
# Order tasks in the provided IDs sequence at the specified date. If not
|
65
|
-
# IDs are provided than rest of tasks is appended to the end of the plan.
|
66
|
-
#
|
67
|
-
|
131
|
+
# Order tasks in the provided IDs sequence at the specified date. If not all
|
132
|
+
# IDs are provided than rest of tasks is appended to the end of the plan. If
|
133
|
+
# a position (last, first and a number) is provided the ordered tasks are
|
134
|
+
# inserted at the specified position. Returns the count of ordered tasks,
|
135
|
+
# the count of the rest of the tasks and the position where the ordered
|
136
|
+
# tasks have been inserted.
|
137
|
+
def order_tasks(date, ids, pos=0)
|
68
138
|
tasks = get_tasks(date)
|
139
|
+
pos = "0" if pos.class == String and pos.downcase == 'first'
|
140
|
+
pos = tasks.size.to_s if pos.class == String and pos.downcase == 'last'
|
69
141
|
ordered = []
|
70
142
|
ids.each do |id|
|
71
143
|
index = tasks.find_index {|t| t.id == id.to_i}
|
72
144
|
ordered << tasks.delete_at(index) if index
|
73
145
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
146
|
+
pos = [pos.to_i.abs,tasks.size].min
|
147
|
+
tasks.insert(pos, ordered)
|
148
|
+
save_tasks(tasks.flatten!, true)
|
149
|
+
[ordered.size, tasks.size, pos]
|
77
150
|
end
|
78
151
|
|
79
152
|
# Prioritize tasks by pair wise comparisson. Each task is compared to the
|
@@ -119,13 +192,15 @@ module Syctask
|
|
119
192
|
save_tasks(planned, true)
|
120
193
|
end
|
121
194
|
|
122
|
-
# Moves the specified tasks to the specified date.
|
123
|
-
#
|
195
|
+
# Moves the specified tasks to the specified date. Sets the remaining timer
|
196
|
+
# to at least 15 minutes and sets the duration to the remaining timer's
|
197
|
+
# values. Returns the count of moved files
|
124
198
|
def move_tasks(filter={}, from_date=Time.now.strftime("%Y-%m-%d"), to_date)
|
125
199
|
return 0 if from_date == to_date
|
126
200
|
moved = get_tasks(from_date, filter)
|
127
201
|
moved.each do |task|
|
128
202
|
task.options[:follow_up] = to_date
|
203
|
+
task.set_duration([task.remaining, 900].max)
|
129
204
|
@service.save(task.dir, task)
|
130
205
|
end
|
131
206
|
add_tasks(moved, to_date)
|
@@ -170,6 +245,12 @@ module Syctask
|
|
170
245
|
|
171
246
|
private
|
172
247
|
|
248
|
+
# Calculates the time for time units. One time unit equals to 900 seconds or
|
249
|
+
# 15 minutes. The return value is in seconds
|
250
|
+
def units_to_time(units)
|
251
|
+
units * 15 * 60
|
252
|
+
end
|
253
|
+
|
173
254
|
# Creates a file where the planned tasks are saved to
|
174
255
|
def make_todo_today_file(date)
|
175
256
|
file_name = Time.now.strftime("#{date}_planned_tasks")
|