comment.py
302 lines
| 12.8 KiB
| text/x-python
|
PythonLexer
Bradley M. Kuhn
|
r4187 | # -*- coding: utf-8 -*- | ||
# 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/>. | ||||
""" | ||||
kallithea.model.comment | ||||
~~~~~~~~~~~~~~~~~~~~~~~ | ||||
Bradley M. Kuhn
|
r4212 | comments model for Kallithea | ||
Bradley M. Kuhn
|
r4187 | |||
Bradley M. Kuhn
|
r4211 | This file was forked by the Kallithea project in July 2014. | ||
Original author and date, and relevant copyright and licensing information is below: | ||||
Bradley M. Kuhn
|
r4187 | :created_on: Nov 11, 2011 | ||
:author: marcink | ||||
Bradley M. Kuhn
|
r4211 | :copyright: (c) 2013 RhodeCode GmbH, and others. | ||
Bradley M. Kuhn
|
r4208 | :license: GPLv3, see LICENSE.md for more details. | ||
Bradley M. Kuhn
|
r4187 | """ | ||
import logging | ||||
Andrew Shadura
|
r5054 | from collections import defaultdict | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r7718 | from tg.i18n import ugettext as _ | ||
Bradley M. Kuhn
|
r4187 | from kallithea.lib import helpers as h | ||
Mads Kiilerich
|
r8075 | from kallithea.lib.utils2 import extract_mentioned_users | ||
Mads Kiilerich
|
r7718 | from kallithea.model.db import ChangesetComment, PullRequest, Repository, User | ||
from kallithea.model.meta import Session | ||||
Bradley M. Kuhn
|
r4187 | from kallithea.model.notification import NotificationModel | ||
Mads Kiilerich
|
r7718 | |||
Bradley M. Kuhn
|
r4187 | |||
log = logging.getLogger(__name__) | ||||
Søren Løvborg
|
r6456 | def _list_changeset_commenters(revision): | ||
return (Session().query(User) | ||||
.join(ChangesetComment.author) | ||||
.filter(ChangesetComment.revision == revision) | ||||
.all()) | ||||
def _list_pull_request_commenters(pull_request): | ||||
return (Session().query(User) | ||||
.join(ChangesetComment.author) | ||||
.filter(ChangesetComment.pull_request_id == pull_request.pull_request_id) | ||||
.all()) | ||||
Søren Løvborg
|
r6483 | class ChangesetCommentsModel(object): | ||
Bradley M. Kuhn
|
r4187 | |||
Søren Løvborg
|
r6194 | def _get_notification_data(self, repo, comment, author, comment_text, | ||
Bradley M. Kuhn
|
r4187 | line_no=None, revision=None, pull_request=None, | ||
status_change=None, closing_pr=False): | ||||
""" | ||||
: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 | ||||
Lars Kruse
|
r6789 | # changeset | ||
Bradley M. Kuhn
|
r4187 | if revision: | ||
Thomas De Schampheleire
|
r7368 | notification_type = NotificationModel.TYPE_CHANGESET_COMMENT | ||
Bradley M. Kuhn
|
r4187 | cs = repo.scm_instance.get_changeset(revision) | ||
Mads Kiilerich
|
r5305 | desc = cs.short_id | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r4446 | threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())] | ||
if line_no: # TODO: url to file _and_ line number | ||||
threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no, | ||||
h.canonical_hostname())) | ||||
Mads Kiilerich
|
r4445 | comment_url = h.canonical_url('changeset_home', | ||
Mads Kiilerich
|
r4384 | repo_name=repo.repo_name, | ||
revision=revision, | ||||
Mads Kiilerich
|
r4445 | anchor='comment-%s' % comment.comment_id) | ||
Mads Kiilerich
|
r8075 | subj = h.link_to( | ||
'Re changeset: %(desc)s %(line)s' % | ||||
Bradley M. Kuhn
|
r4187 | {'desc': desc, 'line': line}, | ||
Mads Kiilerich
|
r8075 | comment_url) | ||
Bradley M. Kuhn
|
r4187 | # get the current participants of this changeset | ||
Søren Løvborg
|
r6456 | recipients = _list_changeset_commenters(revision) | ||
Mads Kiilerich
|
r5438 | # add changeset author if it's known locally | ||
Bradley M. Kuhn
|
r4187 | cs_author = User.get_from_cs_author(cs.author) | ||
if not cs_author: | ||||
Lars Kruse
|
r6789 | # use repo owner if we cannot extract the author correctly | ||
Mads Kiilerich
|
r6019 | # FIXME: just use committer name even if not a user | ||
Søren Løvborg
|
r6193 | cs_author = repo.owner | ||
Søren Løvborg
|
r6456 | recipients.append(cs_author) | ||
Bradley M. Kuhn
|
r4187 | email_kwargs = { | ||
'status_change': status_change, | ||||
Søren Løvborg
|
r6194 | 'cs_comment_user': author.full_name_and_username, | ||
Mads Kiilerich
|
r4445 | 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name), | ||
Mads Kiilerich
|
r4384 | 'cs_comment_url': comment_url, | ||
Mads Kiilerich
|
r6098 | 'cs_url': h.canonical_url('changeset_home', repo_name=repo.repo_name, revision=revision), | ||
Bradley M. Kuhn
|
r4187 | 'raw_id': revision, | ||
Mads Kiilerich
|
r4381 | 'message': cs.message, | ||
Thomas De Schampheleire <thomas.de.schampheleire at gmail.com>
|
r6021 | 'message_short': h.shorter(cs.message, 50, firstline=True), | ||
Mads Kiilerich
|
r6019 | 'cs_author': cs_author, | ||
Mads Kiilerich
|
r4381 | 'repo_name': repo.repo_name, | ||
'short_id': h.short_id(revision), | ||||
'branch': cs.branch, | ||||
Søren Løvborg
|
r6194 | 'comment_username': author.username, | ||
Mads Kiilerich
|
r4446 | 'threading': threading, | ||
Bradley M. Kuhn
|
r4187 | } | ||
Lars Kruse
|
r6789 | # pull request | ||
Bradley M. Kuhn
|
r4187 | elif pull_request: | ||
Thomas De Schampheleire
|
r7368 | notification_type = NotificationModel.TYPE_PULL_REQUEST_COMMENT | ||
Bradley M. Kuhn
|
r4187 | desc = comment.pull_request.title | ||
Mads Kiilerich
|
r4381 | _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':') | ||
Mads Kiilerich
|
r6019 | _other_ref_type, other_ref_name, _other_rev = comment.pull_request.other_ref.split(':') | ||
Mads Kiilerich
|
r4446 | threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name, | ||
pull_request.pull_request_id, | ||||
h.canonical_hostname())] | ||||
if line_no: # TODO: url to file _and_ line number | ||||
threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name, | ||||
pull_request.pull_request_id, line_no, | ||||
h.canonical_hostname())) | ||||
Mads Kiilerich
|
r4447 | comment_url = pull_request.url(canonical=True, | ||
Mads Kiilerich
|
r4445 | anchor='comment-%s' % comment.comment_id) | ||
Mads Kiilerich
|
r8075 | subj = h.link_to( | ||
'Re pull request %(pr_nice_id)s: %(desc)s %(line)s' % | ||||
Bradley M. Kuhn
|
r4187 | {'desc': desc, | ||
Thomas De Schampheleire
|
r5089 | 'pr_nice_id': comment.pull_request.nice_id(), | ||
Bradley M. Kuhn
|
r4187 | 'line': line}, | ||
Mads Kiilerich
|
r8075 | comment_url) | ||
Bradley M. Kuhn
|
r4187 | # get the current participants of this pull request | ||
Søren Løvborg
|
r6456 | recipients = _list_pull_request_commenters(pull_request) | ||
recipients.append(pull_request.owner) | ||||
Mads Kiilerich
|
r5733 | recipients += pull_request.get_reviewer_users() | ||
Bradley M. Kuhn
|
r4187 | |||
Lars Kruse
|
r6789 | # set some variables for email notification | ||
Bradley M. Kuhn
|
r4187 | email_kwargs = { | ||
'pr_title': pull_request.title, | ||||
Thomas De Schampheleire <thomas.de.schampheleire at gmail.com>
|
r6021 | 'pr_title_short': h.shorter(pull_request.title, 50), | ||
Thomas De Schampheleire
|
r5089 | 'pr_nice_id': pull_request.nice_id(), | ||
Bradley M. Kuhn
|
r4187 | 'status_change': status_change, | ||
'closing_pr': closing_pr, | ||||
Mads Kiilerich
|
r4384 | 'pr_comment_url': comment_url, | ||
Mads Kiilerich
|
r6098 | 'pr_url': pull_request.url(canonical=True), | ||
Søren Løvborg
|
r6194 | 'pr_comment_user': author.full_name_and_username, | ||
Mads Kiilerich
|
r4445 | 'pr_target_repo': h.canonical_url('summary_home', | ||
repo_name=pull_request.other_repo.repo_name), | ||||
Mads Kiilerich
|
r6019 | 'pr_target_branch': other_ref_name, | ||
'pr_source_repo': h.canonical_url('summary_home', | ||||
repo_name=pull_request.org_repo.repo_name), | ||||
'pr_source_branch': org_ref_name, | ||||
'pr_owner': pull_request.owner, | ||||
Mads Kiilerich
|
r6022 | 'pr_owner_username': pull_request.owner.username, | ||
Mads Kiilerich
|
r4381 | 'repo_name': pull_request.other_repo.repo_name, | ||
Søren Løvborg
|
r6194 | 'comment_username': author.username, | ||
Mads Kiilerich
|
r4446 | 'threading': threading, | ||
Bradley M. Kuhn
|
r4187 | } | ||
Mads Kiilerich
|
r4381 | return subj, body, recipients, notification_type, email_kwargs | ||
Bradley M. Kuhn
|
r4187 | |||
Søren Løvborg
|
r6194 | def create(self, text, repo, author, revision=None, pull_request=None, | ||
Bradley M. Kuhn
|
r4187 | f_path=None, line_no=None, status_change=None, closing_pr=False, | ||
send_email=True): | ||||
""" | ||||
Jan Heylen
|
r5085 | Creates a new comment for either a changeset or a pull request. | ||
status_change and closing_pr is only for the optional email. | ||||
Bradley M. Kuhn
|
r4187 | |||
Jan Heylen
|
r5085 | Returns the created comment. | ||
Bradley M. Kuhn
|
r4187 | """ | ||
Thomas De Schampheleire
|
r5068 | if not status_change and not text: | ||
Bradley M. Kuhn
|
r4187 | log.warning('Missing text for comment, skipping...') | ||
Mads Kiilerich
|
r5198 | return None | ||
Bradley M. Kuhn
|
r4187 | |||
Søren Løvborg
|
r6424 | repo = Repository.guess_instance(repo) | ||
Søren Løvborg
|
r6423 | author = User.guess_instance(author) | ||
Bradley M. Kuhn
|
r4187 | comment = ChangesetComment() | ||
comment.repo = repo | ||||
Søren Løvborg
|
r6194 | comment.author = author | ||
Bradley M. Kuhn
|
r4187 | comment.text = text | ||
comment.f_path = f_path | ||||
comment.line_no = line_no | ||||
Mads Kiilerich
|
r5306 | if revision is not None: | ||
Bradley M. Kuhn
|
r4187 | comment.revision = revision | ||
Mads Kiilerich
|
r5306 | elif pull_request is not None: | ||
Søren Løvborg
|
r6082 | pull_request = PullRequest.guess_instance(pull_request) | ||
Bradley M. Kuhn
|
r4187 | comment.pull_request = pull_request | ||
else: | ||||
raise Exception('Please specify revision or pull_request_id') | ||||
Session().add(comment) | ||||
Session().flush() | ||||
if send_email: | ||||
(subj, body, recipients, notification_type, | ||||
Mads Kiilerich
|
r4381 | email_kwargs) = self._get_notification_data( | ||
Søren Løvborg
|
r6194 | repo, comment, author, | ||
Bradley M. Kuhn
|
r4187 | comment_text=text, | ||
line_no=line_no, | ||||
revision=revision, | ||||
pull_request=pull_request, | ||||
status_change=status_change, | ||||
closing_pr=closing_pr) | ||||
Mads Kiilerich
|
r4404 | email_kwargs['is_mention'] = False | ||
Bradley M. Kuhn
|
r4187 | # create notification objects, and emails | ||
NotificationModel().create( | ||||
Søren Løvborg
|
r6194 | created_by=author, subject=subj, body=body, | ||
Bradley M. Kuhn
|
r4187 | recipients=recipients, type_=notification_type, | ||
Mads Kiilerich
|
r4381 | email_kwargs=email_kwargs, | ||
Bradley M. Kuhn
|
r4187 | ) | ||
Søren Løvborg
|
r5840 | mention_recipients = extract_mentioned_users(body).difference(recipients) | ||
Bradley M. Kuhn
|
r4187 | if mention_recipients: | ||
Mads Kiilerich
|
r4404 | email_kwargs['is_mention'] = True | ||
Bradley M. Kuhn
|
r4187 | subj = _('[Mention]') + ' ' + subj | ||
Mads Kiilerich
|
r6020 | # FIXME: this subject is wrong and unused! | ||
Bradley M. Kuhn
|
r4187 | NotificationModel().create( | ||
Søren Løvborg
|
r6194 | created_by=author, subject=subj, body=body, | ||
Bradley M. Kuhn
|
r4187 | recipients=mention_recipients, | ||
type_=notification_type, | ||||
email_kwargs=email_kwargs | ||||
) | ||||
return comment | ||||
def delete(self, comment): | ||||
Søren Løvborg
|
r6082 | comment = ChangesetComment.guess_instance(comment) | ||
Bradley M. Kuhn
|
r4187 | Session().delete(comment) | ||
return comment | ||||
def get_comments(self, repo_id, revision=None, pull_request=None): | ||||
""" | ||||
Jan Heylen
|
r5085 | Gets general comments for either revision or pull_request. | ||
Bradley M. Kuhn
|
r4187 | |||
Jan Heylen
|
r5085 | Returns a list, ordered by creation date. | ||
Bradley M. Kuhn
|
r4187 | """ | ||
Jan Heylen
|
r5086 | return self._get_comments(repo_id, revision=revision, pull_request=pull_request, | ||
inline=False) | ||||
Bradley M. Kuhn
|
r4187 | |||
Thomas De Schampheleire
|
r7409 | def get_inline_comments(self, repo_id, revision=None, pull_request=None, | ||
f_path=None, line_no=None): | ||||
Jan Heylen
|
r5085 | """ | ||
Gets inline comments for either revision or pull_request. | ||||
Returns a list of tuples with file path and list of comments per line number. | ||||
""" | ||||
Jan Heylen
|
r5086 | comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request, | ||
Thomas De Schampheleire
|
r7409 | inline=True, f_path=f_path, line_no=line_no) | ||
Jan Heylen
|
r5086 | |||
paths = defaultdict(lambda: defaultdict(list)) | ||||
for co in comments: | ||||
paths[co.f_path][co.line_no].append(co) | ||||
Mads Kiilerich
|
r7927 | return sorted(paths.items()) | ||
Jan Heylen
|
r5086 | |||
Thomas De Schampheleire
|
r7409 | def _get_comments(self, repo_id, revision=None, pull_request=None, | ||
inline=False, f_path=None, line_no=None): | ||||
Jan Heylen
|
r5086 | """ | ||
Gets comments for either revision or pull_request_id, either inline or general. | ||||
Thomas De Schampheleire
|
r7409 | If a file path and optionally line number are given, return only the matching inline comments. | ||
Jan Heylen
|
r5086 | """ | ||
Thomas De Schampheleire
|
r7409 | if f_path is None and line_no is not None: | ||
raise Exception("line_no only makes sense if f_path is given.") | ||||
if inline is None and f_path is not None: | ||||
raise Exception("f_path only makes sense for inline comments.") | ||||
Mads Kiilerich
|
r5460 | q = Session().query(ChangesetComment) | ||
Jan Heylen
|
r5086 | |||
if inline: | ||||
Thomas De Schampheleire
|
r7409 | if f_path is not None: | ||
# inline comments for a given file... | ||||
q = q.filter(ChangesetComment.f_path == f_path) | ||||
if line_no is None: | ||||
# ... on any line | ||||
q = q.filter(ChangesetComment.line_no != None) | ||||
else: | ||||
# ... on specific line | ||||
q = q.filter(ChangesetComment.line_no == line_no) | ||||
else: | ||||
# all inline comments | ||||
q = q.filter(ChangesetComment.line_no != None) \ | ||||
.filter(ChangesetComment.f_path != None) | ||||
Jan Heylen
|
r5086 | else: | ||
Thomas De Schampheleire
|
r7409 | # all general comments | ||
Mads Kiilerich
|
r5585 | q = q.filter(ChangesetComment.line_no == None) \ | ||
Jan Heylen
|
r5086 | .filter(ChangesetComment.f_path == None) | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r5539 | if revision is not None: | ||
Mads Kiilerich
|
r5585 | q = q.filter(ChangesetComment.revision == revision) \ | ||
Mads Kiilerich
|
r5460 | .filter(ChangesetComment.repo_id == repo_id) | ||
Mads Kiilerich
|
r5539 | elif pull_request is not None: | ||
Søren Løvborg
|
r6082 | pull_request = PullRequest.guess_instance(pull_request) | ||
Bradley M. Kuhn
|
r4187 | q = q.filter(ChangesetComment.pull_request == pull_request) | ||
else: | ||||
Jan Heylen
|
r5086 | raise Exception('Please specify either revision or pull_request') | ||
Bradley M. Kuhn
|
r4187 | |||
Jan Heylen
|
r5086 | return q.order_by(ChangesetComment.created_on).all() | ||