syc-task 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ require_relative 'times.rb'
2
+
3
+ module Syctask
4
+
5
+ class Meeting
6
+
7
+ attr_accessor :starts
8
+ attr_accessor :ends
9
+ attr_accessor :title
10
+ attr_accessor :tasks
11
+
12
+ # Sets the busy time for the schedule. The busy times have to be provided
13
+ # as hh:mm-hh:mm. Optionally a title for the busy time can be provided
14
+ def initialize(time, title="", tasks=[])
15
+ @starts = Syctask::Times.new(time[0..1])
16
+ @ends = Syctask::Times.new(time[2..3])
17
+ @title = title
18
+ @tasks = tasks
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,335 @@
1
+ require_relative 'times.rb'
2
+ require_relative 'meeting.rb'
3
+
4
+ module Syctask
5
+
6
+ # Schedule represents a working day with a start and end time, meeting times
7
+ # and titles and tasks. Tasks can also be associated to meetings as in an
8
+ # agenda.
9
+ # Invokation example
10
+ # work = ["8","30","18","45"]
11
+ # busy = [["9","0","10","0"],["11","30","12","15"]]
12
+ # titles = ["Ruby class room training","Discuss Ruby"]
13
+ # tasks = [task1,task2,task3,task4,task5,task6]
14
+ # schedule = Syctask::Schedule.new(work,busy,titles,tasks)
15
+ # schedule.graph.each {|output| puts output}
16
+ #
17
+ # This will create following output
18
+ # Meetings
19
+ # --------
20
+ # A - Ruby class room training
21
+ # B - Discuss Ruby
22
+ #
23
+ # A B
24
+ # xxoo/////xxx|-////oooooxoooo|---|---|---|---|
25
+ # 8 9 10 11 12 13 14 15 16 17 18 19
26
+ # 1 2 3 4 5
27
+ # 6
28
+ #
29
+ # Tasks
30
+ # -----
31
+ # 0 - 1: task1
32
+ # 1 - 2: task2
33
+ # 2 - 3: task3
34
+ # 3 - 4: task4
35
+ # 4 - 5: task5
36
+ # 5 - 6: task6
37
+ #
38
+ # Subsequent tasks are are displayed in the graph alternating with x and o.
39
+ # Meetings are indicated with / and the start is marked with A, B and so on.
40
+ # Task IDs are shown below the graph. The graph will be printed colored.
41
+ # Meetings in red, free times in green and tasks in blue. The past time is
42
+ # shown in black.
43
+ class Schedule
44
+ # Color of meetings
45
+ BUSY_COLOR = :red
46
+ # Color of free times
47
+ FREE_COLOR = :green
48
+ # Color of tasks
49
+ WORK_COLOR = :blue
50
+ # If tasks cannot be assigned to the working time this color is used
51
+ UNSCHEDULED_COLOR = :yellow
52
+ # Regex scans tasks and free times in the graph
53
+ GRAPH_PATTERN = /[\|-]+|\/+|[xo]+/
54
+ # Regex scans meetings in the graph
55
+ BUSY_PATTERN = /\/+/
56
+ # Regex scans free times in the graph
57
+ FREE_PATTERN = /[\|-]+/
58
+ # Regex scans tasks in the graph
59
+ WORK_PATTERN = /[xo]+/
60
+
61
+ # Start time of working day
62
+ attr_reader :starts
63
+ # End time of working day
64
+ attr_reader :ends
65
+ # Meetings assigned to the work time
66
+ attr_accessor :meetings
67
+ # Tasks assigned to the work time
68
+ attr_accessor :tasks
69
+
70
+ # Creates a new Schedule and initializes work time, busy times, titles and
71
+ # tasks. Work time is mandatory, busy times, titles and tasks are optional.
72
+ # Values have to be provided as
73
+ # * work time: [start_hour, start_minute, end_hour, end_minute]
74
+ # * busy time: [[start_hour, start_minute, end_hour, end_minute],[...]]
75
+ # * titles: [title,...]
76
+ # * tasks: [task,...]
77
+ def initialize(work_time, busy_time=[], titles=[], tasks=[])
78
+ @starts = Syctask::Times.new([work_time[0], work_time[1]])
79
+ @ends = Syctask::Times.new([work_time[2], work_time[3]])
80
+ @meetings = []
81
+ titles ||= []
82
+ busy_time.each.with_index do |busy,index|
83
+ title = titles[index] ? titles[index] : "Meeting #{index}"
84
+ @meetings << Syctask::Meeting.new(busy, title)
85
+ end
86
+ @tasks = tasks
87
+ end
88
+
89
+ # Sets the assignments containing tasks that are assigned to meetings.
90
+ # Returns true if succeeds
91
+ def assign(assignments)
92
+ assignments.each do |assignment|
93
+ number = assignment[0].upcase.ord - "A".ord
94
+ return false if number < 0 or number > @meetings.size
95
+ assignment[1].split(',').each do |index|
96
+ @meetings[number].tasks << @tasks[index.to_i] if @tasks[index.to_i]
97
+ end
98
+ @meetings[number].tasks.uniq!
99
+ end
100
+ true
101
+ end
102
+
103
+ # Creates a meeting list for printing. Returns the meeting list
104
+ def meeting_list
105
+ list = sprintf("%s", "Meetings\n").color(:red)
106
+ list << sprintf("%s", "--------\n").color(:red)
107
+ meeting_number = "A"
108
+ @meetings.each do |meeting|
109
+ list << sprintf("%s - %s\n", meeting_number, meeting.title).color(:red)
110
+ meeting_number.next!
111
+ meeting.tasks.each do |task|
112
+ task_color = task.done? ? :green : :blue
113
+ list << sprintf("%5s - %s\n", task.id, task.title).color(task_color)
114
+ end
115
+ end
116
+ list
117
+ end
118
+
119
+ # Creates a meeting caption and returns it for printing
120
+ def meeting_caption
121
+ work_time, meeting_times = get_times
122
+ caption = ""
123
+ meeting_number = "A"
124
+ meeting_times.each do |times|
125
+ caption << ' ' * (times[0] - caption.size) + meeting_number
126
+ meeting_number.next!
127
+ end
128
+ sprintf("%s", caption).color(:red)
129
+ end
130
+
131
+ # Creates the time caption for the time line
132
+ def time_caption
133
+ work_time = get_times[0]
134
+ caption = ""
135
+ work_time[0].upto(work_time[1]) do |time|
136
+ caption << time.to_s + (time < 9 ? ' ' * 3 : ' ' * 2)
137
+ end
138
+ sprintf("%s", caption)
139
+ end
140
+
141
+ # graph first creates creates the time line. Then the busy times are added.
142
+ # After that the tasks are added to the time line and the task caption and
143
+ # task list is created.
144
+ # graph returns the graph, task caption, task list and meeting list
145
+ # * time line
146
+ # * add meetings to time line
147
+ # * add tasks to time line
148
+ # * create task caption
149
+ # * create task list
150
+ # * create meeting caption
151
+ # * create meeting list
152
+ # * return time line, task caption, task list, meeting caption and meeting
153
+ # list
154
+ def graph
155
+ work_time, meeting_times = get_times
156
+ time_line = "|---" * (work_time[1]-work_time[0]) + "|"
157
+ meeting_times.each do |time|
158
+ time_line[time[0]..time[1]] = '/' * (time[1] - time[0]+1)
159
+ end
160
+
161
+ task_list, task_caption = assign_tasks_to_graph(time_line)
162
+
163
+ [meeting_list, meeting_caption,
164
+ colorize(time_line), time_caption,
165
+ task_caption, task_list]
166
+ end
167
+
168
+ private
169
+
170
+ # Colors the time line free time green, busy time red and tasks blue. The
171
+ # past time is colored black
172
+ def colorize(time_line)
173
+ time_line, future = split_time_line(time_line)
174
+ future.scan(GRAPH_PATTERN) do |part|
175
+ time_line << sprintf("%s", part).color(BUSY_COLOR) unless part.scan(BUSY_PATTERN).empty?
176
+ time_line << sprintf("%s", part).color(FREE_COLOR) unless part.scan(FREE_PATTERN).empty?
177
+ time_line << sprintf("%s", part).color(WORK_COLOR) unless part.scan(WORK_PATTERN).empty?
178
+ end if future
179
+ time_line
180
+ end
181
+
182
+ # Splits the time line at the current time. Returning the past part and the
183
+ # future part.
184
+ def split_time_line(time_line)
185
+ time = Time.now
186
+ offset = (time.hour - @starts.h) * 4 + time.min.div(15)
187
+ past = time_line.slice(0,offset)
188
+ future = time_line.slice(offset, time_line.size - offset)
189
+ [past, future]
190
+ end
191
+
192
+ # Assigns the tasks to the timeline in alternation x and o subsequent tasks.
193
+ # Returns the task list and the task caption
194
+ def assign_tasks_to_graph(time_line)
195
+ unscheduled_tasks = []
196
+ signs = ['x','o']
197
+ positions = {}
198
+ position = 0
199
+ unassigned_tasks.each.with_index do |task, index|
200
+ duration = task.duration.to_i
201
+ free_time = scan_free(time_line, duration, position)
202
+ position = free_time[0]
203
+ if position.nil?
204
+ unscheduled_tasks << task
205
+ next
206
+ end
207
+ time_line[position..(position + duration-1)] =
208
+ signs[index%2] * duration
209
+ positions[position] = task.id
210
+ end
211
+
212
+ max_id_size = 1
213
+ @tasks.each {|task| max_id_size = [task.id.to_s.size, max_id_size].max}
214
+ max_ord_size = (@tasks.size - 1).to_s.size
215
+
216
+ task_list = sprintf("%s", "Tasks\n").color(:blue)
217
+ task_list << sprintf("%s", "-----\n").color(:blue)
218
+ @tasks.each.with_index do |task, i|
219
+ if task.done?
220
+ color = :green
221
+ elsif unscheduled_tasks.find_index(task)
222
+ color = UNSCHEDULED_COLOR
223
+ else
224
+ color = WORK_COLOR
225
+ end
226
+ task_list << sprintf("%#{max_ord_size}d: %#{max_id_size}s - %s\n", i, task.id, task.title).
227
+ color(color)
228
+ end
229
+
230
+ task_caption = ""
231
+ create_caption(positions).each do |caption|
232
+ task_caption << sprintf("%s\n", caption).color(WORK_COLOR)
233
+ end
234
+
235
+ [task_list, task_caption]
236
+
237
+ end
238
+
239
+ # creates the caption of the graph with hours in 1 hour steps and task IDs
240
+ # that indicate where in the schedule a task is scheduled.
241
+ def create_caption(positions)
242
+ counter = 0
243
+ lines = [""]
244
+ positions.each do |position,id|
245
+ line_id = next_line(position,lines,counter)
246
+ legend = ' ' * [0, position - lines[line_id].size].max + id.to_s
247
+ lines[line_id] += legend
248
+ counter += 1
249
+ end
250
+ lines
251
+ end
252
+
253
+ # Creates a new line if the the task ID in the caption would override the
254
+ # task ID of a previous task. The effect is shown below
255
+ # |xx-|//o|x--|
256
+ # 8 9 10 10
257
+ # 10 101
258
+ # 11 2
259
+ # position is the position (time) within the schedule
260
+ # lines is the available ID lines (above we have 2 ID lines)
261
+ # counter is the currently displayed line. IDs are displayed alternating in
262
+ # each line, when we have 2 lines IDs will be printed in line 1,2,1,2...
263
+ def next_line(position, lines, counter)
264
+ line = lines[counter%lines.size]
265
+ return counter%lines.size if line.size == 0 or line.size < position - 1
266
+ lines.each.with_index do |line, index|
267
+ return index if line.size < position - 1
268
+ end
269
+ lines << ""
270
+ return lines.size - 1
271
+ end
272
+
273
+ # Scans the schedule for free time where a task can be added to. Count
274
+ # specifies the length of the free time and the position where to start
275
+ # scanning within the graph
276
+ def scan_free(graph, count, position)
277
+ pattern = /(?!\/)[\|-]{#{count}}(?<=-|\||\/)/
278
+
279
+ positions = []
280
+ index = position
281
+ while index and index < graph.size
282
+ index = graph.index(pattern, index)
283
+ if index
284
+ positions << index
285
+ index += 1
286
+ end
287
+ end
288
+ positions
289
+ end
290
+
291
+ # Returns the tasks that are not assigned to meetings
292
+ def unassigned_tasks
293
+ assigned = []
294
+ @meetings.each do |meeting|
295
+ assigned << meeting.tasks
296
+ end
297
+ assigned.flatten!
298
+
299
+ unassigned = []
300
+ unassigned << @tasks
301
+ unassigned.flatten.delete_if {|task| assigned.find_index(task)}
302
+ end
303
+
304
+ public
305
+
306
+ # Retrieves the work and busy times transformed to the time line scale
307
+ def get_times
308
+ work_time = [@starts.h, @ends.round_up]
309
+ meeting_times = []
310
+ @meetings.each do |meeting|
311
+ meeting_time = Array.new(2)
312
+ meeting_time[0] = hour_offset(@starts.h, meeting.starts.h) +
313
+ minute_offset(meeting.starts.m)
314
+ meeting_time[1] = hour_offset(@starts.h, meeting.ends.h) +
315
+ minute_offset(meeting.ends.m)
316
+ meeting_times << meeting_time
317
+ end if @meetings
318
+
319
+ times = [work_time, meeting_times]
320
+ end
321
+
322
+ private
323
+
324
+ # Transposes a time hour to a graph hour
325
+ def hour_offset(starts, ends)
326
+ (ends - starts) * 4
327
+ end
328
+
329
+ # Transposes a time minute to a graph minute
330
+ def minute_offset(minutes)
331
+ minutes.to_i.div(15)
332
+ end
333
+
334
+ end
335
+ end
@@ -0,0 +1,111 @@
1
+ require 'fileutils'
2
+ require_relative '../sycutil/console.rb'
3
+ require_relative 'task_service.rb'
4
+
5
+ module Syctask
6
+ PROMPT_STRING = '(a)dd, (c)omplete, (s)kip, (q)uit: '
7
+
8
+ class TaskPlanner
9
+ WORK_DIR = File.expand_path("~/.tasks")
10
+
11
+ def initialize
12
+ @console = Sycutil::Console.new
13
+ @service = TaskService.new
14
+ make_todo_today_file(Time.now.strftime("%Y-%m-%d"))
15
+ end
16
+
17
+ # List each task and prompt the user whether to add the task to the planned
18
+ # tasks. The user doesn't specify a duration for the task operation the
19
+ # duration will be set to 30 minutes which equals two time chunks. The
20
+ # count of planned tasks is returned
21
+ def plan_tasks(tasks, date=Time.now.strftime("%Y-%m-%d"))
22
+ already_planned = self.get_tasks(date)
23
+ count = 0
24
+ re_display = false
25
+ planned = []
26
+ tasks.each do |task|
27
+ next if already_planned.find_index {|t| t == task}
28
+ unless re_display
29
+ task.print_pretty
30
+ else
31
+ task.print_pretty(true)
32
+ re_display = false
33
+ end
34
+ choice = @console.prompt PROMPT_STRING
35
+ case choice
36
+ when 'a'
37
+ print "Duration (1 = 15 minutes, return 30 minutes): "
38
+ duration = gets.chomp
39
+ task.duration = duration.empty? ? 2 : duration
40
+ task.options[:follow_up] = date
41
+ @service.save(task.dir, task)
42
+ planned << task
43
+ count += 1
44
+ when 'c'
45
+ re_display = true
46
+ redo
47
+ when 's'
48
+ #do nothing
49
+ when 'q'
50
+ break
51
+ end
52
+ end
53
+ save_tasks(planned)
54
+ count
55
+ end
56
+
57
+ # Add the tasks to the planned tasks
58
+ def add_tasks(tasks)
59
+ save_tasks(tasks)
60
+ end
61
+
62
+ # Remove planned tasks from the task plan based on the provided filter
63
+ # (filter options see Task#matches?). Returns the count of removed tasks
64
+ def remove_tasks(date=Time.now.strftime("%Y-%m-%d"), filter={})
65
+ planned = []
66
+ tasks = self.get_tasks(date)
67
+ tasks.each do |task|
68
+ planned << task unless task.matches?(filter)
69
+ end
70
+ save_tasks(planned, true)
71
+ tasks.size - planned.size
72
+ end
73
+
74
+ # Get planned tasks of the specified date. Retrieve only tasks that match
75
+ # the specified filter (filter options see Task#matches?)
76
+ def get_tasks(date=Time.now.strftime("%Y-%m-%d"), filter={})
77
+ make_todo_today_file(date)
78
+ tasks = []
79
+ File.open(@todo_today_file, 'r') do |file|
80
+ file.each do |line|
81
+ dir, id = line.chomp.split(",")
82
+ task = @service.read(dir, id)
83
+ tasks << task if not task.nil? and task.matches?(filter)
84
+ end
85
+ end if File.exists? @todo_today_file
86
+ tasks
87
+ end
88
+
89
+ private
90
+
91
+ # Creates a file where the planned tasks are saved to
92
+ def make_todo_today_file(date)
93
+ file_name = Time.now.strftime("#{date}_planned_tasks")
94
+ @todo_today_file = WORK_DIR+"/"+file_name
95
+ end
96
+
97
+ # Save the tasks to a file. If override is true the file is overriden
98
+ # otherwise the tasks are appended
99
+ def save_tasks(tasks, override=false)
100
+ mode = override ? 'w' : 'a'
101
+ FileUtils.mkdir_p WORK_DIR unless File.exists? WORK_DIR
102
+ File.open(@todo_today_file, mode) do |file|
103
+ tasks.each do |task|
104
+ file.puts("#{task.dir},#{task.id}")
105
+ end
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,172 @@
1
+ require_relative 'schedule.rb'
2
+
3
+ module Syctask
4
+
5
+ # The TaskScheduler creates a graphical representation of a working schedule
6
+ # with busy times visualized. A typical invokation would be
7
+ # work_time = "8:00-18:00"
8
+ # busy_time = "9:00-9:30,13:00-14:30"
9
+ # scheduler = Syctask::TaskScheduler.new(work_time, busy_time)
10
+ # scheduler.print_graph
11
+ # The output would be
12
+ # |---///-|---|---|---///////-|---|---|---|
13
+ # 8 9 10 11 12 13 14 15 16 17 18
14
+ # To add tasks to the schedule tasks have to provided (see Task). A task has
15
+ # a duration which indicates the time it is planned to process a task. The
16
+ # duration is an Integer 1,2,.. where 1 is 15 minutes and 2 is 30 minutes and
17
+ # so on. Assuming we have 5 tasks with a duration of 2, 5, 3, 2 and 3 15
18
+ # minute chunks. Then the invokation of
19
+ # scheduler.schedule_tasks(tasks)
20
+ # would output the schedule
21
+ # |xx-///ooooo|xxx|oo-///////xxx--|---|---|
22
+ # 8 9 10 11 12 13 14 15 16 17 18
23
+ # The tasks are added to the schedule dependent on the time chunks and the
24
+ # available free time gaps.
25
+ class TaskScheduler
26
+ # Time pattern that matches 24 hour times '12:30'
27
+ TIME_PATTERN = /(2[0-3]|[01]?[0-9]):([0-5]?[0-9])/
28
+
29
+ # Work time pattern scans time like '8:00-18:00'
30
+ WORK_TIME_PATTERN = /#{TIME_PATTERN}-#{TIME_PATTERN}/
31
+
32
+ # Busy time pattern scans times like '9:00-9:30,11:00-11:45'
33
+ BUSY_TIME_PATTERN =
34
+ /#{TIME_PATTERN}-#{TIME_PATTERN}(?=,)|#{TIME_PATTERN}-#{TIME_PATTERN}$/
35
+
36
+ # Scans assignments of tasks to meetings 'A:0,2,4;B:3,4,5'
37
+ ASSIGNMENT_PATTERN = /([a-zA-Z]):(\d+(?:,\d+|\d+;)*)/
38
+
39
+ # Working directory
40
+ WORK_DIR = File.expand_path("~/.tasks")
41
+
42
+ # Creates a new TaskScheduler.
43
+ def initialize
44
+ @work_time = []
45
+ @busy_time = []
46
+ @meetings = []
47
+ @tasks = []
48
+ end
49
+
50
+ # Set the work time. Raises an exception if begin time is after start time
51
+ # Invokation: set_work_time(["8","0","18","30"])
52
+ def set_work_time(work_time)
53
+ @work_time = process_work_time(work_time)
54
+ unless sequential?(@work_time)
55
+ raise Exception, "Begin time has to be before end time"
56
+ end
57
+ end
58
+
59
+ # Set the busy times. Raises an exception if one begin time is after start
60
+ # time
61
+ # Invokation: set_busy_times([["9","30","10","45"],["12","0","13","45"]])
62
+ def set_busy_times(busy_time)
63
+ @busy_time = process_busy_time(busy_time)
64
+ @busy_time.each do |busy|
65
+ unless sequential?(busy)
66
+ raise Exception, "Begin time has to be before end time"
67
+ end
68
+ end
69
+ end
70
+
71
+ # Sets the titles of the meetings (busy times)
72
+ # Invokation: set_meeting_titles("title1,title2,title3")
73
+ def set_meeting_titles(titles)
74
+ @meetings = titles.split(",") if titles
75
+ end
76
+
77
+ def set_tasks(tasks)
78
+ @tasks = tasks
79
+ end
80
+
81
+ # Add scheduled tasks to busy times
82
+ # Invokation: set_task_assignments([["A","1,2,3"],["B","2,5,6,7"]])
83
+ def set_task_assignments(assignments)
84
+ @assignments = assignments.scan(ASSIGNMENT_PATTERN)
85
+ raise "No valid assignment" if @assignments.empty?
86
+ end
87
+
88
+ private
89
+
90
+ # Checks the sequence of begin and end time. Returns true if begin is before
91
+ # end time otherwise false
92
+ def sequential?(range)
93
+ return true if range[0].to_i < range[2].to_i
94
+ if range[0].to_i == range[2].to_i
95
+ return true if range[1].to_i < range[3].to_i
96
+ end
97
+ false
98
+ end
99
+
100
+ # Scans the work time and separates hours and minutes. Raises an Exception
101
+ # if work time is nil or empty
102
+ def process_work_time(work_time)
103
+ raise Exception, "Work time must not be nil" if work_time.nil?
104
+ time = work_time.scan(WORK_TIME_PATTERN).flatten
105
+ raise Exception, "Work time cannot be empty" if time.empty?
106
+ time
107
+ end
108
+
109
+ # Scans the busy times and separates hours and minutes.
110
+ def process_busy_time(busy_time)
111
+ busy_time = "" if busy_time.nil?
112
+ busy_time.scan(BUSY_TIME_PATTERN).each {|busy| busy.compact!}
113
+ end
114
+
115
+ public
116
+
117
+ # Restores the value of a previous invokation. Posible values are
118
+ # :work_time, :busy_time, :meetings and :assignments
119
+ # Returns true if a value from a previous call is available otherwise false
120
+ def restore(value)
121
+ work_time, busy_time, meetings, assignments = restore_state
122
+ @work_time = work_time if value == :work_time
123
+ @busy_time = busy_time if value == :busy_time
124
+ @meetings = meetings if value == :meetings
125
+ @assignments = assignments if value == :assignments
126
+ return false if value == :work_time and (@work_time.nil? or @work_time.empty?)
127
+ return false if value == :busy_time and (@busy_time.nil? or @busy_time.empty?)
128
+ return false if value == :meetings and (@busy_time.nil? or @meetings.empty?)
129
+ return false if value == :assignments and (@assignments.nil? or @assignments.empty?)
130
+ true
131
+ end
132
+
133
+ # Prints the meeting list, timeline and task list
134
+ def show
135
+ schedule = Syctask::Schedule.new(@work_time, @busy_time, @meetings, @tasks)
136
+ schedule.assign(@assignments) if @assignments
137
+ schedule.graph.each {|output| puts output}
138
+ save_state @work_time, @busy_time, @meetings, @assignments
139
+ true
140
+ end
141
+
142
+ private
143
+
144
+ # Saves the work time, busy time, meetings and assignments from the
145
+ # invokation for later retrieval
146
+ def save_state(work_time, busy_time, meetings, assignments)
147
+ state = {work_time: work_time,
148
+ busy_time: busy_time,
149
+ meetings: meetings,
150
+ assignments: assignments}
151
+ FileUtils.mkdir WORK_DIR unless File.exists? WORK_DIR
152
+ state_file = WORK_DIR+'/'+Time.now.strftime("%Y-%m-%d_time_schedule")
153
+ File.open(state_file, 'w') do |file|
154
+ YAML.dump(state, file)
155
+ end
156
+ end
157
+
158
+ # Retrieves the state of the last invokation. Returns the work and busy
159
+ # time, meetings and assignments
160
+ def restore_state
161
+ state_file = WORK_DIR+'/'+Time.now.strftime("%Y-%m-%d_time_schedule")
162
+ return [[], [], [], []] unless File.exists? state_file
163
+ state = YAML.load_file(state_file)
164
+ [state[:work_time],
165
+ state[:busy_time],
166
+ state[:meetings],
167
+ state[:assignments]]
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -0,0 +1,19 @@
1
+ module Syctask
2
+
3
+ class Times
4
+
5
+ attr_reader :h
6
+ attr_reader :m
7
+
8
+ def initialize(time)
9
+ @h = time[0].to_i
10
+ @m = time[1].to_i
11
+ end
12
+
13
+ def round_up
14
+ @m > 0 ? @h+1 : @h
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -1,5 +1,5 @@
1
1
  # Syctask provides functions for managing tasks in a task list
2
2
  module Syctask
3
3
  #Holds the version number of syctask
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.3'
5
5
  end
data/lib/syctask.rb CHANGED
@@ -4,6 +4,8 @@ require 'syctask/task.rb'
4
4
  require 'syctask/task_service.rb'
5
5
  require 'syctask/task_scheduler.rb'
6
6
  require 'syctask/task_planner.rb'
7
+ require 'syctask/schedule.rb'
8
+ require 'sycutil/console.rb'
7
9
 
8
10
  # Add requires for other files you add to your project here, so
9
11
  # you just need to require this one file in your bin file
@@ -0,0 +1,60 @@
1
+ require 'io/wait'
2
+
3
+ # Module Inspector contains functions related to the Console that is helpers
4
+ # for user input
5
+ module Sycutil
6
+
7
+ # Console provides functions for user input
8
+ class Console
9
+
10
+ # Listens on Ctrl-C and exits the application
11
+ Signal.trap("INT") do
12
+ puts "-> program terminated by user"
13
+ exit
14
+ end
15
+
16
+ # Listens for key presses and returns the pressed key without pressing
17
+ # return
18
+ #
19
+ # :call-seq:
20
+ # char_if_pressed
21
+ def char_if_pressed
22
+ begin
23
+ system("stty raw -echo")
24
+ c = nil
25
+ if $stdin.ready?
26
+ c = $stdin.getc
27
+ end
28
+ c.chr if c
29
+ ensure
30
+ system "stty -raw echo"
31
+ end
32
+ end
33
+
34
+ # Prompts the user for input.
35
+ #
36
+ # :call-seq:
37
+ # prompt(choice_line) -> char
38
+ #
39
+ # choice_line is the prompt string. If the prompt string contains a (x)
40
+ # sequence x is a valid choice the is relized when pressed and returned.
41
+ def prompt(choice_line)
42
+ pattern = /(?<=\()./
43
+ choices = choice_line.scan(pattern)
44
+
45
+ choice = nil
46
+
47
+ while choices.find_index(choice).nil?
48
+ print choice_line
49
+ choice = nil
50
+ choice = char_if_pressed while choice == nil
51
+ sleep 0.1
52
+ puts
53
+ end
54
+
55
+ choice
56
+ end
57
+
58
+ end
59
+
60
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syc-task
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-10 00:00:00.000000000 Z
12
+ date: 2013-03-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -154,8 +154,14 @@ files:
154
154
  - lib/syctask/version.rb
155
155
  - lib/syctask/task.rb
156
156
  - lib/syctask/task_service.rb
157
+ - lib/syctask/task_planner.rb
157
158
  - lib/syctask/evaluator.rb
158
159
  - lib/syctask.rb
160
+ - lib/syctask/task_scheduler.rb
161
+ - lib/syctask/meeting.rb
162
+ - lib/syctask/times.rb
163
+ - lib/syctask/schedule.rb
164
+ - lib/sycutil/console.rb
159
165
  - README.rdoc
160
166
  - syctask.rdoc
161
167
  homepage: http://syc.dyndns.org/drupal/syc-task