##// END OF EJS Templates
comments: remove usage of pylons global config.
marcink -
r2005:08e45e06 default
parent child Browse files
Show More
@@ -1,655 +1,652 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 comments model for RhodeCode
23 23 """
24 24
25 25 import logging
26 26 import traceback
27 27 import collections
28 28
29 from datetime import datetime
30
31 29 from pylons.i18n.translation import _
32 30 from pyramid.threadlocal import get_current_registry, get_current_request
33 31 from sqlalchemy.sql.expression import null
34 32 from sqlalchemy.sql.functions import coalesce
35 33
36 34 from rhodecode.lib import helpers as h, diffs, channelstream
37 35 from rhodecode.lib import audit_logger
38 from rhodecode.lib.channelstream import channelstream_request
39 36 from rhodecode.lib.utils2 import extract_mentioned_users, safe_str
40 37 from rhodecode.model import BaseModel
41 38 from rhodecode.model.db import (
42 39 ChangesetComment, User, Notification, PullRequest, AttributeDict)
43 40 from rhodecode.model.notification import NotificationModel
44 41 from rhodecode.model.meta import Session
45 42 from rhodecode.model.settings import VcsSettingsModel
46 43 from rhodecode.model.notification import EmailNotificationModel
47 44 from rhodecode.model.validation_schema.schemas import comment_schema
48 45
49 46
50 47 log = logging.getLogger(__name__)
51 48
52 49
53 50 class CommentsModel(BaseModel):
54 51
55 52 cls = ChangesetComment
56 53
57 54 DIFF_CONTEXT_BEFORE = 3
58 55 DIFF_CONTEXT_AFTER = 3
59 56
60 57 def __get_commit_comment(self, changeset_comment):
61 58 return self._get_instance(ChangesetComment, changeset_comment)
62 59
63 60 def __get_pull_request(self, pull_request):
64 61 return self._get_instance(PullRequest, pull_request)
65 62
66 63 def _extract_mentions(self, s):
67 64 user_objects = []
68 65 for username in extract_mentioned_users(s):
69 66 user_obj = User.get_by_username(username, case_insensitive=True)
70 67 if user_obj:
71 68 user_objects.append(user_obj)
72 69 return user_objects
73 70
74 71 def _get_renderer(self, global_renderer='rst'):
72 request = get_current_request()
73
75 74 try:
76 # try reading from visual context
77 from pylons import tmpl_context
78 global_renderer = tmpl_context.visual.default_renderer
75 global_renderer = request.call_context.visual.default_renderer
79 76 except AttributeError:
80 77 log.debug("Renderer not set, falling back "
81 78 "to default renderer '%s'", global_renderer)
82 79 except Exception:
83 80 log.error(traceback.format_exc())
84 81 return global_renderer
85 82
86 83 def aggregate_comments(self, comments, versions, show_version, inline=False):
87 84 # group by versions, and count until, and display objects
88 85
89 86 comment_groups = collections.defaultdict(list)
90 87 [comment_groups[
91 88 _co.pull_request_version_id].append(_co) for _co in comments]
92 89
93 90 def yield_comments(pos):
94 91 for co in comment_groups[pos]:
95 92 yield co
96 93
97 94 comment_versions = collections.defaultdict(
98 95 lambda: collections.defaultdict(list))
99 96 prev_prvid = -1
100 97 # fake last entry with None, to aggregate on "latest" version which
101 98 # doesn't have an pull_request_version_id
102 99 for ver in versions + [AttributeDict({'pull_request_version_id': None})]:
103 100 prvid = ver.pull_request_version_id
104 101 if prev_prvid == -1:
105 102 prev_prvid = prvid
106 103
107 104 for co in yield_comments(prvid):
108 105 comment_versions[prvid]['at'].append(co)
109 106
110 107 # save until
111 108 current = comment_versions[prvid]['at']
112 109 prev_until = comment_versions[prev_prvid]['until']
113 110 cur_until = prev_until + current
114 111 comment_versions[prvid]['until'].extend(cur_until)
115 112
116 113 # save outdated
117 114 if inline:
118 115 outdated = [x for x in cur_until
119 116 if x.outdated_at_version(show_version)]
120 117 else:
121 118 outdated = [x for x in cur_until
122 119 if x.older_than_version(show_version)]
123 120 display = [x for x in cur_until if x not in outdated]
124 121
125 122 comment_versions[prvid]['outdated'] = outdated
126 123 comment_versions[prvid]['display'] = display
127 124
128 125 prev_prvid = prvid
129 126
130 127 return comment_versions
131 128
132 129 def get_unresolved_todos(self, pull_request, show_outdated=True):
133 130
134 131 todos = Session().query(ChangesetComment) \
135 132 .filter(ChangesetComment.pull_request == pull_request) \
136 133 .filter(ChangesetComment.resolved_by == None) \
137 134 .filter(ChangesetComment.comment_type
138 135 == ChangesetComment.COMMENT_TYPE_TODO)
139 136
140 137 if not show_outdated:
141 138 todos = todos.filter(
142 139 coalesce(ChangesetComment.display_state, '') !=
143 140 ChangesetComment.COMMENT_OUTDATED)
144 141
145 142 todos = todos.all()
146 143
147 144 return todos
148 145
149 146 def get_commit_unresolved_todos(self, commit_id, show_outdated=True):
150 147
151 148 todos = Session().query(ChangesetComment) \
152 149 .filter(ChangesetComment.revision == commit_id) \
153 150 .filter(ChangesetComment.resolved_by == None) \
154 151 .filter(ChangesetComment.comment_type
155 152 == ChangesetComment.COMMENT_TYPE_TODO)
156 153
157 154 if not show_outdated:
158 155 todos = todos.filter(
159 156 coalesce(ChangesetComment.display_state, '') !=
160 157 ChangesetComment.COMMENT_OUTDATED)
161 158
162 159 todos = todos.all()
163 160
164 161 return todos
165 162
166 163 def _log_audit_action(self, action, action_data, user, comment):
167 164 audit_logger.store(
168 165 action=action,
169 166 action_data=action_data,
170 167 user=user,
171 168 repo=comment.repo)
172 169
173 170 def create(self, text, repo, user, commit_id=None, pull_request=None,
174 171 f_path=None, line_no=None, status_change=None,
175 172 status_change_type=None, comment_type=None,
176 173 resolves_comment_id=None, closing_pr=False, send_email=True,
177 174 renderer=None):
178 175 """
179 176 Creates new comment for commit or pull request.
180 177 IF status_change is not none this comment is associated with a
181 178 status change of commit or commit associated with pull request
182 179
183 180 :param text:
184 181 :param repo:
185 182 :param user:
186 183 :param commit_id:
187 184 :param pull_request:
188 185 :param f_path:
189 186 :param line_no:
190 187 :param status_change: Label for status change
191 188 :param comment_type: Type of comment
192 189 :param status_change_type: type of status change
193 190 :param closing_pr:
194 191 :param send_email:
195 192 :param renderer: pick renderer for this comment
196 193 """
197 194 if not text:
198 195 log.warning('Missing text for comment, skipping...')
199 196 return
200 197
201 198 if not renderer:
202 199 renderer = self._get_renderer()
203 200
204 201 repo = self._get_repo(repo)
205 202 user = self._get_user(user)
206 203
207 204 schema = comment_schema.CommentSchema()
208 205 validated_kwargs = schema.deserialize(dict(
209 206 comment_body=text,
210 207 comment_type=comment_type,
211 208 comment_file=f_path,
212 209 comment_line=line_no,
213 210 renderer_type=renderer,
214 211 status_change=status_change_type,
215 212 resolves_comment_id=resolves_comment_id,
216 213 repo=repo.repo_id,
217 214 user=user.user_id,
218 215 ))
219 216
220 217 comment = ChangesetComment()
221 218 comment.renderer = validated_kwargs['renderer_type']
222 219 comment.text = validated_kwargs['comment_body']
223 220 comment.f_path = validated_kwargs['comment_file']
224 221 comment.line_no = validated_kwargs['comment_line']
225 222 comment.comment_type = validated_kwargs['comment_type']
226 223
227 224 comment.repo = repo
228 225 comment.author = user
229 226 comment.resolved_comment = self.__get_commit_comment(
230 227 validated_kwargs['resolves_comment_id'])
231 228
232 229 pull_request_id = pull_request
233 230
234 231 commit_obj = None
235 232 pull_request_obj = None
236 233
237 234 if commit_id:
238 235 notification_type = EmailNotificationModel.TYPE_COMMIT_COMMENT
239 236 # do a lookup, so we don't pass something bad here
240 237 commit_obj = repo.scm_instance().get_commit(commit_id=commit_id)
241 238 comment.revision = commit_obj.raw_id
242 239
243 240 elif pull_request_id:
244 241 notification_type = EmailNotificationModel.TYPE_PULL_REQUEST_COMMENT
245 242 pull_request_obj = self.__get_pull_request(pull_request_id)
246 243 comment.pull_request = pull_request_obj
247 244 else:
248 245 raise Exception('Please specify commit or pull_request_id')
249 246
250 247 Session().add(comment)
251 248 Session().flush()
252 249 kwargs = {
253 250 'user': user,
254 251 'renderer_type': renderer,
255 252 'repo_name': repo.repo_name,
256 253 'status_change': status_change,
257 254 'status_change_type': status_change_type,
258 255 'comment_body': text,
259 256 'comment_file': f_path,
260 257 'comment_line': line_no,
261 258 'comment_type': comment_type or 'note'
262 259 }
263 260
264 261 if commit_obj:
265 262 recipients = ChangesetComment.get_users(
266 263 revision=commit_obj.raw_id)
267 264 # add commit author if it's in RhodeCode system
268 265 cs_author = User.get_from_cs_author(commit_obj.author)
269 266 if not cs_author:
270 267 # use repo owner if we cannot extract the author correctly
271 268 cs_author = repo.user
272 269 recipients += [cs_author]
273 270
274 271 commit_comment_url = self.get_url(comment)
275 272
276 273 target_repo_url = h.link_to(
277 274 repo.repo_name,
278 275 h.route_url('repo_summary', repo_name=repo.repo_name))
279 276
280 277 # commit specifics
281 278 kwargs.update({
282 279 'commit': commit_obj,
283 280 'commit_message': commit_obj.message,
284 281 'commit_target_repo': target_repo_url,
285 282 'commit_comment_url': commit_comment_url,
286 283 })
287 284
288 285 elif pull_request_obj:
289 286 # get the current participants of this pull request
290 287 recipients = ChangesetComment.get_users(
291 288 pull_request_id=pull_request_obj.pull_request_id)
292 289 # add pull request author
293 290 recipients += [pull_request_obj.author]
294 291
295 292 # add the reviewers to notification
296 293 recipients += [x.user for x in pull_request_obj.reviewers]
297 294
298 295 pr_target_repo = pull_request_obj.target_repo
299 296 pr_source_repo = pull_request_obj.source_repo
300 297
301 298 pr_comment_url = h.route_url(
302 299 'pullrequest_show',
303 300 repo_name=pr_target_repo.repo_name,
304 301 pull_request_id=pull_request_obj.pull_request_id,
305 302 anchor='comment-%s' % comment.comment_id)
306 303
307 304 # set some variables for email notification
308 305 pr_target_repo_url = h.route_url(
309 306 'repo_summary', repo_name=pr_target_repo.repo_name)
310 307
311 308 pr_source_repo_url = h.route_url(
312 309 'repo_summary', repo_name=pr_source_repo.repo_name)
313 310
314 311 # pull request specifics
315 312 kwargs.update({
316 313 'pull_request': pull_request_obj,
317 314 'pr_id': pull_request_obj.pull_request_id,
318 315 'pr_target_repo': pr_target_repo,
319 316 'pr_target_repo_url': pr_target_repo_url,
320 317 'pr_source_repo': pr_source_repo,
321 318 'pr_source_repo_url': pr_source_repo_url,
322 319 'pr_comment_url': pr_comment_url,
323 320 'pr_closing': closing_pr,
324 321 })
325 322 if send_email:
326 323 # pre-generate the subject for notification itself
327 324 (subject,
328 325 _h, _e, # we don't care about those
329 326 body_plaintext) = EmailNotificationModel().render_email(
330 327 notification_type, **kwargs)
331 328
332 329 mention_recipients = set(
333 330 self._extract_mentions(text)).difference(recipients)
334 331
335 332 # create notification objects, and emails
336 333 NotificationModel().create(
337 334 created_by=user,
338 335 notification_subject=subject,
339 336 notification_body=body_plaintext,
340 337 notification_type=notification_type,
341 338 recipients=recipients,
342 339 mention_recipients=mention_recipients,
343 340 email_kwargs=kwargs,
344 341 )
345 342
346 343 Session().flush()
347 344 if comment.pull_request:
348 345 action = 'repo.pull_request.comment.create'
349 346 else:
350 347 action = 'repo.commit.comment.create'
351 348
352 349 comment_data = comment.get_api_data()
353 350 self._log_audit_action(
354 351 action, {'data': comment_data}, user, comment)
355 352
356 353 msg_url = ''
357 354 channel = None
358 355 if commit_obj:
359 356 msg_url = commit_comment_url
360 357 repo_name = repo.repo_name
361 358 channel = u'/repo${}$/commit/{}'.format(
362 359 repo_name,
363 360 commit_obj.raw_id
364 361 )
365 362 elif pull_request_obj:
366 363 msg_url = pr_comment_url
367 364 repo_name = pr_target_repo.repo_name
368 365 channel = u'/repo${}$/pr/{}'.format(
369 366 repo_name,
370 367 pull_request_id
371 368 )
372 369
373 370 message = '<strong>{}</strong> {} - ' \
374 371 '<a onclick="window.location=\'{}\';' \
375 372 'window.location.reload()">' \
376 373 '<strong>{}</strong></a>'
377 374 message = message.format(
378 375 user.username, _('made a comment'), msg_url,
379 376 _('Show it now'))
380 377
381 378 channelstream.post_message(
382 379 channel, message, user.username,
383 380 registry=get_current_registry())
384 381
385 382 return comment
386 383
387 384 def delete(self, comment, user):
388 385 """
389 386 Deletes given comment
390 387 """
391 388 comment = self.__get_commit_comment(comment)
392 389 old_data = comment.get_api_data()
393 390 Session().delete(comment)
394 391
395 392 if comment.pull_request:
396 393 action = 'repo.pull_request.comment.delete'
397 394 else:
398 395 action = 'repo.commit.comment.delete'
399 396
400 397 self._log_audit_action(
401 398 action, {'old_data': old_data}, user, comment)
402 399
403 400 return comment
404 401
405 402 def get_all_comments(self, repo_id, revision=None, pull_request=None):
406 403 q = ChangesetComment.query()\
407 404 .filter(ChangesetComment.repo_id == repo_id)
408 405 if revision:
409 406 q = q.filter(ChangesetComment.revision == revision)
410 407 elif pull_request:
411 408 pull_request = self.__get_pull_request(pull_request)
412 409 q = q.filter(ChangesetComment.pull_request == pull_request)
413 410 else:
414 411 raise Exception('Please specify commit or pull_request')
415 412 q = q.order_by(ChangesetComment.created_on)
416 413 return q.all()
417 414
418 415 def get_url(self, comment, request=None, permalink=False):
419 416 if not request:
420 417 request = get_current_request()
421 418
422 419 comment = self.__get_commit_comment(comment)
423 420 if comment.pull_request:
424 421 pull_request = comment.pull_request
425 422 if permalink:
426 423 return request.route_url(
427 424 'pull_requests_global',
428 425 pull_request_id=pull_request.pull_request_id,
429 426 _anchor='comment-%s' % comment.comment_id)
430 427 else:
431 428 return request.route_url('pullrequest_show',
432 429 repo_name=safe_str(pull_request.target_repo.repo_name),
433 430 pull_request_id=pull_request.pull_request_id,
434 431 _anchor='comment-%s' % comment.comment_id)
435 432
436 433 else:
437 434 repo = comment.repo
438 435 commit_id = comment.revision
439 436
440 437 if permalink:
441 438 return request.route_url(
442 439 'repo_commit', repo_name=safe_str(repo.repo_id),
443 440 commit_id=commit_id,
444 441 _anchor='comment-%s' % comment.comment_id)
445 442
446 443 else:
447 444 return request.route_url(
448 445 'repo_commit', repo_name=safe_str(repo.repo_name),
449 446 commit_id=commit_id,
450 447 _anchor='comment-%s' % comment.comment_id)
451 448
452 449 def get_comments(self, repo_id, revision=None, pull_request=None):
453 450 """
454 451 Gets main comments based on revision or pull_request_id
455 452
456 453 :param repo_id:
457 454 :param revision:
458 455 :param pull_request:
459 456 """
460 457
461 458 q = ChangesetComment.query()\
462 459 .filter(ChangesetComment.repo_id == repo_id)\
463 460 .filter(ChangesetComment.line_no == None)\
464 461 .filter(ChangesetComment.f_path == None)
465 462 if revision:
466 463 q = q.filter(ChangesetComment.revision == revision)
467 464 elif pull_request:
468 465 pull_request = self.__get_pull_request(pull_request)
469 466 q = q.filter(ChangesetComment.pull_request == pull_request)
470 467 else:
471 468 raise Exception('Please specify commit or pull_request')
472 469 q = q.order_by(ChangesetComment.created_on)
473 470 return q.all()
474 471
475 472 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
476 473 q = self._get_inline_comments_query(repo_id, revision, pull_request)
477 474 return self._group_comments_by_path_and_line_number(q)
478 475
479 476 def get_inline_comments_count(self, inline_comments, skip_outdated=True,
480 477 version=None):
481 478 inline_cnt = 0
482 479 for fname, per_line_comments in inline_comments.iteritems():
483 480 for lno, comments in per_line_comments.iteritems():
484 481 for comm in comments:
485 482 if not comm.outdated_at_version(version) and skip_outdated:
486 483 inline_cnt += 1
487 484
488 485 return inline_cnt
489 486
490 487 def get_outdated_comments(self, repo_id, pull_request):
491 488 # TODO: johbo: Remove `repo_id`, it is not needed to find the comments
492 489 # of a pull request.
493 490 q = self._all_inline_comments_of_pull_request(pull_request)
494 491 q = q.filter(
495 492 ChangesetComment.display_state ==
496 493 ChangesetComment.COMMENT_OUTDATED
497 494 ).order_by(ChangesetComment.comment_id.asc())
498 495
499 496 return self._group_comments_by_path_and_line_number(q)
500 497
501 498 def _get_inline_comments_query(self, repo_id, revision, pull_request):
502 499 # TODO: johbo: Split this into two methods: One for PR and one for
503 500 # commit.
504 501 if revision:
505 502 q = Session().query(ChangesetComment).filter(
506 503 ChangesetComment.repo_id == repo_id,
507 504 ChangesetComment.line_no != null(),
508 505 ChangesetComment.f_path != null(),
509 506 ChangesetComment.revision == revision)
510 507
511 508 elif pull_request:
512 509 pull_request = self.__get_pull_request(pull_request)
513 510 if not CommentsModel.use_outdated_comments(pull_request):
514 511 q = self._visible_inline_comments_of_pull_request(pull_request)
515 512 else:
516 513 q = self._all_inline_comments_of_pull_request(pull_request)
517 514
518 515 else:
519 516 raise Exception('Please specify commit or pull_request_id')
520 517 q = q.order_by(ChangesetComment.comment_id.asc())
521 518 return q
522 519
523 520 def _group_comments_by_path_and_line_number(self, q):
524 521 comments = q.all()
525 522 paths = collections.defaultdict(lambda: collections.defaultdict(list))
526 523 for co in comments:
527 524 paths[co.f_path][co.line_no].append(co)
528 525 return paths
529 526
530 527 @classmethod
531 528 def needed_extra_diff_context(cls):
532 529 return max(cls.DIFF_CONTEXT_BEFORE, cls.DIFF_CONTEXT_AFTER)
533 530
534 531 def outdate_comments(self, pull_request, old_diff_data, new_diff_data):
535 532 if not CommentsModel.use_outdated_comments(pull_request):
536 533 return
537 534
538 535 comments = self._visible_inline_comments_of_pull_request(pull_request)
539 536 comments_to_outdate = comments.all()
540 537
541 538 for comment in comments_to_outdate:
542 539 self._outdate_one_comment(comment, old_diff_data, new_diff_data)
543 540
544 541 def _outdate_one_comment(self, comment, old_diff_proc, new_diff_proc):
545 542 diff_line = _parse_comment_line_number(comment.line_no)
546 543
547 544 try:
548 545 old_context = old_diff_proc.get_context_of_line(
549 546 path=comment.f_path, diff_line=diff_line)
550 547 new_context = new_diff_proc.get_context_of_line(
551 548 path=comment.f_path, diff_line=diff_line)
552 549 except (diffs.LineNotInDiffException,
553 550 diffs.FileNotInDiffException):
554 551 comment.display_state = ChangesetComment.COMMENT_OUTDATED
555 552 return
556 553
557 554 if old_context == new_context:
558 555 return
559 556
560 557 if self._should_relocate_diff_line(diff_line):
561 558 new_diff_lines = new_diff_proc.find_context(
562 559 path=comment.f_path, context=old_context,
563 560 offset=self.DIFF_CONTEXT_BEFORE)
564 561 if not new_diff_lines:
565 562 comment.display_state = ChangesetComment.COMMENT_OUTDATED
566 563 else:
567 564 new_diff_line = self._choose_closest_diff_line(
568 565 diff_line, new_diff_lines)
569 566 comment.line_no = _diff_to_comment_line_number(new_diff_line)
570 567 else:
571 568 comment.display_state = ChangesetComment.COMMENT_OUTDATED
572 569
573 570 def _should_relocate_diff_line(self, diff_line):
574 571 """
575 572 Checks if relocation shall be tried for the given `diff_line`.
576 573
577 574 If a comment points into the first lines, then we can have a situation
578 575 that after an update another line has been added on top. In this case
579 576 we would find the context still and move the comment around. This
580 577 would be wrong.
581 578 """
582 579 should_relocate = (
583 580 (diff_line.new and diff_line.new > self.DIFF_CONTEXT_BEFORE) or
584 581 (diff_line.old and diff_line.old > self.DIFF_CONTEXT_BEFORE))
585 582 return should_relocate
586 583
587 584 def _choose_closest_diff_line(self, diff_line, new_diff_lines):
588 585 candidate = new_diff_lines[0]
589 586 best_delta = _diff_line_delta(diff_line, candidate)
590 587 for new_diff_line in new_diff_lines[1:]:
591 588 delta = _diff_line_delta(diff_line, new_diff_line)
592 589 if delta < best_delta:
593 590 candidate = new_diff_line
594 591 best_delta = delta
595 592 return candidate
596 593
597 594 def _visible_inline_comments_of_pull_request(self, pull_request):
598 595 comments = self._all_inline_comments_of_pull_request(pull_request)
599 596 comments = comments.filter(
600 597 coalesce(ChangesetComment.display_state, '') !=
601 598 ChangesetComment.COMMENT_OUTDATED)
602 599 return comments
603 600
604 601 def _all_inline_comments_of_pull_request(self, pull_request):
605 602 comments = Session().query(ChangesetComment)\
606 603 .filter(ChangesetComment.line_no != None)\
607 604 .filter(ChangesetComment.f_path != None)\
608 605 .filter(ChangesetComment.pull_request == pull_request)
609 606 return comments
610 607
611 608 def _all_general_comments_of_pull_request(self, pull_request):
612 609 comments = Session().query(ChangesetComment)\
613 610 .filter(ChangesetComment.line_no == None)\
614 611 .filter(ChangesetComment.f_path == None)\
615 612 .filter(ChangesetComment.pull_request == pull_request)
616 613 return comments
617 614
618 615 @staticmethod
619 616 def use_outdated_comments(pull_request):
620 617 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
621 618 settings = settings_model.get_general_settings()
622 619 return settings.get('rhodecode_use_outdated_comments', False)
623 620
624 621
625 622 def _parse_comment_line_number(line_no):
626 623 """
627 624 Parses line numbers of the form "(o|n)\d+" and returns them in a tuple.
628 625 """
629 626 old_line = None
630 627 new_line = None
631 628 if line_no.startswith('o'):
632 629 old_line = int(line_no[1:])
633 630 elif line_no.startswith('n'):
634 631 new_line = int(line_no[1:])
635 632 else:
636 633 raise ValueError("Comment lines have to start with either 'o' or 'n'.")
637 634 return diffs.DiffLineNumber(old_line, new_line)
638 635
639 636
640 637 def _diff_to_comment_line_number(diff_line):
641 638 if diff_line.new is not None:
642 639 return u'n{}'.format(diff_line.new)
643 640 elif diff_line.old is not None:
644 641 return u'o{}'.format(diff_line.old)
645 642 return u''
646 643
647 644
648 645 def _diff_line_delta(a, b):
649 646 if None not in (a.new, b.new):
650 647 return abs(a.new - b.new)
651 648 elif None not in (a.old, b.old):
652 649 return abs(a.old - b.old)
653 650 else:
654 651 raise ValueError(
655 652 "Cannot compute delta between {} and {}".format(a, b))
General Comments 0
You need to be logged in to leave comments. Login now