Class: TheFox::Timr::Model::Track

Inherits:
BasicModel show all
Includes:
Error, Helper
Defined in:
lib/timr/model/track.rb

Instance Attribute Summary collapse

Attributes inherited from BasicModel

#file_path, #has_changed

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BasicModel

create_path_by_id, #created=, #delete_file, find_file_by_id, get_id_from_path, #id, #id=, #load_from_file, #modified=, #post_load_from_file, #post_save_to_file, #pre_load_from_file, #pre_save_to_file, #short_id

Constructor Details

#initializeTrack

Returns a new instance of Track.



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/timr/model/track.rb', line 24

def initialize
	super()
	
	@task = nil
	
	@begin_datetime = nil
	@end_datetime = nil
	@is_billed = false
	@message = nil
	@paused = false
end

Instance Attribute Details

#is_billedObject

Returns the value of attribute is_billed.



22
23
24
# File 'lib/timr/model/track.rb', line 22

def is_billed
  @is_billed
end

#messageObject

Track Message. What have you done?



17
18
19
# File 'lib/timr/model/track.rb', line 17

def message
  @message
end

#pausedObject

Is this even in use? ;D



20
21
22
# File 'lib/timr/model/track.rb', line 20

def paused
  @paused
end

#taskObject

Parent Task instance



14
15
16
# File 'lib/timr/model/track.rb', line 14

def task
  @task
end

Class Method Details

.create_track_from_hash(hash) ⇒ Object

Create a new Track instance from a Hash.



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/timr/model/track.rb', line 589

def create_track_from_hash(hash)
	unless hash.is_a?(Hash)
		raise TrackError, "hash variable must be a Hash instance. #{hash.class} given."
	end
	
	track = Track.new
	if hash['id']
		track.id = hash['id']
	end
	if hash['created']
		track.created = hash['created']
	end
	if hash['modified']
		track.modified = hash['modified']
	end
	if hash['is_billed']
		track.is_billed = hash['is_billed']
	end
	if hash['message']
		track.message = hash['message']
	end
	if hash['begin_datetime']
		track.begin_datetime = hash['begin_datetime']
	end
	if hash['end_datetime']
		track.end_datetime = hash['end_datetime']
	end
	track.has_changed = false
	track
end

.find_track_by_id(base_path, track_id) ⇒ Object

This is really bad. Do not use this.



621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
# File 'lib/timr/model/track.rb', line 621

def find_track_by_id(base_path, track_id)
	found_track = nil
	
	# Iterate all files.
	base_path.find.each do |file|
		# Filter all directories.
		unless file.file?
			next
		end
		
		# Filter all non-yaml files.
		unless file.basename.fnmatch('*.yml')
			next
		end
		
		task = Task.load_task_from_file(file)
		tmp_track = task.find_track_by_id(track_id)
		if tmp_track
			if found_track
				raise TrackError, "Track ID '#{track_id}' is not a unique identifier."
			else
				found_track = tmp_track
				
				# Do not break the loop here.
			end
		end
	end
	
	found_track
end

Instance Method Details

#begin_datetime(options = Hash.new) ⇒ Object

Get begin_datetime.

Options:

  • ‘:from` (Time)

    See documentation about ‘:to` on `end_datetime()`.



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/timr/model/track.rb', line 63

def begin_datetime(options = Hash.new)
	from_opt = options.fetch(:from, nil)
	
	if @begin_datetime
		if from_opt && from_opt > @begin_datetime
			bdt = from_opt
		else
			bdt = @begin_datetime
		end
		bdt.localtime
	end
end

#begin_datetime=(begin_datetime) ⇒ Object

Set begin_datetime.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/timr/model/track.rb', line 37

def begin_datetime=(begin_datetime)
	case begin_datetime
	when String
		begin_datetime = Time.parse(begin_datetime)
	when Time
		# OK
	else
		raise TrackError, "begin_datetime needs to be a String or Time, #{begin_datetime.class} given."
	end
	
	if @end_datetime && begin_datetime >= @end_datetime
		raise TrackError, 'begin_datetime must be lesser than end_datetime.'
	end
	
	@begin_datetime = begin_datetime
	
	# Mark Track as changed.
	changed
