##// END OF EJS Templates
pull requeset: use #pr and title in comment notifications
Mads Kiilerich -
r3250:e7daa6a7 beta
parent child Browse files
Show More
@@ -1,244 +1,246 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.comment
3 rhodecode.model.comment
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 comments model for RhodeCode
6 comments model for RhodeCode
7
7
8 :created_on: Nov 11, 2011
8 :created_on: Nov 11, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from sqlalchemy.util.compat import defaultdict
30 from sqlalchemy.util.compat import defaultdict
31
31
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import ChangesetComment, User, Repository, \
35 from rhodecode.model.db import ChangesetComment, User, Repository, \
36 Notification, PullRequest
36 Notification, PullRequest
37 from rhodecode.model.notification import NotificationModel
37 from rhodecode.model.notification import NotificationModel
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class ChangesetCommentsModel(BaseModel):
42 class ChangesetCommentsModel(BaseModel):
43
43
44 cls = ChangesetComment
44 cls = ChangesetComment
45
45
46 def __get_changeset_comment(self, changeset_comment):
46 def __get_changeset_comment(self, changeset_comment):
47 return self._get_instance(ChangesetComment, changeset_comment)
47 return self._get_instance(ChangesetComment, changeset_comment)
48
48
49 def __get_pull_request(self, pull_request):
49 def __get_pull_request(self, pull_request):
50 return self._get_instance(PullRequest, pull_request)
50 return self._get_instance(PullRequest, pull_request)
51
51
52 def _extract_mentions(self, s):
52 def _extract_mentions(self, s):
53 user_objects = []
53 user_objects = []
54 for username in extract_mentioned_users(s):
54 for username in extract_mentioned_users(s):
55 user_obj = User.get_by_username(username, case_insensitive=True)
55 user_obj = User.get_by_username(username, case_insensitive=True)
56 if user_obj:
56 if user_obj:
57 user_objects.append(user_obj)
57 user_objects.append(user_obj)
58 return user_objects
58 return user_objects
59
59
60 def create(self, text, repo, user, revision=None, pull_request=None,
60 def create(self, text, repo, user, revision=None, pull_request=None,
61 f_path=None, line_no=None, status_change=None):
61 f_path=None, line_no=None, status_change=None):
62 """
62 """
63 Creates new comment for changeset or pull request.
63 Creates new comment for changeset or pull request.
64 IF status_change is not none this comment is associated with a
64 IF status_change is not none this comment is associated with a
65 status change of changeset or changesets associated with pull request
65 status change of changeset or changesets associated with pull request
66
66
67 :param text:
67 :param text:
68 :param repo:
68 :param repo:
69 :param user:
69 :param user:
70 :param revision:
70 :param revision:
71 :param pull_request:
71 :param pull_request:
72 :param f_path:
72 :param f_path:
73 :param line_no:
73 :param line_no:
74 :param status_change:
74 :param status_change:
75 """
75 """
76 if not text:
76 if not text:
77 return
77 return
78
78
79 repo = self._get_repo(repo)
79 repo = self._get_repo(repo)
80 user = self._get_user(user)
80 user = self._get_user(user)
81 comment = ChangesetComment()
81 comment = ChangesetComment()
82 comment.repo = repo
82 comment.repo = repo
83 comment.author = user
83 comment.author = user
84 comment.text = text
84 comment.text = text
85 comment.f_path = f_path
85 comment.f_path = f_path
86 comment.line_no = line_no
86 comment.line_no = line_no
87
87
88 if revision:
88 if revision:
89 cs = repo.scm_instance.get_changeset(revision)
89 cs = repo.scm_instance.get_changeset(revision)
90 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
90 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
91 comment.revision = revision
91 comment.revision = revision
92 elif pull_request:
92 elif pull_request:
93 pull_request = self.__get_pull_request(pull_request)
93 pull_request = self.__get_pull_request(pull_request)
94 comment.pull_request = pull_request
94 comment.pull_request = pull_request
95 desc = pull_request.pull_request_id
96 else:
95 else:
97 raise Exception('Please specify revision or pull_request_id')
96 raise Exception('Please specify revision or pull_request_id')
98
97
99 self.sa.add(comment)
98 self.sa.add(comment)
100 self.sa.flush()
99 self.sa.flush()
101
100
102 # make notification
101 # make notification
103 line = ''
102 line = ''
104 body = text
103 body = text
105
104
106 #changeset
105 #changeset
107 if revision:
106 if revision:
108 if line_no:
107 if line_no:
109 line = _('on line %s') % line_no
108 line = _('on line %s') % line_no
110 subj = safe_unicode(
109 subj = safe_unicode(
111 h.link_to('Re commit: %(desc)s %(line)s' % \
110 h.link_to('Re commit: %(desc)s %(line)s' % \
112 {'desc': desc, 'line': line},
111 {'desc': desc, 'line': line},
113 h.url('changeset_home', repo_name=repo.repo_name,
112 h.url('changeset_home', repo_name=repo.repo_name,
114 revision=revision,
113 revision=revision,
115 anchor='comment-%s' % comment.comment_id,
114 anchor='comment-%s' % comment.comment_id,
116 qualified=True,
115 qualified=True,
117 )
116 )
118 )
117 )
119 )
118 )
120 notification_type = Notification.TYPE_CHANGESET_COMMENT
119 notification_type = Notification.TYPE_CHANGESET_COMMENT
121 # get the current participants of this changeset
120 # get the current participants of this changeset
122 recipients = ChangesetComment.get_users(revision=revision)
121 recipients = ChangesetComment.get_users(revision=revision)
123 # add changeset author if it's in rhodecode system
122 # add changeset author if it's in rhodecode system
124 cs_author = User.get_from_cs_author(cs.author)
123 cs_author = User.get_from_cs_author(cs.author)
125 if not cs_author:
124 if not cs_author:
126 #use repo owner if we cannot extract the author correctly
125 #use repo owner if we cannot extract the author correctly
127 cs_author = repo.user
126 cs_author = repo.user
128 recipients += [cs_author]
127 recipients += [cs_author]
129 email_kwargs = {
128 email_kwargs = {
130 'status_change': status_change,
129 'status_change': status_change,
131 }
130 }
132 #pull request
131 #pull request
133 elif pull_request:
132 elif pull_request:
134 _url = h.url('pullrequest_show',
133 _url = h.url('pullrequest_show',
135 repo_name=pull_request.other_repo.repo_name,
134 repo_name=pull_request.other_repo.repo_name,
136 pull_request_id=pull_request.pull_request_id,
135 pull_request_id=pull_request.pull_request_id,
137 anchor='comment-%s' % comment.comment_id,
136 anchor='comment-%s' % comment.comment_id,
138 qualified=True,
137 qualified=True,
139 )
138 )
140 subj = safe_unicode(
139 subj = safe_unicode(
141 h.link_to('Re pull request: %(desc)s %(line)s' % \
140 h.link_to('Re pull request #%(pr_id)s: %(desc)s %(line)s' % \
142 {'desc': desc, 'line': line}, _url)
141 {'desc': comment.pull_request.title,
142 'pr_id': comment.pull_request.pull_request_id,
143 'line': line},
144 _url)
143 )
145 )
144
146
145 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
147 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
146 # get the current participants of this pull request
148 # get the current participants of this pull request
147 recipients = ChangesetComment.get_users(pull_request_id=
149 recipients = ChangesetComment.get_users(pull_request_id=
148 pull_request.pull_request_id)
150 pull_request.pull_request_id)
149 # add pull request author
151 # add pull request author
150 recipients += [pull_request.author]
152 recipients += [pull_request.author]
151
153
152 # add the reviewers to notification
154 # add the reviewers to notification
153 recipients += [x.user for x in pull_request.reviewers]
155 recipients += [x.user for x in pull_request.reviewers]
154
156
155 #set some variables for email notification
157 #set some variables for email notification
156 email_kwargs = {
158 email_kwargs = {
157 'pr_id': pull_request.pull_request_id,
159 'pr_id': pull_request.pull_request_id,
158 'status_change': status_change,
160 'status_change': status_change,
159 'pr_comment_url': _url,
161 'pr_comment_url': _url,
160 'pr_comment_user': h.person(user.email),
162 'pr_comment_user': h.person(user.email),
161 'pr_target_repo': h.url('summary_home',
163 'pr_target_repo': h.url('summary_home',
162 repo_name=pull_request.other_repo.repo_name,
164 repo_name=pull_request.other_repo.repo_name,
163 qualified=True)
165 qualified=True)
164 }
166 }
165 # create notification objects, and emails
167 # create notification objects, and emails
166 NotificationModel().create(
168 NotificationModel().create(
167 created_by=user, subject=subj, body=body,
169 created_by=user, subject=subj, body=body,
168 recipients=recipients, type_=notification_type,
170 recipients=recipients, type_=notification_type,
169 email_kwargs=email_kwargs
171 email_kwargs=email_kwargs
170 )
172 )
171
173
172 mention_recipients = set(self._extract_mentions(body))\
174 mention_recipients = set(self._extract_mentions(body))\
173 .difference(recipients)
175 .difference(recipients)
174 if mention_recipients:
176 if mention_recipients:
175 email_kwargs.update({'pr_mention': True})
177 email_kwargs.update({'pr_mention': True})
176 subj = _('[Mention]') + ' ' + subj
178 subj = _('[Mention]') + ' ' + subj
177 NotificationModel().create(
179 NotificationModel().create(
178 created_by=user, subject=subj, body=body,
180 created_by=user, subject=subj, body=body,
179 recipients=mention_recipients,
181 recipients=mention_recipients,
180 type_=notification_type,
182 type_=notification_type,
181 email_kwargs=email_kwargs
183 email_kwargs=email_kwargs
182 )
184 )
183
185
184 return comment
186 return comment
185
187
186 def delete(self, comment):
188 def delete(self, comment):
187 """
189 """
188 Deletes given comment
190 Deletes given comment
189
191
190 :param comment_id:
192 :param comment_id:
191 """
193 """
192 comment = self.__get_changeset_comment(comment)
194 comment = self.__get_changeset_comment(comment)
193 self.sa.delete(comment)
195 self.sa.delete(comment)
194
196
195 return comment
197 return comment
196
198
197 def get_comments(self, repo_id, revision=None, pull_request=None):
199 def get_comments(self, repo_id, revision=None, pull_request=None):
198 """
200 """
199 Get's main comments based on revision or pull_request_id
201 Get's main comments based on revision or pull_request_id
200
202
201 :param repo_id:
203 :param repo_id:
202 :type repo_id:
204 :type repo_id:
203 :param revision:
205 :param revision:
204 :type revision:
206 :type revision:
205 :param pull_request:
207 :param pull_request:
206 :type pull_request:
208 :type pull_request:
207 """
209 """
208
210
209 q = ChangesetComment.query()\
211 q = ChangesetComment.query()\
210 .filter(ChangesetComment.repo_id == repo_id)\
212 .filter(ChangesetComment.repo_id == repo_id)\
211 .filter(ChangesetComment.line_no == None)\
213 .filter(ChangesetComment.line_no == None)\
212 .filter(ChangesetComment.f_path == None)
214 .filter(ChangesetComment.f_path == None)
213 if revision:
215 if revision:
214 q = q.filter(ChangesetComment.revision == revision)
216 q = q.filter(ChangesetComment.revision == revision)
215 elif pull_request:
217 elif pull_request:
216 pull_request = self.__get_pull_request(pull_request)
218 pull_request = self.__get_pull_request(pull_request)
217 q = q.filter(ChangesetComment.pull_request == pull_request)
219 q = q.filter(ChangesetComment.pull_request == pull_request)
218 else:
220 else:
219 raise Exception('Please specify revision or pull_request')
221 raise Exception('Please specify revision or pull_request')
220 q = q.order_by(ChangesetComment.created_on)
222 q = q.order_by(ChangesetComment.created_on)
221 return q.all()
223 return q.all()
222
224
223 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
225 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
224 q = self.sa.query(ChangesetComment)\
226 q = self.sa.query(ChangesetComment)\
225 .filter(ChangesetComment.repo_id == repo_id)\
227 .filter(ChangesetComment.repo_id == repo_id)\
226 .filter(ChangesetComment.line_no != None)\
228 .filter(ChangesetComment.line_no != None)\
227 .filter(ChangesetComment.f_path != None)\
229 .filter(ChangesetComment.f_path != None)\
228 .order_by(ChangesetComment.comment_id.asc())\
230 .order_by(ChangesetComment.comment_id.asc())\
229
231
230 if revision:
232 if revision:
231 q = q.filter(ChangesetComment.revision == revision)
233 q = q.filter(ChangesetComment.revision == revision)
232 elif pull_request:
234 elif pull_request:
233 pull_request = self.__get_pull_request(pull_request)
235 pull_request = self.__get_pull_request(pull_request)
234 q = q.filter(ChangesetComment.pull_request == pull_request)
236 q = q.filter(ChangesetComment.pull_request == pull_request)
235 else:
237 else:
236 raise Exception('Please specify revision or pull_request_id')
238 raise Exception('Please specify revision or pull_request_id')
237
239
238 comments = q.all()
240 comments = q.all()
239
241
240 paths = defaultdict(lambda: defaultdict(list))
242 paths = defaultdict(lambda: defaultdict(list))
241
243
242 for co in comments:
244 for co in comments:
243 paths[co.f_path][co.line_no].append(co)
245 paths[co.f_path][co.line_no].append(co)
244 return paths.items()
246 return paths.items()
General Comments 0
You need to be logged in to leave comments. Login now