Class: TheFox::Timr::Model::Task

Inherits:
BasicModel show all
Includes:
Error
Defined in:
lib/timr/model/task.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

#changed, create_path_by_id, #created=, #delete_file, find_file_by_id, get_id_from_path, #id, #id=, #load_from_file, #modified=, #post_save_to_file, #pre_load_from_file, #save_to_file, #short_id

Constructor Details

#initializeTask

Returns a new instance of Task.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/timr/model/task.rb', line 16

def initialize
	super()
	
	# Meta
	@foreign_id = nil # --id
	@name = nil
	@description = nil
	@current_track = nil
	@estimation = nil
	@hourly_rate = nil
	@has_flat_rate = false
	
	# Data
	@tracks = Hash.new
end

Instance Attribute Details

#current_trackObject (readonly)

Returns the value of attribute current_track.



12
13
14
# File 'lib/timr/model/task.rb', line 12

def current_track
  @current_track
end

#descriptionObject

Returns the value of attribute description.



11
12
13
# File 'lib/timr/model/task.rb', line 11

def description
  @description
end

#foreign_idObject

Returns the value of attribute foreign_id.



10
11
12
# File 'lib/timr/model/task.rb', line 10

def foreign_id
  @foreign_id
end

#has_flat_rateObject

Returns the value of attribute has_flat_rate.



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

def has_flat_rate
  @has_flat_rate
end

#hourly_rateObject

Returns the value of attribute hourly_rate.



13
14
15
# File 'lib/timr/model/task.rb', line 13

def hourly_rate
  @hourly_rate
end

Class Method Details

.create_task_from_hash(options) ⇒ Object

Create a new Task using a Hash.

Options:

  • ‘:name` (String)

  • ‘:description` (String)

  • ‘:estimation` (String|Integer|Duration)

  • ‘:hourly_rate` (Integer)



1017
1018
1019
1020
1021
1022
1023
1024
1025
# File 'lib/timr/model/task.rb', line 1017

def create_task_from_hash(options)
	task = Task.new
	task.name = options.fetch(:name, nil)
	task.description = options.fetch(:description, nil)
	task.estimation = options.fetch(:estimation, nil)
	task.hourly_rate = options.fetch(:hourly_rate, nil)
	task.has_flat_rate = options.fetch(:has_flat_rate, false)
	task
end

.load_task_from_file(path) ⇒ Object

Load a Task from a file into a Task instance.



994
995
996
997
998
# File 'lib/timr/model/task.rb', line 994

def load_task_from_file(path)
	task = Task.new
	task.load_from_file(path)
	task
end

.load_task_from_file_with_id(base_path, task_id) ⇒ Object

Search a Task in a base path for a Track by ID. If found a file load it into a Task instance.



1002
1003
1004
1005
1006
1007
# File 'lib/timr/model/task.rb', line 1002

def load_task_from_file_with_id(base_path, task_id)
	task_file_path = BasicModel.find_file_by_id(base_path, task_id)
	if task_file_path
		load_task_from_file(task_file_path)
	end
end

Instance Method Details

#add_track(track, set_as_current_track = false) ⇒ Object

Add a Track.



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

def add_track(track, set_as_current_track = false)
	track.task = self
	
	@tracks[track.id] = track
	
	if set_as_current_track
		@current_track = track
	end
	
	# Mark Task as changed.
	changed
end

#begin_datetime(options = Hash.new) ⇒ Object

Uses ‘tracks()` with `options` to filter.

Options:

  • ‘:from`



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/timr/model/task.rb', line 317

def begin_datetime(options = Hash.new)
	from_opt = options.fetch(:from, nil)
	
	# Cache
	# if @begin_datetime
	# 	return @begin_datetime
	# end
	# Cannot use this cache because of :from :to range limitation.
	# It needs always to be direct from child Tracks, because the
	# cache does not know when the begin and end datetimes of the
	# child Tracks change.
	
	# Do not sort. We only need to sort the tracks
	# by begin_datetime and take the first.
	options[:sort] = false
	
	first_track = tracks(options)
		.select{ |track_id, track| track.begin_datetime } # filter nil
		.sort_by{ |track_id, track| track.begin_datetime }
		.to_h # sort_by makes [[]]
		.values # no keys to take the first
		.first
	
	if first_track
		bdt = first_track.begin_datetime(options)
	end
	
	if from_opt && bdt && from_opt > bdt
		bdt = from_opt
	end
	
	bdt
