timr 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ackrc +9 -0
- data/.editorconfig +1 -0
- data/.env.example +7 -0
- data/.github/CONTRIBUTING.md +32 -0
- data/.github/ISSUE_TEMPLATE.md +13 -0
- data/.gitignore +8 -2
- data/.rdoc_options +21 -0
- data/.travis.yml +10 -7
- data/Gemfile +8 -0
- data/README.md +216 -3
- data/bin/.gitignore +2 -0
- data/bin/README.md +17 -0
- data/bin/build.sh +14 -0
- data/bin/build_api.sh +14 -0
- data/bin/build_coverage.sh +23 -0
- data/bin/build_info.sh +27 -0
- data/bin/build_man.sh +41 -0
- data/bin/clean.sh +14 -0
- data/bin/dev_setup.sh +19 -0
- data/bin/install.sh +49 -0
- data/bin/publish +38 -0
- data/bin/release.sh +35 -0
- data/bin/test.sh +19 -0
- data/bin/timr +20 -40
- data/bin/timr_bash_completion.sh +337 -0
- data/bin/uninstall.sh +24 -0
- data/lib/timr.rb +36 -8
- data/lib/timr/command/basic_command.rb +170 -0
- data/lib/timr/command/continue_command.rb +86 -0
- data/lib/timr/command/help_command.rb +137 -0
- data/lib/timr/command/log_command.rb +297 -0
- data/lib/timr/command/pause_command.rb +89 -0
- data/lib/timr/command/pop_command.rb +176 -0
- data/lib/timr/command/push_command.rb +141 -0
- data/lib/timr/command/report_command.rb +689 -0
- data/lib/timr/command/start_command.rb +172 -0
- data/lib/timr/command/status_command.rb +198 -0
- data/lib/timr/command/stop_command.rb +127 -0
- data/lib/timr/command/task_command.rb +318 -0
- data/lib/timr/command/track_command.rb +381 -0
- data/lib/timr/command/version_command.rb +18 -0
- data/lib/timr/duration.rb +159 -0
- data/lib/timr/exception/timr_error.rb +113 -0
- data/lib/timr/ext/time.rb +12 -0
- data/lib/timr/helper/datetime_helper.rb +128 -0
- data/lib/timr/helper/terminal_helper.rb +58 -0
- data/lib/timr/helper/translation_helper.rb +45 -0
- data/lib/timr/model/basic_model.rb +287 -0
- data/lib/timr/model/config.rb +48 -0
- data/lib/timr/model/foreign_id_db.rb +84 -0
- data/lib/timr/model/stack.rb +161 -0
- data/lib/timr/model/task.rb +1039 -0
- data/lib/timr/model/track.rb +589 -0
- data/lib/timr/progressbar.rb +41 -0
- data/lib/timr/simple_opt_parser.rb +230 -0
- data/lib/timr/status.rb +70 -0
- data/lib/timr/table.rb +88 -0
- data/lib/timr/timr.rb +500 -558
- data/lib/timr/version.rb +4 -15
- data/man/.gitignore +2 -0
- data/man/_footer +3 -0
- data/man/timr-continue.1 +48 -0
- data/man/timr-continue.1.ronn +39 -0
- data/man/timr-ftime.7 +77 -0
- data/man/timr-ftime.7.ronn +57 -0
- data/man/timr-log.1 +109 -0
- data/man/timr-log.1.ronn +87 -0
- data/man/timr-pause.1 +56 -0
- data/man/timr-pause.1.ronn +45 -0
- data/man/timr-pop.1 +66 -0
- data/man/timr-pop.1.ronn +53 -0
- data/man/timr-push.1 +25 -0
- data/man/timr-push.1.ronn +20 -0
- data/man/timr-report.1 +228 -0
- data/man/timr-report.1.ronn +193 -0
- data/man/timr-start.1 +100 -0
- data/man/timr-start.1.ronn +82 -0
- data/man/timr-status.1 +53 -0
- data/man/timr-status.1.ronn +42 -0
- data/man/timr-stop.1 +75 -0
- data/man/timr-stop.1.ronn +60 -0
- data/man/timr-task.1 +147 -0
- data/man/timr-task.1.ronn +115 -0
- data/man/timr-track.1 +109 -0
- data/man/timr-track.1.ronn +89 -0
- data/man/timr.1 +119 -0
- data/man/timr.1.ronn +68 -0
- data/timr.gemspec +18 -3
- data/timr.sublime-project +20 -1
- metadata +142 -23
- data/Makefile +0 -12
- data/Makefile.common +0 -56
- data/lib/timr/stack.rb +0 -81
- data/lib/timr/task.rb +0 -258
- data/lib/timr/track.rb +0 -167
- data/lib/timr/window.rb +0 -259
- data/lib/timr/window_help.rb +0 -41
- data/lib/timr/window_tasks.rb +0 -30
- data/lib/timr/window_test.rb +0 -20
- data/lib/timr/window_timeline.rb +0 -33
- data/tests/tc_stack.rb +0 -121
- data/tests/tc_task.rb +0 -190
- data/tests/tc_track.rb +0 -144
- data/tests/tc_window.rb +0 -428
- data/tests/ts_all.rb +0 -6
@@ -0,0 +1,589 @@
|
|
1
|
+
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module TheFox
|
5
|
+
module Timr
|
6
|
+
module Model
|
7
|
+
|
8
|
+
class Track < BasicModel
|
9
|
+
|
10
|
+
include TheFox::Timr::Helper
|
11
|
+
include TheFox::Timr::Error
|
12
|
+
|
13
|
+
# Parent Task instance
|
14
|
+
attr_accessor :task
|
15
|
+
|
16
|
+
# Track Message. What have you done?
|
17
|
+
attr_reader :message
|
18
|
+
|
19
|
+
# Is this even in use? ;D
|
20
|
+
attr_accessor :paused
|
21
|
+
|
22
|
+
attr_reader :is_billed
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super()
|
26
|
+
|
27
|
+
@task = nil
|
28
|
+
|
29
|
+
@begin_datetime = nil
|
30
|
+
@end_datetime = nil
|
31
|
+
@is_billed = false
|
32
|
+
@message = nil
|
33
|
+
@paused = false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Set begin_datetime.
|
37
|
+
def begin_datetime=(begin_datetime)
|
38
|
+
case begin_datetime
|
39
|
+
when String
|
40
|
+
begin_datetime = Time.parse(begin_datetime)
|
41
|
+
when Time
|
42
|
+
# OK
|
43
|
+
else
|
44
|
+
raise TrackError, "begin_datetime needs to be a String or Time, #{begin_datetime.class} given."
|
45
|
+
end
|
46
|
+
|
47
|
+
if @end_datetime && begin_datetime >= @end_datetime
|
48
|
+
raise TrackError, 'begin_datetime must be lesser than end_datetime.'
|
49
|
+
end
|
50
|
+
|
51
|
+
@begin_datetime = begin_datetime
|
52
|
+
|
53
|
+
# Mark Track as changed.
|
54
|
+
changed
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get begin_datetime.
|
58
|
+
#
|
59
|
+
# Options:
|
60
|
+
#
|
61
|
+
# - `:from` (Time)
|
62
|
+
# See documentation about `:to` on `end_datetime()`.
|
63
|
+
def begin_datetime(options = Hash.new)
|
64
|
+
from_opt = options.fetch(:from, nil)
|
65
|
+
|
66
|
+
if @begin_datetime
|
67
|
+
if from_opt && from_opt > @begin_datetime
|
68
|
+
bdt = from_opt
|
69
|
+
else
|
70
|
+
bdt = @begin_datetime
|
71
|
+
end
|
72
|
+
bdt.localtime
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get begin_datetime String.
|
77
|
+
#
|
78
|
+
# Options:
|
79
|
+
#
|
80
|
+
# - `:format` (String)
|
81
|
+
def begin_datetime_s(options = Hash.new)
|
82
|
+
format_opt = options.fetch(:format, HUMAN_DATETIME_FOMRAT)
|
83
|
+
|
84
|
+
bdt = begin_datetime(options)
|
85
|
+
if bdt
|
86
|
+
bdt.strftime(format_opt)
|
87
|
+
else
|
88
|
+
'---'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set end_datetime.
|
93
|
+
def end_datetime=(end_datetime)
|
94
|
+
if !@begin_datetime
|
95
|
+
raise TrackError, 'end_datetime cannot be set until begin_datetime is set.'
|
96
|
+
end
|
97
|
+
|
98
|
+
case end_datetime
|
99
|
+
when String
|
100
|
+
end_datetime = Time.parse(end_datetime)
|
101
|
+
when Time
|
102
|
+
# OK
|
103
|
+
else
|
104
|
+
raise TrackError, "end_datetime needs to be a String or Time, #{end_datetime.class} given."
|
105
|
+
end
|
106
|
+
|
107
|
+
if end_datetime <= @begin_datetime
|
108
|
+
raise TrackError, 'end_datetime must be greater than begin_datetime.'
|
109
|
+
end
|
110
|
+
|
111
|
+
@end_datetime = end_datetime
|
112
|
+
|
113
|
+
# Mark Track as changed.
|
114
|
+
changed
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get end_datetime.
|
118
|
+
#
|
119
|
+
# Options:
|
120
|
+
#
|
121
|
+
# - `:to` (Time)
|
122
|
+
# This limits `@end_datetime`. If `:to` > `@end_datetime` it returns the
|
123
|
+
# original `@end_datetime`. Otherwise it will return `:to`. The same applies for
|
124
|
+
# `:from` on `begin_datetime()` but just the other way round.
|
125
|
+
def end_datetime(options = Hash.new)
|
126
|
+
to_opt = options.fetch(:to, nil)
|
127
|
+
|
128
|
+
if @end_datetime
|
129
|
+
if to_opt && to_opt < @end_datetime
|
130
|
+
edt = to_opt
|
131
|
+
else
|
132
|
+
edt = @end_datetime
|
133
|
+
end
|
134
|
+
edt.localtime
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get end_datetime String.
|
139
|
+
#
|
140
|
+
# Options:
|
141
|
+
#
|
142
|
+
# - `:format` (String)
|
143
|
+
def end_datetime_s(options = Hash.new)
|
144
|
+
format_opt = options.fetch(:format, HUMAN_DATETIME_FOMRAT)
|
145
|
+
|
146
|
+
edt = end_datetime(options)
|
147
|
+
if edt
|
148
|
+
edt.strftime(format_opt)
|
149
|
+
else
|
150
|
+
'---'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Set message.
|
155
|
+
def message=(message)
|
156
|
+
@message = message
|
157
|
+
|
158
|
+
# Mark Track as changed.
|
159
|
+
changed
|
160
|
+
end
|
161
|
+
|
162
|
+
# Start this Track. A Track cannot be restarted because it's the smallest time unit.
|
163
|
+
def start(options = Hash.new)
|
164
|
+
message_opt = options.fetch(:message, nil)
|
165
|
+
|
166
|
+
if @begin_datetime
|
167
|
+
raise TrackError, 'Cannot restart Track. Use dup() on this instance or create a new instance by using Track.new().'
|
168
|
+
end
|
169
|
+
|
170
|
+
@begin_datetime = DateTimeHelper.get_datetime_from_options(options)
|
171
|
+
|
172
|
+
if message_opt
|
173
|
+
@message = message_opt
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Stop this Track.
|
178
|
+
#
|
179
|
+
# Options:
|
180
|
+
#
|
181
|
+
# - `:start_date`
|
182
|
+
# - `:start_time`
|
183
|
+
# - `:end_date`, `:date`
|
184
|
+
# - `:end_time`, `:time`
|
185
|
+
# - `:message` (String)
|
186
|
+
def stop(options = Hash.new)
|
187
|
+
start_date_opt = options.fetch(:start_date, nil)
|
188
|
+
start_time_opt = options.fetch(:start_time, nil)
|
189
|
+
end_date_opt = options.fetch(:end_date, options.fetch(:date, nil))
|
190
|
+
end_time_opt = options.fetch(:end_time, options.fetch(:time, nil))
|
191
|
+
message_opt = options.fetch(:message, nil)
|
192
|
+
# paused_opt = options.fetch(:paused, false)
|
193
|
+
|
194
|
+
# Set Start DateTime
|
195
|
+
if start_date_opt || start_time_opt
|
196
|
+
begin_options = {
|
197
|
+
:date => start_date_opt,
|
198
|
+
:time => start_time_opt,
|
199
|
+
}
|
200
|
+
@begin_datetime = DateTimeHelper.get_datetime_from_options(begin_options)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Set End DateTime
|
204
|
+
end_options = {
|
205
|
+
:date => end_date_opt,
|
206
|
+
:time => end_time_opt,
|
207
|
+
}
|
208
|
+
@end_datetime = DateTimeHelper.get_datetime_from_options(end_options)
|
209
|
+
|
210
|
+
if message_opt
|
211
|
+
@message = message_opt
|
212
|
+
end
|
213
|
+
|
214
|
+
# @paused = paused_opt
|
215
|
+
|
216
|
+
# Mark Track as changed.
|
217
|
+
changed
|
218
|
+
end
|
219
|
+
|
220
|
+
# Cacluates the secondes between begin and end datetime and returns a new Duration instance.
|
221
|
+
#
|
222
|
+
# Options:
|
223
|
+
#
|
224
|
+
# - `:from` (Time), `:to` (Time)
|
225
|
+
# Limit the begin and end datetimes to a specific range.
|
226
|
+
def duration(options = Hash.new)
|
227
|
+
from_opt = options.fetch(:from, nil)
|
228
|
+
to_opt = options.fetch(:to, nil)
|
229
|
+
|
230
|
+
if @begin_datetime
|
231
|
+
bdt = @begin_datetime.utc
|
232
|
+
end
|
233
|
+
if @end_datetime
|
234
|
+
edt = @end_datetime.utc
|
235
|
+
else
|
236
|
+
edt = Time.now.utc
|
237
|
+
end
|
238
|
+
|
239
|
+
# Cut Start
|
240
|
+
if from_opt && bdt && from_opt > bdt
|
241
|
+
bdt = from_opt.utc
|
242
|
+
end
|
243
|
+
|
244
|
+
# Cut End
|
245
|
+
if to_opt && edt && to_opt < edt
|
246
|
+
edt = to_opt.utc
|
247
|
+
end
|
248
|
+
|
249
|
+
seconds = 0
|
250
|
+
if bdt && edt
|
251
|
+
if bdt < edt
|
252
|
+
seconds = (edt - bdt).to_i
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
Duration.new(seconds)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Alias method.
|
260
|
+
def billed_duration(options = Hash.new)
|
261
|
+
if self.is_billed
|
262
|
+
duration(options)
|
263
|
+
else
|
264
|
+
Duration.new(0)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Alias method.
|
269
|
+
def unbilled_duration(options = Hash.new)
|
270
|
+
if !self.is_billed
|
271
|
+
duration(options)
|
272
|
+
else
|
273
|
+
Duration.new(0)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# When begin_datetime is `2017-01-01 01:15`
|
278
|
+
# and end_datetime is `2017-01-03 02:17`
|
279
|
+
# this function returns
|
280
|
+
#
|
281
|
+
# ```
|
282
|
+
# [
|
283
|
+
# Date.new(2017, 1, 1),
|
284
|
+
# Date.new(2017, 1, 2),
|
285
|
+
# Date.new(2017, 1, 3),
|
286
|
+
# ]
|
287
|
+
# ```
|
288
|
+
def days
|
289
|
+
begin_date = @begin_datetime.to_date
|
290
|
+
end_date = @end_datetime.to_date
|
291
|
+
|
292
|
+
begin_date.upto(end_date)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Evaluates the Short Status and returns a new Status instance.
|
296
|
+
def status
|
297
|
+
if @begin_datetime.nil?
|
298
|
+
short_status = '-' # not started
|
299
|
+
elsif @end_datetime.nil?
|
300
|
+
short_status = 'R' # running
|
301
|
+
elsif @end_datetime
|
302
|
+
if @paused
|
303
|
+
# It's actually stopped but with an additional flag.
|
304
|
+
short_status = 'P' # paused
|
305
|
+
else
|
306
|
+
short_status = 'S' # stopped
|
307
|
+
end
|
308
|
+
else
|
309
|
+
short_status = 'U' # unknown
|
310
|
+
end
|
311
|
+
|
312
|
+
Status.new(short_status)
|
313
|
+
end
|
314
|
+
|
315
|
+
# Is the Track stopped?
|
316
|
+
def stopped?
|
317
|
+
status.short_status == 'S' # stopped
|
318
|
+
end
|
319
|
+
|
320
|
+
# Title generated from message. If the message has multiple lines only the first
|
321
|
+
# line will be taken to create the title.
|
322
|
+
#
|
323
|
+
# `max_length` can be used to define a maximum length. Three dots `...` will be appended
|
324
|
+
# at the end if the title is longer than `max_length`.
|
325
|
+
def title(max_length = nil)
|
326
|
+
unless @message
|
327
|
+
return
|
328
|
+
end
|
329
|
+
|
330
|
+
msg = @message.split("\n\n").first.split("\n").first
|
331
|
+
|
332
|
+
if max_length && msg.length > max_length + 2
|
333
|
+
msg = msg[0, max_length] << '...'
|
334
|
+
end
|
335
|
+
msg
|
336
|
+
end
|
337
|
+
|
338
|
+
# Title Alias
|
339
|
+
def name(max_length = nil)
|
340
|
+
title(max_length)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Set is_billed.
|
344
|
+
def is_billed=(is_billed)
|
345
|
+
@is_billed = is_billed
|
346
|
+
|
347
|
+
# Mark Track as changed.
|
348
|
+
changed
|
349
|
+
end
|
350
|
+
|
351
|
+
# When the Track is marked as changed it needs to mark the Task as changed.
|
352
|
+
#
|
353
|
+
# A single Track cannot be stored to a file. Tracks are assiged to a Task and are stored to the Task file.
|
354
|
+
def changed
|
355
|
+
super()
|
356
|
+
|
357
|
+
if @task
|
358
|
+
@task.changed
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Alias for Task. A Track cannot saved to a file. Only the whole Task.
|
363
|
+
def save_to_file(path = nil, force = false)
|
364
|
+
if @task
|
365
|
+
@task.save_to_file(path, force)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Duplicate this Track using the same Message. This is used almost by every Command.
|
370
|
+
# Start, Continue, Push, etc.
|
371
|
+
def dup
|
372
|
+
track = Track.new
|
373
|
+
track.task = @task
|
374
|
+
track.message = @message.clone
|
375
|
+
track
|
376
|
+
end
|
377
|
+
|
378
|
+
# Removes itself from parent Task.
|
379
|
+
def remove
|
380
|
+
if @task
|
381
|
+
@task.remove_track(self)
|
382
|
+
else
|
383
|
+
false
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# To String
|
388
|
+
def to_s
|
389
|
+
"Track_#{short_id}"
|
390
|
+
end
|
391
|
+
|
392
|
+
# To Hash
|
393
|
+
def to_h
|
394
|
+
h = {
|
395
|
+
'id' => @meta['id'],
|
396
|
+
'short_id' => short_id, # Not used.
|
397
|
+
'created' => @meta['created'],
|
398
|
+
'modified' => @meta['modified'],
|
399
|
+
'is_billed' => @is_billed,
|
400
|
+
'message' => @message,
|
401
|
+
}
|
402
|
+
if @begin_datetime
|
403
|
+
h['begin_datetime'] = @begin_datetime.utc.strftime(MODEL_DATETIME_FORMAT)
|
404
|
+
end
|
405
|
+
if @end_datetime
|
406
|
+
h['end_datetime'] = @end_datetime.utc.strftime(MODEL_DATETIME_FORMAT)
|
407
|
+
end
|
408
|
+
h
|
409
|
+
end
|
410
|
+
|
411
|
+
# Used to print informations to STDOUT.
|
412
|
+
def to_compact_str
|
413
|
+
to_compact_array.join("\n")
|
414
|
+
end
|
415
|
+
|
416
|
+
# Used to print informations to STDOUT.
|
417
|
+
def to_compact_array
|
418
|
+
to_ax = Array.new
|
419
|
+
|
420
|
+
if @task
|
421
|
+
to_ax.concat(@task.to_track_array)
|
422
|
+
end
|
423
|
+
|
424
|
+
to_ax << 'Track: %s %s' % [self.short_id, self.title]
|
425
|
+
|
426
|
+
# if self.title
|
427
|
+
# to_ax << 'Title: %s' % [self.title]
|
428
|
+
# end
|
429
|
+
if self.begin_datetime
|
430
|
+
to_ax << 'Start: %s' % [self.begin_datetime_s]
|
431
|
+
end
|
432
|
+
if self.end_datetime
|
433
|
+
to_ax << 'End: %s' % [self.end_datetime_s]
|
434
|
+
end
|
435
|
+
|
436
|
+
if self.duration && self.duration.to_i > 0
|
437
|
+
to_ax << 'Duration: %s' % [self.duration.to_human]
|
438
|
+
end
|
439
|
+
|
440
|
+
to_ax << 'Status: %s' % [self.status.colorized]
|
441
|
+
|
442
|
+
# if self.message
|
443
|
+
# to_ax << 'Message: %s' % [self.message]
|
444
|
+
# end
|
445
|
+
|
446
|
+
to_ax
|
447
|
+
end
|
448
|
+
|
449
|
+
# Used to print informations to STDOUT.
|
450
|
+
def to_detailed_str(options = Hash.new)
|
451
|
+
to_detailed_array(options).join("\n")
|
452
|
+
end
|
453
|
+
|
454
|
+
# Used to print informations to STDOUT.
|
455
|
+
#
|
456
|
+
# Options:
|
457
|
+
#
|
458
|
+
# - `:full_id` (Boolean) Show full Task and Track IDs.
|
459
|
+
def to_detailed_array(options = Hash.new)
|
460
|
+
full_id_opt = options.fetch(:full_id, false) # @TODO full_id unit test
|
461
|
+
|
462
|
+
to_ax = Array.new
|
463
|
+
|
464
|
+
if @task
|
465
|
+
to_ax.concat(@task.to_track_array(options))
|
466
|
+
end
|
467
|
+
|
468
|
+
if full_id_opt
|
469
|
+
to_ax << 'Track: %s' % [self.id]
|
470
|
+
else
|
471
|
+
to_ax << 'Track: %s' % [self.short_id]
|
472
|
+
end
|
473
|
+
|
474
|
+
if self.begin_datetime
|
475
|
+
to_ax << 'Start: %s' % [self.begin_datetime_s]
|
476
|
+
end
|
477
|
+
if self.end_datetime
|
478
|
+
to_ax << 'End: %s' % [self.end_datetime_s]
|
479
|
+
end
|
480
|
+
|
481
|
+
if self.duration && self.duration.to_i > 0
|
482
|
+
duration_human = self.duration.to_human
|
483
|
+
to_ax << 'Duration: %s' % [duration_human]
|
484
|
+
|
485
|
+
duration_man_days = self.duration.to_man_days
|
486
|
+
if duration_human != duration_man_days
|
487
|
+
to_ax << 'Man Unit: %s' % [duration_man_days]
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
to_ax << 'Billed: %s' % [self.is_billed ? 'Yes' : 'No']
|
492
|
+
to_ax << 'Status: %s' % [self.status.colorized]
|
493
|
+
|
494
|
+
if self.message
|
495
|
+
to_ax << 'Message: %s' % [self.message]
|
496
|
+
end
|
497
|
+
|
498
|
+
to_ax
|
499
|
+
end
|
500
|
+
|
501
|
+
# Are two Tracks equal?
|
502
|
+
#
|
503
|
+
# Uses ID for comparision.
|
504
|
+
def eql?(track)
|
505
|
+
unless track.is_a?(Track)
|
506
|
+
raise TrackError, "track variable must be a Track instance. #{track.class} given."
|
507
|
+
end
|
508
|
+
|
509
|
+
self.id == track.id
|
510
|
+
end
|
511
|
+
|
512
|
+
def inspect
|
513
|
+
"#<Track #{short_id}>"
|
514
|
+
end
|
515
|
+
|
516
|
+
# All methods in this block are static.
|
517
|
+
class << self
|
518
|
+
|
519
|
+
# Create a new Track instance from a Hash.
|
520
|
+
def create_track_from_hash(hash)
|
521
|
+
unless hash.is_a?(Hash)
|
522
|
+
raise TrackError, "hash variable must be a Hash instance. #{hash.class} given."
|
523
|
+
end
|
524
|
+
|
525
|
+
track = Track.new
|
526
|
+
if hash['id']
|
527
|
+
track.id = hash['id']
|
528
|
+
end
|
529
|
+
if hash['created']
|
530
|
+
track.created = hash['created']
|
531
|
+
end
|
532
|
+
if hash['modified']
|
533
|
+
track.modified = hash['modified']
|
534
|
+
end
|
535
|
+
if hash['is_billed']
|
536
|
+
track.is_billed = hash['is_billed']
|
537
|
+
end
|
538
|
+
if hash['message']
|
539
|
+
track.message = hash['message']
|
540
|
+
end
|
541
|
+
if hash['begin_datetime']
|
542
|
+
track.begin_datetime = hash['begin_datetime']
|
543
|
+
end
|
544
|
+
if hash['end_datetime']
|
545
|
+
track.end_datetime = hash['end_datetime']
|
546
|
+
end
|
547
|
+
track.has_changed = false
|
548
|
+
track
|
549
|
+
end
|
550
|
+
|
551
|
+
# This is really bad. Do not use this.
|
552
|
+
def find_track_by_id(base_path, track_id)
|
553
|
+
found_track = nil
|
554
|
+
|
555
|
+
# Iterate all files.
|
556
|
+
base_path.find.each do |file|
|
557
|
+
# Filter all directories.
|
558
|
+
unless file.file?
|
559
|
+
next
|
560
|
+
end
|
561
|
+
|
562
|
+
# Filter all non-yaml files.
|
563
|
+
unless file.basename.fnmatch('*.yml')
|
564
|
+
next
|
565
|
+
end
|
566
|
+
|
567
|
+
task = Task.load_task_from_file(file)
|
568
|
+
tmp_track = task.find_track_by_id(track_id)
|
569
|
+
if tmp_track
|
570
|
+
if found_track
|
571
|
+
raise TrackError, "Track ID '#{track_id}' is not a unique identifier."
|
572
|
+
else
|
573
|
+
found_track = tmp_track
|
574
|
+
|
575
|
+
# Do not break the loop here.
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
found_track
|
581
|
+
end
|
582
|
+
|
583
|
+
end
|
584
|
+
|
585
|
+
end # class Track
|
586
|
+
|
587
|
+
end # module Model
|
588
|
+
end # module Timr
|
589
|
+
end #module TheFox
|