91 |
91 |
end
|
92 |
92 |
|
93 |
93 |
TIMELOG_RE = /
|
94 |
|
(
|
95 |
|
((\d+)(h|hours?))((\d+)(m|min)?)?
|
|
94 |
(?:
|
|
95 |
(?:\d+(?:h|hours?))(?:\d+(?:m|min)?)?
|
96 |
96 |
|
|
97 |
|
((\d+)(h|hours?|m|min))
|
|
97 |
(?:\d+(?:h|hours?|m|min))
|
98 |
98 |
|
|
99 |
|
(\d+):(\d+)
|
|
99 |
\d+:\d+
|
100 |
100 |
|
|
101 |
|
(\d+([\.,]\d+)?)h?
|
|
101 |
(?:\d+(?:[\.,]\d+)?)h?
|
102 |
102 |
)
|
103 |
103 |
/x
|
104 |
104 |
|
|
105 |
DONE_RE = /\d0%/
|
|
106 |
|
105 |
107 |
def scan_comment_for_issue_ids
|
106 |
108 |
return if comments.blank?
|
107 |
109 |
# keywords used to reference issues
|
... | ... | |
114 |
116 |
|
115 |
117 |
referenced_issues = []
|
116 |
118 |
|
117 |
|
comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
|
118 |
|
action, refs = match[2], match[3]
|
|
119 |
comments.scan(/(?:[\s\(\[,-]|^)(?:(#{kw_regexp})[\s:]+)?(#\d+(?:\s+@#{DONE_RE})?(?:\s+@#{TIMELOG_RE})?(?:[\s,;&]+#\d+(?:\s+@#{DONE_RE})?(?:\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
|
|
120 |
action, refs = match[0], match[1]
|
119 |
121 |
next unless action.present? || ref_keywords_any
|
120 |
122 |
|
121 |
|
refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
|
122 |
|
issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
|
|
123 |
refs.scan(/#(\d+)(?:\s+@(#{DONE_RE}))?(?:\s+@(#{TIMELOG_RE}))?/).each do |m|
|
|
124 |
issue, done_ratio, hours = find_referenced_issue_by_id(m[0].to_i), m[1], m[2]
|
123 |
125 |
if issue
|
124 |
126 |
referenced_issues << issue
|
125 |
|
fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
|
|
127 |
if fix_keywords.include?(action.to_s.downcase)
|
|
128 |
fix_issue(issue)
|
|
129 |
elsif done_ratio.present?
|
|
130 |
update_issue(issue, done_ratio.sub(/[^\d]+/,'').to_i, comments)
|
|
131 |
end
|
126 |
132 |
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
|
127 |
133 |
end
|
128 |
134 |
end
|
... | ... | |
215 |
221 |
issue
|
216 |
222 |
end
|
217 |
223 |
|
|
224 |
def update_issue(issue, done_ratio, notes)
|
|
225 |
status = 8 # "In Progress" - TODO make this configurable
|
|
226 |
#status = IssueStatus.find_by_id(Setting.commit_progress_status_id.to_i)
|
|
227 |
if status.nil?
|
|
228 |
logger.warn("No status macthes commit_progress_status_id setting (#{Setting.commit_progress_status_id})") if logger
|
|
229 |
return issue
|
|
230 |
end
|
|
231 |
|
|
232 |
# the issue may have been updated by the closure of another one (eg. duplicate)
|
|
233 |
issue.reload
|
|
234 |
# don't change the status is the issue is closed
|
|
235 |
return if issue.status && issue.status.is_closed?
|
|
236 |
|
|
237 |
notes = ll(Setting.default_language, :text_status_changed_by_changeset, text_tag) + "\n\n" + notes
|
|
238 |
journal = issue.init_journal(user || User.anonymous, notes)
|
|
239 |
issue.status = status
|
|
240 |
issue.done_ratio = done_ratio
|
|
241 |
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
|
|
242 |
{ :changeset => self, :issue => issue })
|
|
243 |
unless issue.save
|
|
244 |
logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
|
|
245 |
end
|
|
246 |
issue
|
|
247 |
end
|
|
248 |
|
218 |
249 |
def log_time(issue, hours)
|
219 |
250 |
time_entry = TimeEntry.new(
|
220 |
251 |
:user => user,
|