end

#begin_datetime_s(options = Hash.new) ⇒ Object

Options:

  • ‘:format`



354
355
356
357
358
359
360
361
362
363
# File 'lib/timr/model/task.rb', line 354

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 for ‘duration` method.

Options:

  • ‘:billed` (Boolean)



660
661
662
# File 'lib/timr/model/task.rb', line 660

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

#consumed_budgeObject

Get the actual consumed budge.



503
504
505
506
507
508
509
# File 'lib/timr/model/task.rb', line 503

def consumed_budge
	if @hourly_rate
		duration.to_i.to_f / 3600.0 * @hourly_rate
	else
		0.0
	end
end

#continue(options = Hash.new) ⇒ Object

Continues the current Track. Only if it isn’t already running.



608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/timr/model/task.rb', line 608

def continue(options = Hash.new)
	track_opt = options.fetch(:track, nil)
	
	if @current_track
		if @current_track.stopped?
			
			# Duplicate and start.
			@current_track = @current_track.dup
			@current_track.start(options)
			
			
			add_track(@current_track)
		else
			raise TrackError, "Cannot continue Track #{@current_track.short_id}, is already running."
		end
	else
		unless track_opt
			raise TaskError, 'No Track given.'
		end
		
		# Duplicate and start.
		@current_track = track_opt.dup
		@current_track.start(options)
		
		add_track(@current_track)
	end
	
	@current_track
end

#duration(options = Hash.new) ⇒ Object

Consumed duration.

Options:

  • ‘:billed` (Boolean)



643
644
645
646
647
648
649
650
651
652
653
# File 'lib/timr/model/task.rb', line 643

def duration(options = Hash.new)
	billed_opt = options.fetch(:billed, nil)
	
	duration = Duration.new
	@tracks.each do |track_id, track|
		if billed_opt.nil? || (billed_opt && track.is_billed) || (!billed_opt && !track.is_billed)
			duration += track.duration(options)
		end
	end
	duration
end

#end_datetime(options = Hash.new) ⇒ Object

Uses ‘tracks()` with `options` to filter.

Options:

  • ‘:to`



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/timr/model/task.rb', line 370

def end_datetime(options = Hash.new)
	to_opt = options.fetch(:to, nil)
	
	# Cache
	# if @end_datetime
	# 	return @end_datetime
	# end
	# Cannot use this cache because of :from :to range limitation.
	# It needs always to be direct from child Tracks, because the
	# cache does not know when the begin and end datetimes of the
	# child Tracks change.
	
	# Do not sort. We only need to sort the tracks
	# by end_datetime and take the last.
	options[:sort] = false
	
	last_track = tracks(options)
		.select{ |track_id, track| track.end_datetime } # filter nil
		.sort_by{ |track_id, track| track.end_datetime }
		.to_h # sort_by makes [[]]
		.values # no keys to take the last
		.last
	
	if last_track
		edt = last_track.end_datetime(options)
	end
	
	if to_opt && edt && to_opt < edt
		edt = to_opt
	end
	
	edt
end

#end_datetime_s(options = Hash.new) ⇒ Object

Options:

  • ‘:format`



407
408
409
410
411
412
413
414
415
416
# File 'lib/timr/model/task.rb', line 407

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?(task) ⇒ Boolean

Are two Tasks equal?

Uses ID for comparision.

Returns:

  • (Boolean)


767
768
769
770
771
772
773
# File 'lib/timr/model/task.rb', line 767

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

#estimated_budgeObject

Calculate the budge based on estimation.



512
513
514
515
516
517
518
# File 'lib/timr/model/task.rb', line 512

def estimated_budge
	if @hourly_rate
		estimation.to_i.to_f / 3600.0 * @hourly_rate
	else
		0.0
	end
end

#estimationObject

Get estimation.



469
470
471
# File 'lib/timr/model/task.rb', line 469

def estimation
	@estimation
end

#estimation=(estimation) ⇒ Object

Set estimation.

Either using a Duration instance, Integer or a String like ‘2h 30m`. Estimation is parsed by [chronic_duration](github.com/henrypoydar/chronic_duration).