end

#begin_datetime_s(options = Hash.new) ⇒ Object

Get begin_datetime String.

Options:

  • ‘:format` (String)



81
82
83
84
85
86
87
88
89
90
# File 'lib/timr/model/track.rb', line 81

def begin_datetime_s(options = Hash.new)
	format_opt = options.fetch(:format, HUMAN_DATETIME_FOMRAT)
	
	bdt = begin_datetime(options)
	if bdt
		bdt.strftime(format_opt)
	else
		'---'
	end
end

#billed_duration(options = Hash.new) ⇒ Object

Alias method.



268
269
270
# File 'lib/timr/model/track.rb', line 268

def billed_duration(options = Hash.new)
	duration(options.merge({:billed => true}))
end

#changedObject

When the Track is marked as changed it needs to mark the Task as changed.

A single Track cannot be stored to a file. Tracks are assiged to a Task and are stored to the Task file.



359
360
361
362
363
364
365
# File 'lib/timr/model/track.rb', line 359

def changed
	super()
	
	if @task
		@task.changed
	end
end

#daysObject

When begin_datetime is ‘2017-01-01 01:15`

and end_datetime   is `2017-01-03 02:17`

this function returns

“‘ [ Date.new(2017, 1, 1), Date.new(2017, 1, 2), Date.new(2017, 1, 3), ] “`



288
289
290
291
292
293
# File 'lib/timr/model/track.rb', line 288

def days
	begin_date = @begin_datetime.to_date
	end_date = @end_datetime.to_date
	
	begin_date.upto(end_date)
end

#dupObject

Duplicate this Track using the same Message. This is used almost by every Command.

Start, Continue, Push, etc.



376
377
378
379
380
381
# File 'lib/timr/model/track.rb', line 376

def dup
	track = Track.new
	track.task = @task
	track.message = @message.clone
	track
end

#duration(options = Hash.new) ⇒ Object

Cacluates the secondes between begin and end datetime and returns a new Duration instance.

Options:

  • ‘:from` (Time), `:to` (Time)

    Limit the begin and end datetimes to a specific range.

  • ‘:billed` (Boolean)



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/timr/model/track.rb', line 227

def duration(options = Hash.new)
	from_opt = options.fetch(:from, nil)
	to_opt = options.fetch(:to, nil)
	billed_opt = options.fetch(:billed, nil)
	
	unless billed_opt.nil?
		if @is_billed != billed_opt
			return Duration.new(0)
		end
	end
	
	if @begin_datetime
		bdt = @begin_datetime.utc
	end
	if @end_datetime
		edt = @end_datetime.utc
	else
		edt = Time.now.utc
	end
	
	# Cut Start
	if from_opt && bdt && from_opt > bdt
		bdt = from_opt.utc
	end
	
	# Cut End
	if to_opt && edt && to_opt < edt
		edt = to_opt.utc
	end
	
	seconds = 0
	if bdt && edt
		if bdt < edt
			seconds = (edt - bdt).to_i
		end
	end
	
	Duration.new(seconds)
end

#end_datetime(options = Hash.new) ⇒ Object

Get end_datetime.

Options:

  • ‘:to` (Time)

    This limits ‘@end_datetime`. If `:to` > `@end_datetime` it returns the

original ‘@end_datetime`. Otherwise it will return `:to`. The same applies for `:from` on `begin_datetime()` but just the other way round.



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/timr/model/track.rb', line 125

def end_datetime(options = Hash.new)
	to_opt = options.fetch(:to, nil)
	
	if @end_datetime
		if to_opt && to_opt < @end_datetime
			edt = to_opt
		else
			edt = @end_datetime
		end
		edt.localtime
	end
end

