##// END OF EJS Templates
comments: check for None instead of boolean checks
Mads Kiilerich -
r5539:87285c50 default
parent child Browse files
Show More
@@ -1,278 +1,278 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.model.comment
15 kallithea.model.comment
16 ~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 comments model for Kallithea
18 comments model for Kallithea
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Nov 11, 2011
22 :created_on: Nov 11, 2011
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import logging
28 import logging
29
29
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
33 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
34 from kallithea.lib import helpers as h
34 from kallithea.lib import helpers as h
35 from kallithea.model import BaseModel
35 from kallithea.model import BaseModel
36 from kallithea.model.db import ChangesetComment, User, \
36 from kallithea.model.db import ChangesetComment, User, \
37 Notification, PullRequest
37 Notification, PullRequest
38 from kallithea.model.notification import NotificationModel
38 from kallithea.model.notification import NotificationModel
39 from kallithea.model.meta import Session
39 from kallithea.model.meta import Session
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class ChangesetCommentsModel(BaseModel):
44 class ChangesetCommentsModel(BaseModel):
45
45
46 cls = ChangesetComment
46 cls = ChangesetComment
47
47
48 def __get_changeset_comment(self, changeset_comment):
48 def __get_changeset_comment(self, changeset_comment):
49 return self._get_instance(ChangesetComment, changeset_comment)
49 return self._get_instance(ChangesetComment, changeset_comment)
50
50
51 def __get_pull_request(self, pull_request):
51 def __get_pull_request(self, pull_request):
52 return self._get_instance(PullRequest, pull_request)
52 return self._get_instance(PullRequest, pull_request)
53
53
54 def _extract_mentions(self, s):
54 def _extract_mentions(self, s):
55 user_objects = []
55 user_objects = []
56 for username in extract_mentioned_users(s):
56 for username in extract_mentioned_users(s):
57 user_obj = User.get_by_username(username, case_insensitive=True)
57 user_obj = User.get_by_username(username, case_insensitive=True)
58 if user_obj:
58 if user_obj:
59 user_objects.append(user_obj)
59 user_objects.append(user_obj)
60 return user_objects
60 return user_objects
61
61
62 def _get_notification_data(self, repo, comment, user, comment_text,
62 def _get_notification_data(self, repo, comment, user, comment_text,
63 line_no=None, revision=None, pull_request=None,
63 line_no=None, revision=None, pull_request=None,
64 status_change=None, closing_pr=False):
64 status_change=None, closing_pr=False):
65 """
65 """
66 :returns: tuple (subj,body,recipients,notification_type,email_kwargs)
66 :returns: tuple (subj,body,recipients,notification_type,email_kwargs)
67 """
67 """
68 # make notification
68 # make notification
69 body = comment_text # text of the comment
69 body = comment_text # text of the comment
70 line = ''
70 line = ''
71 if line_no:
71 if line_no:
72 line = _('on line %s') % line_no
72 line = _('on line %s') % line_no
73
73
74 #changeset
74 #changeset
75 if revision:
75 if revision:
76 notification_type = Notification.TYPE_CHANGESET_COMMENT
76 notification_type = Notification.TYPE_CHANGESET_COMMENT
77 cs = repo.scm_instance.get_changeset(revision)
77 cs = repo.scm_instance.get_changeset(revision)
78 desc = cs.short_id
78 desc = cs.short_id
79
79
80 threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())]
80 threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())]
81 if line_no: # TODO: url to file _and_ line number
81 if line_no: # TODO: url to file _and_ line number
82 threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no,
82 threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no,
83 h.canonical_hostname()))
83 h.canonical_hostname()))
84 comment_url = h.canonical_url('changeset_home',
84 comment_url = h.canonical_url('changeset_home',
85 repo_name=repo.repo_name,
85 repo_name=repo.repo_name,
86 revision=revision,
86 revision=revision,
87 anchor='comment-%s' % comment.comment_id)
87 anchor='comment-%s' % comment.comment_id)
88 subj = safe_unicode(
88 subj = safe_unicode(
89 h.link_to('Re changeset: %(desc)s %(line)s' % \
89 h.link_to('Re changeset: %(desc)s %(line)s' % \
90 {'desc': desc, 'line': line},
90 {'desc': desc, 'line': line},
91 comment_url)
91 comment_url)
92 )
92 )
93 # get the current participants of this changeset
93 # get the current participants of this changeset
94 recipients = ChangesetComment.get_users(revision=revision)
94 recipients = ChangesetComment.get_users(revision=revision)
95 # add changeset author if it's known locally
95 # add changeset author if it's known locally
96 cs_author = User.get_from_cs_author(cs.author)
96 cs_author = User.get_from_cs_author(cs.author)
97 if not cs_author:
97 if not cs_author:
98 #use repo owner if we cannot extract the author correctly
98 #use repo owner if we cannot extract the author correctly
99 cs_author = repo.user
99 cs_author = repo.user
100 recipients += [cs_author]
100 recipients += [cs_author]
101 email_kwargs = {
101 email_kwargs = {
102 'status_change': status_change,
102 'status_change': status_change,
103 'cs_comment_user': user.full_name_and_username,
103 'cs_comment_user': user.full_name_and_username,
104 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name),
104 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name),
105 'cs_comment_url': comment_url,
105 'cs_comment_url': comment_url,
106 'raw_id': revision,
106 'raw_id': revision,
107 'message': cs.message,
107 'message': cs.message,
108 'repo_name': repo.repo_name,
108 'repo_name': repo.repo_name,
109 'short_id': h.short_id(revision),
109 'short_id': h.short_id(revision),
110 'branch': cs.branch,
110 'branch': cs.branch,
111 'comment_username': user.username,
111 'comment_username': user.username,
112 'threading': threading,
112 'threading': threading,
113 }
113 }
114 #pull request
114 #pull request
115 elif pull_request:
115 elif pull_request:
116 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
116 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
117 desc = comment.pull_request.title
117 desc = comment.pull_request.title
118 _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':')
118 _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':')
119 threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name,
119 threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name,
120 pull_request.pull_request_id,
120 pull_request.pull_request_id,
121 h.canonical_hostname())]
121 h.canonical_hostname())]
122 if line_no: # TODO: url to file _and_ line number
122 if line_no: # TODO: url to file _and_ line number
123 threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name,
123 threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name,
124 pull_request.pull_request_id, line_no,
124 pull_request.pull_request_id, line_no,
125 h.canonical_hostname()))
125 h.canonical_hostname()))
126 comment_url = pull_request.url(canonical=True,
126 comment_url = pull_request.url(canonical=True,
127 anchor='comment-%s' % comment.comment_id)
127 anchor='comment-%s' % comment.comment_id)
128 subj = safe_unicode(
128 subj = safe_unicode(
129 h.link_to('Re pull request %(pr_nice_id)s: %(desc)s %(line)s' % \
129 h.link_to('Re pull request %(pr_nice_id)s: %(desc)s %(line)s' % \
130 {'desc': desc,
130 {'desc': desc,
131 'pr_nice_id': comment.pull_request.nice_id(),
131 'pr_nice_id': comment.pull_request.nice_id(),
132 'line': line},
132 'line': line},
133 comment_url)
133 comment_url)
134 )
134 )
135 # get the current participants of this pull request
135 # get the current participants of this pull request
136 recipients = ChangesetComment.get_users(pull_request_id=
136 recipients = ChangesetComment.get_users(pull_request_id=
137 pull_request.pull_request_id)
137 pull_request.pull_request_id)
138 # add pull request author
138 # add pull request author
139 recipients += [pull_request.owner]
139 recipients += [pull_request.owner]
140
140
141 # add the reviewers to notification
141 # add the reviewers to notification
142 recipients += [x.user for x in pull_request.reviewers]
142 recipients += [x.user for x in pull_request.reviewers]
143
143
144 #set some variables for email notification
144 #set some variables for email notification
145 email_kwargs = {
145 email_kwargs = {
146 'pr_title': pull_request.title,
146 'pr_title': pull_request.title,
147 'pr_nice_id': pull_request.nice_id(),
147 'pr_nice_id': pull_request.nice_id(),
148 'status_change': status_change,
148 'status_change': status_change,
149 'closing_pr': closing_pr,
149 'closing_pr': closing_pr,
150 'pr_comment_url': comment_url,
150 'pr_comment_url': comment_url,
151 'pr_comment_user': user.full_name_and_username,
151 'pr_comment_user': user.full_name_and_username,
152 'pr_target_repo': h.canonical_url('summary_home',
152 'pr_target_repo': h.canonical_url('summary_home',
153 repo_name=pull_request.other_repo.repo_name),
153 repo_name=pull_request.other_repo.repo_name),
154 'repo_name': pull_request.other_repo.repo_name,
154 'repo_name': pull_request.other_repo.repo_name,
155 'ref': org_ref_name,
155 'ref': org_ref_name,
156 'comment_username': user.username,
156 'comment_username': user.username,
157 'threading': threading,
157 'threading': threading,
158 }
158 }
159
159
160 return subj, body, recipients, notification_type, email_kwargs
160 return subj, body, recipients, notification_type, email_kwargs
161
161
162 def create(self, text, repo, user, revision=None, pull_request=None,
162 def create(self, text, repo, user, revision=None, pull_request=None,
163 f_path=None, line_no=None, status_change=None, closing_pr=False,
163 f_path=None, line_no=None, status_change=None, closing_pr=False,
164 send_email=True):
164 send_email=True):
165 """
165 """
166 Creates a new comment for either a changeset or a pull request.
166 Creates a new comment for either a changeset or a pull request.
167 status_change and closing_pr is only for the optional email.
167 status_change and closing_pr is only for the optional email.
168
168
169 Returns the created comment.
169 Returns the created comment.
170 """
170 """
171 if not status_change and not text:
171 if not status_change and not text:
172 log.warning('Missing text for comment, skipping...')
172 log.warning('Missing text for comment, skipping...')
173 return None
173 return None
174
174
175 repo = self._get_repo(repo)
175 repo = self._get_repo(repo)
176 user = self._get_user(user)
176 user = self._get_user(user)
177 comment = ChangesetComment()
177 comment = ChangesetComment()
178 comment.repo = repo
178 comment.repo = repo
179 comment.author = user
179 comment.author = user
180 comment.text = text
180 comment.text = text
181 comment.f_path = f_path
181 comment.f_path = f_path
182 comment.line_no = line_no
182 comment.line_no = line_no
183
183
184 if revision is not None:
184 if revision is not None:
185 comment.revision = revision
185 comment.revision = revision
186 elif pull_request is not None:
186 elif pull_request is not None:
187 pull_request = self.__get_pull_request(pull_request)
187 pull_request = self.__get_pull_request(pull_request)
188 comment.pull_request = pull_request
188 comment.pull_request = pull_request
189 else:
189 else:
190 raise Exception('Please specify revision or pull_request_id')
190 raise Exception('Please specify revision or pull_request_id')
191
191
192 Session().add(comment)
192 Session().add(comment)
193 Session().flush()
193 Session().flush()
194
194
195 if send_email:
195 if send_email:
196 (subj, body, recipients, notification_type,
196 (subj, body, recipients, notification_type,
197 email_kwargs) = self._get_notification_data(
197 email_kwargs) = self._get_notification_data(
198 repo, comment, user,
198 repo, comment, user,
199 comment_text=text,
199 comment_text=text,
200 line_no=line_no,
200 line_no=line_no,
201 revision=revision,
201 revision=revision,
202 pull_request=pull_request,
202 pull_request=pull_request,
203 status_change=status_change,
203 status_change=status_change,
204 closing_pr=closing_pr)
204 closing_pr=closing_pr)
205 email_kwargs['is_mention'] = False
205 email_kwargs['is_mention'] = False
206 # create notification objects, and emails
206 # create notification objects, and emails
207 NotificationModel().create(
207 NotificationModel().create(
208 created_by=user, subject=subj, body=body,
208 created_by=user, subject=subj, body=body,
209 recipients=recipients, type_=notification_type,
209 recipients=recipients, type_=notification_type,
210 email_kwargs=email_kwargs,
210 email_kwargs=email_kwargs,
211 )
211 )
212
212
213 mention_recipients = set(self._extract_mentions(body))\
213 mention_recipients = set(self._extract_mentions(body))\
214 .difference(recipients)
214 .difference(recipients)
215 if mention_recipients:
215 if mention_recipients:
216 email_kwargs['is_mention'] = True
216 email_kwargs['is_mention'] = True
217 subj = _('[Mention]') + ' ' + subj
217 subj = _('[Mention]') + ' ' + subj
218 NotificationModel().create(
218 NotificationModel().create(
219 created_by=user, subject=subj, body=body,
219 created_by=user, subject=subj, body=body,
220 recipients=mention_recipients,
220 recipients=mention_recipients,
221 type_=notification_type,
221 type_=notification_type,
222 email_kwargs=email_kwargs
222 email_kwargs=email_kwargs
223 )
223 )
224
224
225 return comment
225 return comment
226
226
227 def delete(self, comment):
227 def delete(self, comment):
228 comment = self.__get_changeset_comment(comment)
228 comment = self.__get_changeset_comment(comment)
229 Session().delete(comment)
229 Session().delete(comment)
230
230
231 return comment
231 return comment
232
232
233 def get_comments(self, repo_id, revision=None, pull_request=None):
233 def get_comments(self, repo_id, revision=None, pull_request=None):
234 """
234 """
235 Gets general comments for either revision or pull_request.
235 Gets general comments for either revision or pull_request.
236
236
237 Returns a list, ordered by creation date.
237 Returns a list, ordered by creation date.
238 """
238 """
239 return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
239 return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
240 inline=False)
240 inline=False)
241
241
242 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
242 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
243 """
243 """
244 Gets inline comments for either revision or pull_request.
244 Gets inline comments for either revision or pull_request.
245
245
246 Returns a list of tuples with file path and list of comments per line number.
246 Returns a list of tuples with file path and list of comments per line number.
247 """
247 """
248 comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
248 comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
249 inline=True)
249 inline=True)
250
250
251 paths = defaultdict(lambda: defaultdict(list))
251 paths = defaultdict(lambda: defaultdict(list))
252 for co in comments:
252 for co in comments:
253 paths[co.f_path][co.line_no].append(co)
253 paths[co.f_path][co.line_no].append(co)
254 return paths.items()
254 return paths.items()
255
255
256 def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False):
256 def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False):
257 """
257 """
258 Gets comments for either revision or pull_request_id, either inline or general.
258 Gets comments for either revision or pull_request_id, either inline or general.
259 """
259 """
260 q = Session().query(ChangesetComment)
260 q = Session().query(ChangesetComment)
261
261
262 if inline:
262 if inline:
263 q = q.filter(ChangesetComment.line_no != None)\
263 q = q.filter(ChangesetComment.line_no != None)\
264 .filter(ChangesetComment.f_path != None)
264 .filter(ChangesetComment.f_path != None)
265 else:
265 else:
266 q = q.filter(ChangesetComment.line_no == None)\
266 q = q.filter(ChangesetComment.line_no == None)\
267 .filter(ChangesetComment.f_path == None)
267 .filter(ChangesetComment.f_path == None)
268
268
269 if revision:
269 if revision is not None:
270 q = q.filter(ChangesetComment.revision == revision)\
270 q = q.filter(ChangesetComment.revision == revision)\
271 .filter(ChangesetComment.repo_id == repo_id)
271 .filter(ChangesetComment.repo_id == repo_id)
272 elif pull_request:
272 elif pull_request is not None:
273 pull_request = self.__get_pull_request(pull_request)
273 pull_request = self.__get_pull_request(pull_request)
274 q = q.filter(ChangesetComment.pull_request == pull_request)
274 q = q.filter(ChangesetComment.pull_request == pull_request)
275 else:
275 else:
276 raise Exception('Please specify either revision or pull_request')
276 raise Exception('Please specify either revision or pull_request')
277
277
278 return q.order_by(ChangesetComment.created_on).all()
278 return q.order_by(ChangesetComment.created_on).all()
General Comments 0
You need to be logged in to leave comments. Login now