Examples:

  • ‘-e 2:10:5`

    Sets Estimation to 2h 10m 5s.

  • ‘-e ’2h 10m 5s’‘

    Sets Estimation to 2h 10m 5s.

Use ‘+` or `-` to calculate with Estimation Times:

  • ‘-e ’-45m’‘

    Subtracts 45 minutes from the original Estimation.

  • ‘-e ’+1h 30m’‘

    Adds 1 hour 30 minutes to the original Estimation.

See [chronic_duration](github.com/henrypoydar/chronic_duration) for more examples.



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/timr/model/task.rb', line 439

def estimation=(estimation)
	case estimation
	when String
		# Cannot use estimation.strip! because frozen.
		estimation = estimation.strip
		
		if estimation[0] == '+'
			estimation = estimation[1..-1]
			@estimation += Duration.parse(estimation)
		elsif estimation[0] == '-'
			estimation = estimation[1..-1]
			@estimation -= Duration.parse(estimation)
		else
			@estimation = Duration.parse(estimation)
		end
	when Integer
		@estimation = Duration.new(estimation)
	when Duration
		@estimation = estimation
	when nil
		@estimation = estimation
	else
		raise TaskError, "estimation needs to be an instance of String, Integer, Duration or nil, #{estimation.class} given."
	end
	
	# Mark Task as changed.
	changed
end

#estimation_sObject

Get estimation as String.



474
475
476
477
478
479
480
# File 'lib/timr/model/task.rb', line 474

def estimation_s
	if @estimation
		@estimation.to_human_s
	else
		'---'
	end
end

#find_track_by_id(track_id) ⇒ Object

Find a Track by ID even if the ID is not 40 characters long. When the ID is 40 characters long ‘@tracks` is faster. ;)



739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
# File 'lib/timr/model/task.rb', line 739

def find_track_by_id(track_id)
	track_id_len = track_id.length
	
	# puts "search track id '#{track_id}'"
	
	if track_id_len < 40
		found_track_id = nil
		@tracks.keys.each do |key|
			if track_id == key[0, track_id_len]
				if found_track_id
					raise TrackError, "Track ID '#{track_id}' is not a unique identifier."
				else
					found_track_id = key
					
					# Do not break the loop here.
					# Iterate all keys to make sure the ID is unique.
				end
			end
		end
		track_id = found_track_id
	end
	
	@tracks[track_id]
end

#formatted(options = Hash.new) ⇒ Object

Return formatted String.

Options:

  • ‘:format`

  • ‘:prefix` - Default: `%`

Format:

  • ‘%id` - ID

  • ‘%sid` - Short ID

  • ‘%fid` - Foreign ID

  • ‘%n` - Name

  • ‘%d` - Description

  • ‘%ds` - Duration Seconds

  • ‘%dh` - Duration Human Format



962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
# File 'lib/timr/model/task.rb', line 962

def formatted(options = Hash.new)
	format = options.fetch(:format, '')
	prefix = options.fetch(:prefix, '%')
	
	formatted_s = format
		.gsub("#{prefix}id", self.id)
		.gsub("#{prefix}sid", self.short_id ? self.short_id : '')
		.gsub("#{prefix}fid", self.foreign_id ? self.foreign_id : '')
		.gsub("#{prefix}n", self.name ? self.name : '')
		.gsub("#{prefix}ds", self.duration(options).to_s)
	
	duration_human = self.duration(options).to_human
	if duration_human
		formatted_s.gsub!('%dh', duration_human)
	else
		formatted_s.gsub!('%dh', '')
	end
	
	# Must not before %dh and %ds.
	formatted_s.gsub!("#{prefix}d", self.description ? self.description : '')
	
	formatted_s
end

#id_foreign_or_shortObject

Get Foreign ID or Short ID.



33
34
35
# File 'lib/timr/model/task.rb', line 33

def id_foreign_or_short
	@foreign_id ? @foreign_id : short_id