#end_datetime=(end_datetime) ⇒ Object

Set end_datetime.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/timr/model/track.rb', line 93

def end_datetime=(end_datetime)
	if !@begin_datetime
		raise TrackError, 'end_datetime cannot be set until begin_datetime is set.'
	end
	
	case end_datetime
	when String
		end_datetime = Time.parse(end_datetime)
	when Time
		# OK
	else
		raise TrackError, "end_datetime needs to be a String or Time, #{end_datetime.class} given."
	end
	
	if end_datetime <= @begin_datetime
		raise TrackError, 'end_datetime must be greater than begin_datetime.'
	end
	
	@end_datetime = end_datetime
	
	# Mark Track as changed.
	changed
end

#end_datetime_s(options = Hash.new) ⇒ Object

Get end_datetime String.

Options:

  • ‘:format` (String)



143
144
145
146
147
148
149
150
151
152
# File 'lib/timr/model/track.rb', line 143

def end_datetime_s(options = Hash.new)
	format_opt = options.fetch(:format, HUMAN_DATETIME_FOMRAT)
	
	edt = end_datetime(options)
	if edt
		edt.strftime(format_opt)
	else
		'---'
	end
end

#eql?(track) ⇒ Boolean

Are two Tracks equal?

Uses ID for comparision.

Returns:

  • (Boolean)


509
510
511
512
513
514
515
# File 'lib/timr/model/track.rb', line 509

def eql?(track)
	unless track.is_a?(Track)
		raise TrackError, "track variable must be a Track instance. #{track.class} given."
	end
	
	self.id == track.id
end

#formatted(options = Hash.new) ⇒ Object

Return formatted String.

Options:

  • ‘:format`

Format:

  • ‘%id` - ID

  • ‘%sid` - Short ID

  • ‘%t` - Title generated from message.

  • ‘%m` - Message

  • ‘%bdt` - Begin DateTime

  • ‘%bd` - Begin Date

  • ‘%bt` - Begin Time

  • ‘%edt` - End DateTime

  • ‘%ed` - End Date

  • ‘%et` - End Time

  • ‘%ds` - Duration Seconds

  • ‘%dh` - Duration Human Format

  • ‘%bi` - Billed Integer

  • ‘%bh` - Billed Human Format (YES, NO)



539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
# File 'lib/timr/model/track.rb', line 539

def formatted(options = Hash.new)
	format = options.fetch(:format, '')
	
	formatted_s = format
		.gsub('%id', self.id)
		.gsub('%sid', self.short_id)
		.gsub('%t', self.title ? self.title : '')
		.gsub('%m', self.message ? self.message : '')
		.gsub('%bdt', self.begin_datetime ? self.begin_datetime.strftime('%F %H:%M') : '')
		.gsub('%bd', self.begin_datetime ? self.begin_datetime.strftime('%F') : '')
		.gsub('%bt', self.begin_datetime ? self.begin_datetime.strftime('%H:%M') : '')
		.gsub('%edt', self.end_datetime ? self.end_datetime.strftime('%F %H:%M') : '')
		.gsub('%ed', self.end_datetime ? self.end_datetime.strftime('%F') : '')
		.gsub('%et', self.end_datetime ? self.end_datetime.strftime('%H:%M') : '')
		.gsub('%ds', self.duration.to_s)
		.gsub('%bi', self.is_billed.to_i.to_s)
		.gsub('%bh', self.is_billed ? 'YES' : 'NO')
	
	duration_human = self.duration.to_human
	if duration_human
		formatted_s.gsub!('%dh', self.duration.to_human)
	else
		formatted_s.gsub!('%dh', '')
	end
	
	task_formating_options = {
		:format => formatted_s,
		:prefix => '%T',
	}
	
	if @task
		formatted_s = @task.formatted(task_formating_options)
	else
		tmp_task = Task.new
		tmp_task.id = ''
		
		formatted_s = tmp_task.formatted(task_formating_options)
	end
	
	formatted_s
end

