1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# frozen_string_literal: true
# == TimeTrackable concern
#
# Contains functionality related to objects that support time tracking.
#
# Used by Issue and MergeRequest.
#
module TimeTrackable
extend ActiveSupport::Concern
included do
attr_reader :time_spent, :time_spent_user, :spent_at, :summary
alias_method :time_spent?, :time_spent
attribute :time_estimate, default: 0
validate :check_time_estimate
validate :check_negative_time_spent
has_many :timelogs, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent
after_initialize :set_time_estimate_default_value
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def spend_time(options)
@time_spent = options[:duration]
@time_spent_note_id = options[:note_id]
@time_spent_user = User.find(options[:user_id])
@spent_at = options[:spent_at]
@summary = options[:summary]
@original_total_time_spent = nil
return if @time_spent == 0
@timelog = if @time_spent == :reset
reset_spent_time
else
add_or_subtract_spent_time
end
end
alias_method :spend_time=, :spend_time
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def total_time_spent
sum = timelogs.sum(:time_spent)
# A new restriction has been introduced to limit total time spent to -
# Timelog::MAX_TOTAL_TIME_SPENT or 3.154e+7 seconds (approximately a year, a generous limit)
# Since there could be existing records that breach the limit, check and return the maximum/minimum allowed value.
# (some issuable might have total time spent that's negative because a validation was missing.)
sum.clamp(-Timelog::MAX_TOTAL_TIME_SPENT, Timelog::MAX_TOTAL_TIME_SPENT)
end
def human_total_time_spent
Gitlab::TimeTrackingFormatter.output(total_time_spent)
end
def time_change
@timelog&.time_spent.to_i # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def human_time_change
Gitlab::TimeTrackingFormatter.output(time_change)
end
def human_time_estimate
Gitlab::TimeTrackingFormatter.output(time_estimate)
end
def time_estimate=(val)
val.is_a?(Integer) ? super([val, Gitlab::Database::MAX_INT_VALUE].min) : super(val)
end
def set_time_estimate_default_value
return if new_record?
return unless has_attribute?(:time_estimate)
self.time_estimate ||= self.class.column_defaults['time_estimate']
end
private
def reset_spent_time
timelogs.new(time_spent: total_time_spent * -1, user: @time_spent_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def add_or_subtract_spent_time
timelogs.new(
time_spent: time_spent,
note_id: @time_spent_note_id,
user: @time_spent_user,
spent_at: @spent_at,
summary: @summary
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def check_negative_time_spent
return if time_spent.nil? || time_spent == :reset
if time_spent < 0 && (time_spent.abs > original_total_time_spent)
errors.add(:base, _('Time to subtract exceeds the total time spent'))
end
end
# we need to cache the total time spent so multiple calls to #valid?
# doesn't give a false error
def original_total_time_spent
@original_total_time_spent ||= total_time_spent
end
def check_time_estimate
return unless new_record? || time_estimate_changed?
return if time_estimate.is_a?(Numeric) && time_estimate >= 0
errors.add(:time_estimate, _('must have a valid format and be greater than or equal to zero.'))
end
end
|