comment.py
285 lines
| 10.7 KiB
| text/x-python
|
PythonLexer
r1670 | # -*- coding: utf-8 -*- | |||
""" | ||||
rhodecode.model.comment | ||||
~~~~~~~~~~~~~~~~~~~~~~~ | ||||
comments model for RhodeCode | ||||
r1789 | ||||
r1670 | :created_on: Nov 11, 2011 | |||
:author: marcink | ||||
r1824 | :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com> | |||
r1670 | :license: GPLv3, see COPYING for more details. | |||
""" | ||||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU General Public License as published by | ||||
# the Free Software Foundation, either version 3 of the License, or | ||||
# (at your option) any later version. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
import logging | ||||
import traceback | ||||
r1712 | from pylons.i18n.translation import _ | |||
from sqlalchemy.util.compat import defaultdict | ||||
r2178 | from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode | |||
r1712 | from rhodecode.lib import helpers as h | |||
r1670 | from rhodecode.model import BaseModel | |||
r2440 | from rhodecode.model.db import ChangesetComment, User, Repository, \ | |||
Notification, PullRequest | ||||
r1703 | from rhodecode.model.notification import NotificationModel | |||
r3430 | from rhodecode.model.meta import Session | |||
r1670 | ||||
log = logging.getLogger(__name__) | ||||
class ChangesetCommentsModel(BaseModel): | ||||
r2522 | cls = ChangesetComment | |||
r1713 | def __get_changeset_comment(self, changeset_comment): | |||
r1716 | return self._get_instance(ChangesetComment, changeset_comment) | |||
r1670 | ||||
r2440 | def __get_pull_request(self, pull_request): | |||
return self._get_instance(PullRequest, pull_request) | ||||
r1712 | def _extract_mentions(self, s): | |||
r1713 | user_objects = [] | |||
for username in extract_mentioned_users(s): | ||||
r1712 | user_obj = User.get_by_username(username, case_insensitive=True) | |||
if user_obj: | ||||
r1713 | user_objects.append(user_obj) | |||
return user_objects | ||||
r1712 | ||||
r3430 | def _get_notification_data(self, repo, comment, user, comment_text, | |||
line_no=None, revision=None, pull_request=None, | ||||
status_change=None, closing_pr=False): | ||||
""" | ||||
Get notification data | ||||
:param comment_text: | ||||
:param line: | ||||
:returns: tuple (subj,body,recipients,notification_type,email_kwargs) | ||||
""" | ||||
# make notification | ||||
body = comment_text # text of the comment | ||||
line = '' | ||||
if line_no: | ||||
line = _('on line %s') % line_no | ||||
#changeset | ||||
if revision: | ||||
notification_type = Notification.TYPE_CHANGESET_COMMENT | ||||
cs = repo.scm_instance.get_changeset(revision) | ||||
desc = "%s" % (cs.short_id) | ||||
_url = h.url('changeset_home', | ||||
repo_name=repo.repo_name, | ||||
revision=revision, | ||||
anchor='comment-%s' % comment.comment_id, | ||||
qualified=True, | ||||
) | ||||
subj = safe_unicode( | ||||
h.link_to('Re changeset: %(desc)s %(line)s' % \ | ||||
{'desc': desc, 'line': line}, | ||||
_url) | ||||
) | ||||
Mads Kiilerich
|
r3780 | email_subject = '%s commented on changeset %s' % \ | ||
r3430 | (user.username, h.short_id(revision)) | |||
# get the current participants of this changeset | ||||
recipients = ChangesetComment.get_users(revision=revision) | ||||
# add changeset author if it's in rhodecode system | ||||
cs_author = User.get_from_cs_author(cs.author) | ||||
if not cs_author: | ||||
#use repo owner if we cannot extract the author correctly | ||||
cs_author = repo.user | ||||
recipients += [cs_author] | ||||
email_kwargs = { | ||||
'status_change': status_change, | ||||
'cs_comment_user': h.person(user.email), | ||||
'cs_target_repo': h.url('summary_home', repo_name=repo.repo_name, | ||||
qualified=True), | ||||
'cs_comment_url': _url, | ||||
'raw_id': revision, | ||||
'message': cs.message | ||||
} | ||||
#pull request | ||||
elif pull_request: | ||||
notification_type = Notification.TYPE_PULL_REQUEST_COMMENT | ||||
desc = comment.pull_request.title | ||||
_url = h.url('pullrequest_show', | ||||
repo_name=pull_request.other_repo.repo_name, | ||||
pull_request_id=pull_request.pull_request_id, | ||||
anchor='comment-%s' % comment.comment_id, | ||||
qualified=True, | ||||
) | ||||
subj = safe_unicode( | ||||
h.link_to('Re pull request #%(pr_id)s: %(desc)s %(line)s' % \ | ||||
{'desc': desc, | ||||
'pr_id': comment.pull_request.pull_request_id, | ||||
'line': line}, | ||||
_url) | ||||
) | ||||
Mads Kiilerich
|
r3780 | email_subject = '%s commented on pull request #%s' % \ | ||
r3430 | (user.username, comment.pull_request.pull_request_id) | |||
# get the current participants of this pull request | ||||
recipients = ChangesetComment.get_users(pull_request_id= | ||||
pull_request.pull_request_id) | ||||
# add pull request author | ||||
recipients += [pull_request.author] | ||||
# add the reviewers to notification | ||||
recipients += [x.user for x in pull_request.reviewers] | ||||
#set some variables for email notification | ||||
email_kwargs = { | ||||
Mads Kiilerich
|
r3780 | 'pr_title': pull_request.title, | ||
r3430 | 'pr_id': pull_request.pull_request_id, | |||
'status_change': status_change, | ||||
'closing_pr': closing_pr, | ||||
'pr_comment_url': _url, | ||||
'pr_comment_user': h.person(user.email), | ||||
'pr_target_repo': h.url('summary_home', | ||||
repo_name=pull_request.other_repo.repo_name, | ||||
qualified=True) | ||||
} | ||||
return subj, body, recipients, notification_type, email_kwargs, email_subject | ||||
r2541 | def create(self, text, repo, user, revision=None, pull_request=None, | |||
r3430 | f_path=None, line_no=None, status_change=None, closing_pr=False, | |||
send_email=True): | ||||
r1670 | """ | |||
r2443 | Creates new comment for changeset or pull request. | |||
r2478 | IF status_change is not none this comment is associated with a | |||
r2443 | status change of changeset or changesets associated with pull request | |||
r1789 | ||||
r1670 | :param text: | |||
r2541 | :param repo: | |||
:param user: | ||||
r1675 | :param revision: | |||
r2443 | :param pull_request: | |||
r1670 | :param f_path: | |||
:param line_no: | ||||
r2296 | :param status_change: | |||
r3430 | :param closing_pr: | |||
r3257 | :param send_email: | |||
r1670 | """ | |||
r2443 | if not text: | |||
r3430 | log.warning('Missing text for comment, skipping...') | |||
r2443 | return | |||
r2150 | ||||
r2541 | repo = self._get_repo(repo) | |||
user = self._get_user(user) | ||||
r2443 | comment = ChangesetComment() | |||
comment.repo = repo | ||||
r2541 | comment.author = user | |||
r2443 | comment.text = text | |||
comment.f_path = f_path | ||||
comment.line_no = line_no | ||||
if revision: | ||||
r1677 | comment.revision = revision | |||
r2443 | elif pull_request: | |||
pull_request = self.__get_pull_request(pull_request) | ||||
comment.pull_request = pull_request | ||||
else: | ||||
raise Exception('Please specify revision or pull_request_id') | ||||
r1670 | ||||
r3430 | Session().add(comment) | |||
Session().flush() | ||||
r2443 | ||||
r3257 | if send_email: | |||
r3430 | (subj, body, recipients, notification_type, | |||
email_kwargs, email_subject) = self._get_notification_data( | ||||
repo, comment, user, | ||||
comment_text=text, | ||||
line_no=line_no, | ||||
revision=revision, | ||||
pull_request=pull_request, | ||||
status_change=status_change, | ||||
closing_pr=closing_pr) | ||||
r3257 | # create notification objects, and emails | |||
r2077 | NotificationModel().create( | |||
r2541 | created_by=user, subject=subj, body=body, | |||
r3257 | recipients=recipients, type_=notification_type, | |||
r3430 | email_kwargs=email_kwargs, email_subject=email_subject | |||
r2077 | ) | |||
r1703 | ||||
r3257 | mention_recipients = set(self._extract_mentions(body))\ | |||
.difference(recipients) | ||||
if mention_recipients: | ||||
email_kwargs.update({'pr_mention': True}) | ||||
subj = _('[Mention]') + ' ' + subj | ||||
NotificationModel().create( | ||||
created_by=user, subject=subj, body=body, | ||||
recipients=mention_recipients, | ||||
type_=notification_type, | ||||
email_kwargs=email_kwargs | ||||
) | ||||
r2443 | return comment | |||
r1670 | ||||
r1713 | def delete(self, comment): | |||
r1670 | """ | |||
Deletes given comment | ||||
r1789 | ||||
r1670 | :param comment_id: | |||
""" | ||||
r1713 | comment = self.__get_changeset_comment(comment) | |||
r3430 | Session().delete(comment) | |||
r1713 | ||||
r1670 | return comment | |||
r1675 | ||||
r2440 | def get_comments(self, repo_id, revision=None, pull_request=None): | |||
r2439 | """ | |||
r4089 | Gets main comments based on revision or pull_request_id | |||
r2439 | ||||
:param repo_id: | ||||
:param revision: | ||||
r2440 | :param pull_request: | |||
r2439 | """ | |||
r2440 | ||||
r2439 | q = ChangesetComment.query()\ | |||
r1675 | .filter(ChangesetComment.repo_id == repo_id)\ | |||
.filter(ChangesetComment.line_no == None)\ | ||||
r2439 | .filter(ChangesetComment.f_path == None) | |||
if revision: | ||||
q = q.filter(ChangesetComment.revision == revision) | ||||
r2440 | elif pull_request: | |||
pull_request = self.__get_pull_request(pull_request) | ||||
q = q.filter(ChangesetComment.pull_request == pull_request) | ||||
r2439 | else: | |||
r2440 | raise Exception('Please specify revision or pull_request') | |||
r2639 | q = q.order_by(ChangesetComment.created_on) | |||
r2439 | return q.all() | |||
r1675 | ||||
r2440 | def get_inline_comments(self, repo_id, revision=None, pull_request=None): | |||
r3430 | q = Session().query(ChangesetComment)\ | |||
r1675 | .filter(ChangesetComment.repo_id == repo_id)\ | |||
r1681 | .filter(ChangesetComment.line_no != None)\ | |||
r2187 | .filter(ChangesetComment.f_path != None)\ | |||
.order_by(ChangesetComment.comment_id.asc())\ | ||||
r2439 | ||||
if revision: | ||||
q = q.filter(ChangesetComment.revision == revision) | ||||
r2440 | elif pull_request: | |||
pull_request = self.__get_pull_request(pull_request) | ||||
q = q.filter(ChangesetComment.pull_request == pull_request) | ||||
r2439 | else: | |||
raise Exception('Please specify revision or pull_request_id') | ||||
comments = q.all() | ||||
r1677 | ||||
r1789 | paths = defaultdict(lambda: defaultdict(list)) | |||
r1675 | ||||
for co in comments: | ||||
r1677 | paths[co.f_path][co.line_no].append(co) | |||
return paths.items() | ||||