#inspectObject



581
582
583
# File 'lib/timr/model/track.rb', line 581

def inspect
	"#<Track #{short_id}>"
end

#name(max_length = nil) ⇒ Object

Title Alias



344
345
346
# File 'lib/timr/model/track.rb', line 344

def name(max_length = nil)
	title(max_length)
end

#removeObject

Removes itself from parent Task.



384
385
386
387
388
389
390
# File 'lib/timr/model/track.rb', line 384

def remove
	if @task
		@task.remove_track(self)
	else
		false
	end
end

#running?Boolean

Is the Track running?

Returns:

  • (Boolean)


316
317
318
# File 'lib/timr/model/track.rb', line 316

def running?
	status.short_status == 'R' # running
end

#save_to_file(path = nil, force = false) ⇒ Object

Alias for Task. A Track cannot saved to a file. Only the whole Task.



368
369
370
371
372
# File 'lib/timr/model/track.rb', line 368

def save_to_file(path = nil, force = false)
	if @task
		@task.save_to_file(path, force)
	end
end

#start(options = Hash.new) ⇒ Object

Start this Track. A Track cannot be restarted because it’s the smallest time unit.



163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/timr/model/track.rb', line 163

def start(options = Hash.new)
	message_opt = options.fetch(:message, nil)
	
	if @begin_datetime
		raise TrackError, 'Cannot restart Track. Use dup() on this instance or create a new instance by using Track.new().'
	end
	
	@begin_datetime = DateTimeHelper.get_datetime_from_options(options)
	
	if message_opt
		@message = message_opt
	end
end

#statusObject

Evaluates the Short Status and returns a new Status instance.



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/timr/model/track.rb', line 296

def status
	if @begin_datetime.nil?
		short_status = '-' # not started
	elsif @end_datetime.nil?
		short_status = 'R' # running
	elsif @end_datetime
		if @paused
			# It's actually stopped but with an additional flag.
			short_status = 'P' # paused
		else
			short_status = 'S' # stopped
		end
	else
		short_status = 'U' # unknown
	end
	
	Status.new(short_status)
end

#stop(options = Hash.new) ⇒ Object

Stop this Track.

Options:

  • ‘:start_date`

  • ‘:start_time`

  • ‘:end_date`, `:date`

  • ‘:end_time`, `:time`

  • ‘:message` (String)



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/timr/model/track.rb', line 186

def stop(options = Hash.new)
	start_date_opt = options.fetch(:start_date, nil)
	start_time_opt = options.fetch(:start_time, nil)
	end_date_opt = options.fetch(:end_date, options.fetch(:date, nil))
	end_time_opt = options.fetch(:end_time, options.fetch(:time, nil))
	message_opt = options.fetch(:message, nil)
	# paused_opt = options.fetch(:paused, false)
	
	# Set Start DateTime
	if start_date_opt || start_time_opt
		begin_options = {
			:date => start_date_opt,
			:time => start_time_opt,
		}
		@begin_datetime = DateTimeHelper.get_datetime_from_options(begin_options)
	end
	
	# Set End DateTime
	end_options = {
		:date => end_date_opt,
		:time => end_time_opt,
	}
	@end_datetime = DateTimeHelper.get_datetime_from_options(end_options)
	
	if message_opt
		@message = message_opt
	end
	
	# @paused = paused_opt
	
	# Mark Track as changed.
	changed
end

#stopped?Boolean

Is the Track stopped?

Returns:

  • (Boolean)


321
322
323
# File 'lib/timr/model/track.rb', line 321

def stopped?
	status.short_status == 'S' # stopped
end

#title(max_length = nil) ⇒ Object

Title generated from message. If the message has multiple lines only the first line will be taken to create the title.

‘max_length` can be used to define a maximum length. Three dots `…` will be appended at the end if the title is longer than `max_length`.



330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/timr/model/track.rb', line 330