end

#inspectObject



986
987
988
# File 'lib/timr/model/task.rb', line 986

def inspect
	"#<Task_#{short_id} tracks=#{@tracks.count}>"
end

#is_billed=(is_billed) ⇒ Object

Set is_billed.



731
732
733
734
735
# File 'lib/timr/model/task.rb', line 731

def is_billed=(is_billed)
	@tracks.each do |track_id, track|
		track.is_billed = is_billed
	end
end

#loss_budgeObject

Calculates the budge loss when a Flat Rate is used and the consumed duration is greater than the estimation.



521
522
523
524
525
526
527
528
529
530
531
# File 'lib/timr/model/task.rb', line 521

def loss_budge
	if @has_flat_rate && @hourly_rate
		if duration > estimation
			(duration - estimation).to_i.to_f / 3600.0 * @hourly_rate
		else
			0.0
		end
	else
		0.0
	end
end

#move_track(track, target_task) ⇒ Object

Move a Track to another Task.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/timr/model/task.rb', line 107

def move_track(track, target_task)
	if eql?(target_task)
		return false
	end
	
	unless remove_track(track)
		return false
	end
	
	set_as_current_track = false
	if @current_track && @current_track.eql?(track)
		@current_track = nil
		set_as_current_track = true
	end
	
	target_task.add_track(track, set_as_current_track)
	
	true
end

#name(max_length = nil) ⇒ Object

Get name.



53
54
55
56
57
58
59
# File 'lib/timr/model/task.rb', line 53

def name(max_length = nil)
	name = @name
	if name && max_length && name.length > max_length + 2
		name = name[0, max_length] << '...'
	end
	name
end

#name=(name) ⇒ Object

Set name.



45
46
47
48
49
50
# File 'lib/timr/model/task.rb', line 45

def name=(name)
	@name = name
	
	# Mark Task as changed.
	changed
end

#name_s(max_length = nil) ⇒ Object

Get name or ‘—` if name is not set.



62
63
64
65
66
67
68
69
# File 'lib/timr/model/task.rb', line 62

def name_s(max_length = nil)
	s = name(max_length)
	if s.nil?
		'---'
	else
		s
	end
end

#pause(options = Hash.new) ⇒ Object

Pauses a current running Track.



595
596
597
598
599
600
601
602
603
604
# File 'lib/timr/model/task.rb', line 595

def pause(options = Hash.new)
	if @current_track
		@current_track.stop(options)
		
		# Mark Task as changed.
		changed
		
		@current_track
	end
end

#remaining_timeObject

Get the remaining Time of estimation.

Returns a Duration instance.



676
677
678
679
680
# File 'lib/timr/model/task.rb', line 676

def remaining_time
	if @estimation
		estimation - duration
	end
end

#remaining_time_percentObject

Get the remaining Time as percent.



696
697
698
699
700
701
# File 'lib/timr/model/task.rb', line 696

def remaining_time_percent
	rmt = remaining_time
	if rmt && @estimation
		(rmt.to_i.to_f / @estimation.to_i.to_f) * 100.0
	end
end

#remaining_time_percent_sObject

Get the remaining Time Percent as String.



704
705
706
707
708
709
710
711
# File 'lib/timr/model/task.rb', line 704

def remaining_time_percent_s
	rmtp = remaining_time_percent
	if rmtp
		'%.1f %%' % [rmtp]
	else
		'---'
	end
end

#remaining_time_sObject

Get the remaining Time as Human String.

  • Like ‘2h 30m`.

  • Or ‘—` when `@estimation` is `nil`.



686
687
688
689
690
691
692
693
# File 'lib/timr/model/task.rb', line 686

def remaining_time_s
	rmt = remaining_time
	if rmt
		rmt.to_human_s
	else
		'---'
	end
end

#remove_track(track) ⇒ Object

Remove a Track.



94
95
96
97
98
99
100
101
102
103
104
# File 'lib/timr/model/task.rb', line 94

def remove_track(track)
	track.task = nil
	
	if @tracks.delete(track.id)
		# Mark Task as changed.
		changed
	else
		# Track is not assiged to this Task.
		false
	end
end

#resetObject



127
128
129
130
131
132
133
134
# File 'lib/timr/model/task.rb', line 127

def reset
	if @current_track
		@current_track = nil
		
		# Mark Task as changed.
		changed
	end
end

#start(options = Hash.new) ⇒ Object

Start a new Track by given ‘options`.