def title(max_length = nil)
	unless @message
		return
	end
	
	msg = @message.split("\n\n").first.split("\n").first
	
	if max_length && msg.length > max_length + 2
		msg = msg[0, max_length] << '...'
	end
	msg
end

#to_compact_arrayObject

Used to print informations to STDOUT.



422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/timr/model/track.rb', line 422

def to_compact_array
	to_ax = Array.new
	
	if @task
		to_ax.concat(@task.to_track_array)
	end
	
	to_ax << 'Track: %s %s' % [self.short_id, self.title]
	
	# if self.title
	# 	to_ax << 'Title: %s' % [self.title]
	# end
	if self.begin_datetime
		to_ax << 'Start: %s' % [self.begin_datetime_s]
	end
	if self.end_datetime
		to_ax << 'End:   %s' % [self.end_datetime_s]
	end
	
	if self.duration && self.duration.to_i > 0
		to_ax << 'Duration: %s' % [self.duration.to_human_s]
	end
	
	to_ax << 'Status: %s' % [self.status.colorized]
	
	# if self.message
	# 	to_ax << 'Message: %s' % [self.message]
	# end
	
	to_ax
end

#to_compact_strObject

Used to print informations to STDOUT.



417
418
419
# File 'lib/timr/model/track.rb', line 417

def to_compact_str
	to_compact_array.join("\n")
end

#to_detailed_array(options = Hash.new) ⇒ Object

Used to print informations to STDOUT.

Options:

  • ‘:full_id` (Boolean) Show full Task and Track IDs.



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/timr/model/track.rb', line 464

def to_detailed_array(options = Hash.new)
	full_id_opt = options.fetch(:full_id, false) # @TODO full_id unit test
	
	to_ax = Array.new
	
	if @task
		to_ax.concat(@task.to_track_array(options))
	end
	
	if full_id_opt
		to_ax << 'Track: %s' % [self.id]
	else
		to_ax << 'Track: %s' % [self.short_id]
	end
	
	if self.begin_datetime
		to_ax << 'Start: %s' % [self.begin_datetime_s]
	end
	if self.end_datetime
		to_ax << 'End:   %s' % [self.end_datetime_s]
	end
	
	if self.duration && self.duration.to_i > 0
		duration_human = self.duration.to_human_s
		to_ax << 'Duration: %s' % [duration_human]
		
		duration_man_days = self.duration.to_man_days
		if duration_human != duration_man_days
			to_ax << 'Man Unit: %s' % [duration_man_days]
		end
	end
	
	to_ax << 'Billed: %s' % [self.is_billed ? 'Yes' : 'No']
	to_ax << 'Status: %s' % [self.status.colorized]
	
	if self.message
		to_ax << 'Message: %s' % [self.message]
	end
	
	to_ax
end

#to_detailed_str(options = Hash.new) ⇒ Object

Used to print informations to STDOUT.



455
456
457
# File 'lib/timr/model/track.rb', line 455

def to_detailed_str(options = Hash.new)
	to_detailed_array(options).join("\n")
end

#to_hObject

To Hash



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/timr/model/track.rb', line 398

def to_h
	h = {
		'id' => @meta['id'],
		'short_id' => short_id, # Not used.
		'created' => @meta['created'],
		'modified' => @meta['modified'],
		'is_billed' => @is_billed,
		'message' => @message,
	}
	if @begin_datetime
		h['begin_datetime'] = @begin_datetime.utc.strftime(MODEL_DATETIME_FORMAT)
	end
	if @end_datetime
		h['end_datetime'] = @end_datetime.utc.strftime(MODEL_DATETIME_FORMAT)
	end
	h
end

#to_sObject

To String



393
394
395
# File 'lib/timr/model/track.rb', line 393

def to_s
	"Track_#{short_id}"
end

#unbilled_duration(options = Hash.new) ⇒ Object

Alias method.



273
274
275
# File 'lib/timr/model/track.rb', line 273

def unbilled_duration(options = Hash.new)
	duration(options.merge({:billed => false}))
end