Options:

  • ‘:foreign_id` (String)

  • ‘:track_id` (String)

  • ‘:no_stop` (Boolean)



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
# File 'lib/timr/model/task.rb', line 540

def start(options = Hash.new)
	foreign_id_opt = options.fetch(:foreign_id, nil)
	track_id_opt = options.fetch(:track_id, nil)
	
	# Used by Push.
	no_stop_opt = options.fetch(:no_stop, false)
	
	unless no_stop_opt
		# End current Track before starting a new one.
		# Leave options empty here for stop().
		stop
	end
	
	if foreign_id_opt && @foreign_id.nil?
		@foreign_id = foreign_id_opt
	end
	
	if track_id_opt
		found_track = find_track_by_id(track_id_opt)
		if found_track
			
			@current_track = found_track.dup
		else
			raise TrackError, "No Track found for Track ID '#{track_id_opt}'."
		end
	else
		@current_track = Track.new
		@current_track.task = self
	end
	
	@tracks[@current_track.id] = @current_track
	
	# Mark Task as changed.
	changed
	
	@current_track.start(options)
	@current_track
end

#statusObject

Get Task status as Status instance.



714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/timr/model/task.rb', line 714

def status
	stati = @tracks.map{ |track_id, track| track.status.short_status }.to_set
	
	if @tracks.count == 0
		status = ?-
	elsif stati.include?(?R)
		status = ?R
	elsif stati.include?(?S)
		status = ?S
	else
		status = ?U
	end
	
	Status.new(status)
end

#stop(options = Hash.new) ⇒ Object

Stops a current running Track.



580
581
582
583
584
585
586
587
588
589
590
591
592
# File 'lib/timr/model/task.rb', line 580

def stop(options = Hash.new)
	if @current_track
		@current_track.stop(options)
		
		# Reset current Track variable.
		@current_track = nil
		
		# Mark Task as changed.
		changed
	end
	
	nil
end

#to_compact_arrayObject

Used to print informations to STDOUT.



806
807
808
809
810
811
812
813
814
815
816
817
818
# File 'lib/timr/model/task.rb', line 806

def to_compact_array
	full_id = self.foreign_id ? self.foreign_id : self.short_id
	
	to_ax = Array.new
	to_ax << 'Task: %s %s' % [full_id, self.name]
	if self.description
		to_ax << 'Description: %s' % [self.description]
	end
	if self.estimation
		to_ax << 'Estimation: %s' % [self.estimation.to_human_s]
	end
	to_ax
end

#to_compact_strObject

Used to print informations to STDOUT.



801
802
803
# File 'lib/timr/model/task.rb', line 801

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 ID.



830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
# File 'lib/timr/model/task.rb', line 830

def to_detailed_array(options = Hash.new)
	full_id_opt = options.fetch(:full_id, false)
	
	full_id = full_id_opt ? self.id : self.short_id
	
	to_ax = Array.new
	
	to_ax << 'Task: %s' % [full_id]
	
	if self.foreign_id
		to_ax << 'Foreign ID: %s' % [self.foreign_id]
	end
	
	to_ax << 'Name: %s' % [self.name]
	
	if self.description
		to_ax << 'Description: %s' % [self.description]
	end
	
	# Duration
	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
	
	# Billed Duration
	billed_duration_human = self.billed_duration.to_human
	to_ax << 'Billed   Duration: %s' % [billed_duration_human]
	
	# Unbilled Duration
	unbilled_duration_human = self.unbilled_duration.to_human
	to_ax << 'Unbilled Duration: %s' % [unbilled_duration_human]
	
	if self.estimation
		to_ax << 'Estimation:     %s' % [self.estimation.to_human]
		
		to_ax << 'Time Remaining: %s (%s)' % [self.remaining_time_s, self.remaining_time_percent_s]
		
		bar_options = {
			:total => self.estimation.to_i,
			:progress => self.duration.to_i,
			:length => 50,
			:progress_mark => '#',
			:remainder_mark => '-',
		}
		bar = ProgressBar.new(bar_options)
		
		to_ax << '                |%s|' % [bar.render]
	end
	
	if self.hourly_rate
		to_ax << 'Hourly Rate:     %.2f' % [self.hourly_rate]
		to_ax << 'Flat Rate:       %s' % [@has_flat_rate ? 'Yes' : 'No']
		
		to_ax << 'Consumed Budge:  %.2f' % [self.consumed_budge]
		
		if self.estimation
			to_ax << 'Estimated Budge: %.2f' % [self.estimated_budge]
		end
		
		if @has_flat_rate
			to_ax << 'Loss Budge:      %.2f' % [self.loss_budge]
		end
	end
	
	tracks = self.tracks
	first_track = tracks
		.select{ |track_id, track| track.begin_datetime }
		.sort_by{ |track_id, track| track.begin_datetime }
		.to_h
		.values
		.first
	if first_track
		to_ax << 'Begin Track: %s  %s' % [first_track.short_id, first_track.begin_datetime_s]
	end
	
	last_track = tracks
		.select{ |track_id, track| track.end_datetime }
		.sort_by{ |track_id, track| track.end_datetime }
		.to_h
		.values
		.last
	if last_track
		to_ax << 'End   Track: %s  %s' % [last_track.short_id, last_track.end_datetime_s]
	end
	
	status = self.status.colorized
	to_ax << 'Status: %s' % [status]
	
	if @current_track
		to_ax << 'Current Track: %s %s' % [@current_track.short_id, @current_track.title]
	end
	
	tracks_count = tracks.count
	to_ax << 'Tracks:          %d' % [tracks_count]
	
	billed_tracks_count = tracks({:billed => true}).count
	to_ax << 'Billed Tracks:   %d' % [billed_tracks_count]
	
	unbilled_tracks_count = tracks({:billed => false}).count
	to_ax << 'Unbilled Tracks: %d' % [unbilled_tracks_count]
	
	if tracks_count > 0 && @tracks_opt # --tracks
		to_ax << 'Track IDs: %s' % [tracks.map{ |track_id, track| track.short_id }.join(' ')]
	end
	
	if self.file_path
		to_ax << 'File path: %s' % [self.file_path]
	end
	
	to_ax
end

#to_detailed_strObject

Used to print informations to STDOUT.



821
822
823
# File 'lib/timr/model/task.rb', line 821

def to_detailed_str
	to_detailed_array.join("\n")
end

#to_sObject

To String



776
777
778
# File 'lib/timr/model/task.rb', line 776

def to_s
	"Task_#{short_id}"
end

#to_track_array(options = Hash.new) ⇒ Object

Use to print informations for Track.

Options:

  • ‘:full_id` (Boolean) Show full Task ID.



785
786
787
788
789
790
791
792
793
794
795
796
797
798
# File 'lib/timr/model/task.rb', line 785

def to_track_array(options = Hash.new)
	full_id_opt = options.fetch(:full_id, false) # @TODO full_id unit test
	
	full_id = full_id_opt ? self.id : ( self.foreign_id ? self.foreign_id : self.short_id )
	
	name_a = ['Task:', full_id]
	if self.name
		name_a << self.name
	end
	
	to_ax = Array.new
	to_ax << name_a.join(' ')
	to_ax
end

#tracks(options = Hash.new) ⇒ Object

Select Track by Time Range and/or Status.

Options:

  • ‘:from`, `:to` limit the begin and end datetimes to a specific range.

  • ‘:status` filter Tracks by Short Status.

  • ‘:billed` filter Tracks by is_billed flag.

- ‘true` filter billed Tracks. - `false` filter unbilled Tracks. - `nil` filter off.

Fixed Start and End (‘from != nil && to != nil`)

“‘ Selected Range |———-| Track A ----------------- Track B ------ Track C ------------ Track D ---- Track E --- Track F --- “`

  • Track A is bigger then the Options range. Take it.

  • Track B ends in the Options range. Take it.

  • Track C starts in the Options range. Take it.

  • Track D starts and ends within the Options range. Definitely take this.

  • Track E is out-of-score. Ignore it.

  • Track F is out-of-score. Ignore it.


Open End (‘to == nil`)

Take all except Track E.

“‘ Selected Range |———-> Track A ----------------- Track B ------ Track C ------------ Track D ---- Track E --- Track F --- “`


Open Start (‘from == nil`)

Take all except Track F.

“‘ Selected Range <———-| Track A ----------------- Track B ------ Track C ------------ Track D ---- Track E --- Track F --- “`



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/timr/model/task.rb', line 195

def tracks(options = Hash.new)
	from_opt = options.fetch(:from, nil)
	to_opt = options.fetch(:to, nil)
	status_opt = options.fetch(:status, nil)
	sort_opt = options.fetch(:sort, true)
	billed_opt = options.fetch(:billed, nil)
	
	if status_opt
		case status_opt
		when String
			status_opt = [status_opt]
		when Array
			# OK
		else
			raise TaskError, ":status needs to be an instance of String or Array, #{status_opt.class} given."
		end
	end
	
	if from_opt && to_opt && from_opt > to_opt
		raise TaskError, 'From cannot be bigger than To.'
	end
	
	filtered_tracks = Hash.new
	if from_opt.nil? && to_opt.nil?
		# Take all Tracks.
		filtered_tracks = @tracks.select{ |track_id, track|
			# Filter Tracks with no Begin DateTime.
			# This can happen when 'timr track add' without any DateTime.
			!track.begin_datetime.nil?
		}
	elsif !from_opt.nil? && to_opt.nil?
		# Open End (to_opt == nil)
		filtered_tracks = @tracks.select{ |track_id, track|
			bdt = track.begin_datetime
			edt = track.end_datetime || Time.now
			
			bdt && ( \
				# Track A, B
				bdt <  from_opt && edt >  from_opt || \
				
				# Track C, D, F
				bdt >= from_opt && edt >= from_opt
			)
		}
	elsif from_opt.nil? && !to_opt.nil?
		# Open Start (from_opt == nil)
		filtered_tracks = @tracks.select{ |track_id, track|
			bdt = track.begin_datetime
			edt = track.end_datetime || Time.now
			
			bdt && ( \
				# Track B, D, E
				bdt <  to_opt && edt <= to_opt || \
				
				# Track A, C
				bdt <  to_opt && edt >  to_opt
			)
		}
	elsif !from_opt.nil? && !to_opt.nil?
		# Fixed Start and End (from_opt != nil && to_opt != nil)
		filtered_tracks = @tracks.select{ |track_id, track|
			bdt = track.begin_datetime
			edt = track.end_datetime || Time.now
			
			bdt && ( \
				# Track D
				bdt >= from_opt && edt <= to_opt || \
				
				# Track A
				bdt <  from_opt && edt >  to_opt || \
				
				# Track B
				bdt <  from_opt && edt <= to_opt && edt > from_opt || \
				
				# Track C
				bdt >= from_opt && edt >  to_opt && bdt < to_opt
			)
		}
	else
		raise ThisShouldNeverHappenError, 'Should never happen, bug shit happens.'
	end
	
	if status_opt
		filtered_tracks.select!{ |track_id, track|
			status_opt.include?(track.status.short_status)
		}
	end
	
	unless billed_opt.nil?
		if billed_opt
			filtered_tracks.select!{ |track_id, track|
				track.is_billed
			}
		else
			filtered_tracks.select!{ |track_id, track|
				!track.is_billed
			}
		end
	end
	
	if sort_opt
		filtered_tracks.sort{ |t1, t2|
			t1 = t1.last
			t2 = t2.last
			
			cmp1 = t1.begin_datetime <=> t2.begin_datetime
			if cmp1.nil? || cmp1 == 0
				t1.end_datetime <=> t2.end_datetime
			else
				cmp1
			end
		}.to_h
	else
		filtered_tracks
	end
end

#unbilled_duration(options = Hash.new) ⇒ Object

Alias for ‘duration` method.

Options:

  • ‘:billed` (Boolean)



669
670
671
# File 'lib/timr/model/task.rb', line 669

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